2024年12月29日-qt学习
使用qtcreator写了一个简单的qt程序。记录一下。
程序整体的结构
在我的程序中,一个窗口,对应一个类,对应一个命名空间,继承QWidget或者QMainWindow类。类中有信号(signals)和槽(slots),按照我的理解:槽是实际执行任务的方法,信号是能跨窗口触发槽的方法。
我的程序主要有mainWindow和starPromptWindow两个窗口,在mainwindow类中实例化了一个m_starPromptWindow,用于触发信号从而使用槽。
信号和槽
例如,如果我要点击starPromptWindow的一个按钮,要改变mainWindow的一个控件内容,那我应该在starPromptWindow中定义一个信号,还要有一个触发信号的方法,在mainWindow中应该有一个目标方法来改变控件内容。我应当在mainWindow中连接这个信号和目标方法,因为我在mainWindow中实例化了一个starPromptWindow对象m_starPromptWindow。
总之就是:
- starPromptWindow:
- 按钮
- 信号
- 发出信号的方法
- 与按钮连接
- mainWindow:
- 实例化一个starPromptWindow
- 连接信号与目标方法
- 目标方法(槽)
- 操作mainWindow中的控件
如果我反过来,要点击mainWindow中的按钮,控制starPromptWindow中的控件,那我同样应该使用信号。但是不能像这样直接调换地位:
- starPromptWindow:
- 实例化一个mainWindow
- 连接信号与目标方法
- 目标方法(槽)
- 操作starPromptWindow中的控件
- mainWindow:
- 按钮
- 信号
- 发出信号的方法
- 与按钮连接
这是错误的(大概)。按理来说应该这么做:
- starPromptWindow:
- 信号
- 发出信号的方法
- 与mainWindow中的按钮连接
- 连接在mainWindow中设置
- 一个private槽
- 用于控制starPromptWindow中的控件
- mainWindow:
- 实例化一个starPromptWindow
- 按钮
- 连接按钮与starPromptWindow的信号
不过事实上也可以不通过信号,直接使用starPromptWindow中的public槽:
- starPromptWindow:
- 一个public槽
- 用于控制starPromptWindow中的控件
- 一个public槽
- mainWindow:
- 实例化一个starPromptWindow
- 连接到starPromptWindow的public槽
总而言之,通过信号和槽,就能间接地通过一个窗口的方法和另一个窗口的控件交互了。
窗口头文件
以mainWindow为例:
1 |
|
按照qt(或者说是qtcreator)标准写法写的类定义,声明成员。
cpp程序
以mainWindow为例:
1 |
|
实现功能的部分。
其中,~MainWindow()函数是退出窗口时执行的动作,这个方法在类定义的时候声明了。
不过事实上,要更改窗口关闭时的行为,我是重写了closeEvent。例如,我不希望starPromptWindow退出的时候彻底退出,我希望只是隐藏,同时保存prompt,于是我重写了closeEvent:
1 |
|
对应的,要在头文件类定义中说明你重写了这个方法:
1 |
|
主要功能:
绘图:
通过发送请求完成。请求的内容存储在ApiData.json中。
- 图片预览
通过生成并打开一个QDialog类型的、继承了mainWindow的imageDialog实现。在其中插入一个QLabel类型的imageLabel变量,通过setPixmap(QPixmap::fromImage(scaledImage))设置内容,然后layout->addWidget(imageLabel)将其插入QVBoxLayout型的layout,最后通过imageDialog->setLayout(layout)设置layout。
- 值得注意的是,QDialog有一个性质名为模态,model,需要通过imageDialog->setModal(false)设置为flase,否则只能聚焦在最后一个打开的QDialog上。
- 值得注意的是,如果不执行imageDialog->setAttribute(Qt::WA_DeleteOnClose),imageDialog占用的内存不会释放,导致占用节节高。
- 图片和参数保存
图片有现成的类型QImage,QImage有现成的方法save();文件有QFile,可以通过文本流QTextStream写入:
(QFile变量名为jsonFile)
1 |
|
- “上一次输入的prompt”将被自动记录,并在下一次启动时填写
同上读写json文件。
没有使用数据库是因为不方便直接编辑。
- 收藏prompt,查看与管理收藏的prompt
使用了starPromptWindow窗口完成。读写文件同上。和主窗口的交互详见信号量部分。
次要功能:
- 网络错误时会弹出提示框
使用了我自己写的专门的类ErrorDialog(继承了QDialog)。qt有专门的错误提示框QErrorMessage,用起来很方便,不过我不太喜欢ui布局所以自己写了。
- clear按钮可以快速清屏右边的日志
直接将按钮连接到ui->outputTextEdit->clear函数
- alt + s 是绘制的快捷键
先创建快捷方式,然后连接。就像这样
1 |
|
- ctrl + s 是保存tag的快捷键(如果不手动保存的话,画完一张图后也会自动保存)
同理
1 |
|
- 响应式布局
最麻烦的一集
使用qtcreator的可视化编辑器折腾了好半天,继承QWidget的情况下,怎么都会挤成一坨。后来改成了继承MainWindow类才好。
starPromptWindow的初始化函数如下:
1 |
|
注意这里layout用的是QVBoxLayout,是具体的垂直布局,即使只有一个子控件,而不是基类QLayout(QLayout似乎并不能直接拿来用)。
setLayout(mainLayout) 和 this->setLayout(mainLayout) 都会使得控件挤成一团。不理解为什么
main函数
首先实例化一个QApplication,然后实例化主窗口。
starPromptWindow不需要在这里实例化因为在mainWindow中实例化了。
1 |
|
总结
qt说难不难,主要是麻烦。窗口之间交互依赖信号和槽,在谁里实例化谁要搞明白。而且可读性不太强。之前写过pyqt,最大区别大概是connect用法不同?
qt是把A的动作A1和某窗口(一般是this)的目标方法连接,pyqt是A的成员A1的方法connect直接连接目标方法。何况cpp大量使用指针,导致读的时候需要留个心眼看是直接传递的值还是传递了指针。
写完之后一看,思路还挺清晰的。也可能是因为我的程序比较小,规模大了也可能会比较混沌也说不定