Day 11 集成到界面
集成ScanTask
到界面中
我们前面花了好几天来实现后台的处理,却一直看不到程序的界面,接下来我们尝试实现界面相关的功能。首先我们实现单张扫描功能。
我们增加一个slot
函数onActSingleScan()
,并在ScannerMainWindow
的构造函数中将其与QAction actSingleScan
关联起来。然后我们给出第一个实现版本:
1 | void ScannerMainWindow::onActSingleScan() |
运行程序,我们能够从调试窗口看到了程序运行的调试输出信息,说明程序在运行,但是界面一动不动,被阻塞住了。我们需要把它挪到独立的线程中运行。最简单的做法还是和在连接设备中做的一样,使用QtConcurrent::run()
启动线程运行。
一种新手很容易犯的错误写法是:
1 | void ScannerMainWindow::onActSingleScan() |
除非doScan()
启动后不访问ScanTask
的任何属性或方法(那样直接就定义成static
函数就得了),否则一运行就会崩溃。因为onActSingleScan()
启动并运行线程后它自己就立即退出了,从而导致task
也被析构了,而在运行的线程再访问task
就会导致错误。
我们也可以用各种手段来延长task
的生命周期,但是最简单的做法是下面:
1 | void ScannerMainWindow::onActSingleScan() |
现在,我们有一些需求:
- 当开始扫描后,要禁止掉两个扫描按钮,同时使能停止扫描的按钮
- 当高倍扫描结束后,要是能扫描按钮,允许马上开始下一次扫描。同时,当前的扫描任务在后台运行直到结束
我们在ScanTask
中定义了一些signal
,其中有几个和GUI界面通信使用的,我们重新审视一下它们的作用:
sigPreviewed()
:用于传递拍摄得到的预览图和标签图,以及标签文本,会在左边的控制视图中显示预览图和二维码的内容。sigLowScanRanged()
:传递低倍扫描区域在预览图上面的范围。会在控制视图中的预览图上画出这个范围sigScanStart()
:用于传递低倍/高倍要扫描的行列数,会在控制视图中的进度图中设置窗格的行列数sigImageCaptured()
:传递拍摄得到的一张照片,在控制视图中会填充一个网格,在主视图中会显示这张照片sigDeviceFree()
:释放扫描仪设备。会使能扫描按钮sigTaskCompleted()
:表示扫描全部结束,会在消息窗口中输出信息(目前不做别的)sigChooseOptions()
:通知用户确认扫描参数sigChooseLowScanRange()
:通知用户确认低倍扫描范围
其中,前面几个都是单向消息,而最后两个是双向通信。需要在GUI中关联和处理这些信号。
其中,sigPreviewed()
,sigLowScanRanged()
,sigScanStart()
由ControlPanelForm
来处理,而sigScanStart()
和sigImageCaptured()
由MainClientForm
处理,后面几条信号则由ScannerMainWindow
处理。我们分别在对应的类中创建相应的slot
函数,并在onActSingleScan()
中建立关联关系:
1 | void ScannerMainWindow::onActSingleScan() |
注意上面代码中对
sigChooseOptions
和sigChooseLowScanRange
的connect
中,使用了第五个参数,Qt::BlockingQueuedConnection
。
现在运行程序,可以看到,后台的运行不再阻塞界面的更新了。接下来我们会简单地实现一下界面。