修改QAction
在代码中控制界面菜单的QAction内容,如替换文字、设置可选、设置样式等。
获取action
通过ui.[action_name]
获取,如:ui.action_open_image
获取&修改文字
1 | this->ui.action_open_image->text(); |
设置是否可选
1 | this->ui.action_open_image->setEnabled(false); |
遍历一个QMenu下的所有QAction
1 | foreach(QAction * action, this->ui.menu_2->actions()) |
鼠标滚轮事件
判断当前按钮
1 | void ImageViewer::wheelEvent(QWheelEvent* evt) |
缩放控制
应对两种情况:缩放过小和放大过大,进行了两个控制
- 缩小时不允许小于某个窗口比例(例如0.5)。缩小过小时,长宽均小于比例时,按矩形与窗口对应边的比例较大的那个来。例如矩形长比窗口长为0.3,宽之比为0.5,那么以宽为主;否则反之。
- 放大时显示窗口映射到原图范围不大于某个像素值(例如100像素)。放大过大时,显示区映射到图像上矩形长宽任意一个小于限定值时,需要进行拉伸,保证大于限定值。例如,放大后显示区矩形映射到图像上宽度为90,高度为80,限定值为100,那么要求两个都大于100。调整后显示区高度应该为100,宽度为112.5,保证都大于限定值。
1 | // 变量 |
1 | void ImageViewer::wheelEvent(QWheelEvent* evt) |
矢量绘图功能
针对视图类ImageViewer
添加绘图功能。绘图功能包括:画点、画线、画矩形、画旋转矩形等。自由涂鸦暂不考虑。
存储绘图图形数据的变量
存放在视图类中的几何图形数据主要用来显示,不设计过多的存储机制。
注意这里的mImgStartX
等变量专门用来存储鼠标移动的点在图像上的坐标。
1 | double mImgStartX, mImgStartY, mImgEndX, mImgEndY; // 用于记录起止点在图像上的坐标 |
类接口:启动和关闭绘图
视图类初始化后,通过这两个函数启动关闭其绘图功能。
1 | void ImageViewer::startDrawPoint() |
静态函数:在图像上绘制图形
1 | /*在img上绘制各种图形*/ |
图形绘制后触发的事件
1 | signals: |
在图像窗口显示带矢量图形的图片
1 | /*显示带有矢量图形的图片,适量坐标均为图像坐标,绘制后调用showImage()*/ |
绘图的关键:鼠标事件
鼠标按下后记录图像坐标(如果在图像内),移动时根据是否在绘图更新鼠标坐标,鼠标抬起时记录绘图结果。
1 | void ImageViewer::mousePressEvent(QMouseEvent* evt) |
在父组件响应事件
1 | private slots: |
绑定事件
1 | connect(this->iViewer, SIGNAL(drawNewPoint(const cv::Point2d&)), this, SLOT(onDrawNewPoint(const cv::Point2d&))); |
事件
1 | void KvImage::onDrawNewPoint(const cv::Point2d& pt) |
删除矢量
声明
1 | bool deletePoint(const cv::Point2d& pt); |
实现
1 | bool ImageViewer::deletePoint(const cv::Point2d& pt) |
QTextBrowser滚动到底部
移动操作参考QTextCursor::MoveOperation-enum
1 | this->mInfoBar->moveCursor(QTextCursor::End); // 滚动到底部 |
矢量窗口
用来显示矢量(点线面),并具有选择查看和导出保存的功能。
创建类
VectorWindow.h
1 |
|
VectorWindow.cpp
1 |
|
设置窗体类
1 | ////////// 初始化窗口 |
使用按钮控件控制窗体最前
当当前窗口为隐藏状态时,直接设置其是否顶置;若当前状态为显示,设置顶置方式后窗口会自动隐藏,因此需要额外调用一次show()
函数。
创建插槽:
1 | void VectorWindow::onToggleTopHint(bool checked) |
创建QRadioButton按钮并绑定事件:
1 | // 是否顶置按钮 |
使用表格
QTableWidget继承自QTableView,功能较为齐全。下面是一个使用示例:
1 | ////////////// 设置表格 |
表格初始化
设置了一些显示参数,如标题字体样式、对齐方式、宽高策略、是否可选中项目等。
1 | void VectorWindow::initTable(QTableWidget* table) |
表格内容操作:不同数据显示的初始化,填充,清空,滚动
填充数据前应该对表格进行初始化,不同的数据类型具有不同的表头信息
1 | void VectorWindow::initShowPoints() const |
填充一行数据:
1 | bool VectorWindow::appendPoint(const cv::Point2d& pt) const |
填充列表数据直接循环调用填充单个的即可。
控制表格滚动到底部,一般在插入数据后调用
1 | // 滚动到底部 |
清空表格不仅要清空内容,还要把行列设置为0从而清空表头
1 | void VectorWindow::clearTable() const |
表格事件
当希望点击表格中的矢量定位到矢量在图上的位置时,需要用到表格事件QTableWidget::signals,这边用到了双击事件cellDoubleClicked(int row, int col)
。
创建槽函数
1 | public slots: |
1 | void VectorWindow::onItemDoubleClicked(int row, int col) |
绑定信号
1 | // 绑定事件 |
表格内的操作按钮
当希望删除矢量时,可以借助每个矢量后面的操作按钮完成。
创建按钮
1 | QWidget* VectorWindow::createRowCtrlBtnWidget() |
添加到表格中:
1 | QWidget* btnWidget = this->createRowCtrlBtnWidget(); |
获取行号:使用sender()函数
当某一个Object
emit一个signal的时候,它就是一个sender。系统会记录下当前是谁emit出这个signal的,所以你在对应的slot里就可以通过sender()
得到当前是谁invoke了你的slot。
在如下例子中,onEditRow()
函数作为表格项按钮的响应函数,其sender
就是按钮本身,而按钮的父组件是添加到表格中的widget,其pos()
函数的返回值是其在表格中左上角的位置坐标。因此通过表格的indexAt()
函数可以反算出行号。
1 | void VectorWindow::onEditRow() |
还有一种思路是将行号作为属性(property)存放到每一行的操作按钮控件中,但是由于表格的删除操作,实际的行号可能随时发生变化,那么创建一个表格行时生成的操作按钮里记录的行号就过时了,因此不适合作为属性传入,这里采用实时计算的方式较为合适。作为属性传入的见下一节,在右键点击菜单中,菜单的生成是实时的,此时的行号具有时效性。
右键点击操作
右键点击一个项目进行删除或者复制到剪贴板。
QTableWidget的点击事件关联的都是鼠标左键,只有customContextMenuRequested(QPoint)
信号是QWidget中唯一的右键菜单点击的信号,且该信号的触发条件是QWidget的上下文菜单策略(ContextMenuPolicy)设置为Qt::CustomContextMenu
。
1 | this->mTable->setContextMenuPolicy(Qt::CustomContextMenu); |
响应事件:
1 | void VectorWindow::onTableRightClicked(const QPoint pos) |
右键菜单中添加的动作与表格内按钮的动作可能用到相同的响应函数,例如矢量的查看和删除。在表格按钮中,行号的获取是通过表单控件的位置计算出来的,由于表格行数随时可能发生变化,因此通过实时计算的方式可以保证行号的正确;而在右键菜单中,其生成和消失是根据当前鼠标位置决定的,并不会和每一行数据进行绑定,因此通过属性设置行号即可完成功能。