Qt 5.12 Widget桌面开发
Qt基础
Qt编译安装
本次实验环境为VS2019+QT5.12.4
- 首先打开PowerShell,执行下面命令以创建编译环境:C:QtQt5.12.45.12.4msvc2017_64binqtenv2.batC:"Program Files (x86)”"Microsoft Visual Studio”2019CommunityVCAuxiliaryBuildvcvars64.bat。
进入QT源代码文件夹:C:QtQt5.12.45.12.4Src。
- 进行配置:C:QtQt5.12.45.12.4Srcqtbaseconfigure.bat -prefix E:QT -hostprefix E:QT5.12.4\ -opensource -confirm-license -debug-and-release -developer-build -plugin-manifests -pch -silent -dbus-linked -dbus-runtime -accessibility -system-proxies -direct2d -gif -ico -sql-odbc -sql-sqlite
进行编译:C:QtQt5.12.4ToolsQtCreatorbinjom.exe
安装:C:QtQt5.12.4ToolsQtCreatorbinjom.exe install
清除构建文件:C:QtQt5.12.4ToolsQtCreatorbinjom.exe clean
Qt附加程序
程序发布程序:windeployqt
要完成应用程序的发布,请遵循以下步骤:
以Release模式编译项目。

前往编译目录下获取编译文件,该命令通常与项目目录位于同一层次:

│ .qmake.stash │ Makefile │ Makefile.Debug │ Makefile.Release │ ├─debug └─release │ main.o │ moc_predefs.h │ moc_widget.cpp │ moc_widget.o │ widget.o │ Widget_NoUI_Test.exe │ └─stable.h.gch |
将编译后的exe提取到一个空目录下。
从开始菜单中选择对应版本的环境工具:

运行windeployqt命令:

此时的程序运行库全部在该目录下,将目录打包发布即可。
Qml预览工具:qmlscene
qmlscene用于预览qml脚本文件:
打开Qt的环境工具:

切换到qml文件目录下运行qmlscene命令:
你也可以直接打开qmlscene.exe,打开后按要求选择qml文件。
元对象系统
信号和槽
属性系统
事件系统
事件驱动模型
事件(Event)是由操作系统或程序自身发出的任务请求。当键盘点击、窗口重绘、鼠标移动、计时结束……时都会产生对应事件。
事件驱动模型基本思路如下[1.]:
由程序维护一个事件(消息)队列。
当事件产生时(如鼠标按下)向事件队列中添加事件。
由事件循环不断从事件队列中取出事件。
根据取出事件的类别,调用对应的事件处理函数。
事件驱动模型是I/O编程范式(事件驱动模型、单线程模型、多线程模型)的一种,其目的是为了解决I/O交互问题。大多数GUI都应用了事件驱动模型。因此,可以说:
“GUI是由事件驱动的”
Qt中的事件
事件对象
Qt中的事件均继承自QEvent。常用事件对象的继承结构如下:

当事件发生时,Qt将创建一个事件对象,并将其添加到事件循环中。
使用事件循环
要使用Qt应用程序中,你必须创建一个QApplication对象并调用其exec()函数:
int main(int argc, char *argv[]) { QApplication a(argc, argv); return a.exec(); } |
在调用QApplication的exec函数后,程序将进入事件循环并监听程序的事件。
回调函数
回调(CallBack)函数,用于处理特定事件。
消息循环不断从消息队列中取出事件,然后根据事件类型调用对应的事件处理函数。
要自定义事件处理,必须重写类的事件处理函数。例如QWidget的鼠标事件:
virtual void |
mouseDoubleClickEvent(QMouseEvent *event) |
virtual void |
mouseMoveEvent(QMouseEvent *event) |
virtual void |
mousePressEvent(QMouseEvent *event) |
virtual void |
mouseReleaseEvent(QMouseEvent *event) |
事件传递
添加事件
事件的添加可以调用以下函数:
QCoreApplication::postEvent(QObject *receiver, QEvent *event) |
|---|
QCoreApplication::sendEvent(QObject *receiver, QEvent *event) |
两个函数的不同在于:
postEvent将事件添加到事件循环末尾,而sendEvent则通过notify()将事件直接发送给receiver。
事件的传递
Qt中事件产生后首先加入事件循环,然后
进入QApplication::notify()进行第一次事件分发。
被分发的事件首先进入QObject对象的事件过滤器(EventFilter)中进行事件过滤。
被过滤的事件进入QObject::event(),并根据事件类型调用事件处理函数(CallBack)。
若事件被接受(通过调用Accpet()),则事件传递结束;若事件被忽略(ignore())则将事件传递给父控件处理。
传递过程如下:

处理事件的方法
Qt 提供了5个级别的事件处理和事件过滤方法。[5.]
重新实现特殊的事件处理器
重新实现像 mousePressEvent()、keyPressEvent()和paintEvent()这样的事件处理器是到现在为止最常用的事件处理方式。我们已经看到很多有关这种处理方式的例子了。
重新实现QObject::event
通过event()函数的、重新实现,可以在这些事件到达特定的事件处理器之前处理它们。这种方式常用于覆盖 Tab键的默认意义。
那些没有特定事件处理器的不常见类型的事件中(例如,Qevent::HoverEnter()。当重新实现event()时,必须对那些没有明确处理的情况调用其基类的event()函数。 1
在QObect中安装事件过滤器
对象一旦使用installEventFilter()注册过,用于目标对象的所有事件都会首先发送给这个监视对象的eventFilter()函数。如果在同一个对象上安装了多个事件处理器,那么就会按照安装顺序逆序,从最近安装的到最先安装的,依次激活这些事件处理器。
在QApplication 对象中安装事件过滤器
一且在qApp(唯一的那个QApplication对象)中注册了事件过滤器,那么应用程序中每个对象的每个事件都会在发送到其他事件过滤器之前,先发送给这个eventFilter()函数。这种处理方式对于调试是非常有用的。它也可以用来处理那些发送给失效窗口部件的鼠标事件,因为QApplication常都会忽略这些事件。
子类化QApplication 并且重新实现 notify()
Qt调用 Qapplication::notify()来发送一个事件。重新实现这个函数是在事件过滤器得到所有事件之前获得它们的唯一方式。但事件过滤器通常更有用,因为可以同时有多个事件过滤器,而notify()函数却只能有一个。
安装事件过滤器
每个Qobject都有一个eventFilter函数,重写该函数并为控件安装事件过滤器。 2
该函数的完整定义如下:
bool |
eventFilter(QObject *watched, QEvent *event) |
当watched的事件event被处理后,返回true,否则返回false。
// widget.h class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget()override; // QObject interface bool eventFilter(QObject *watched, QEvent *event) override; private: QPushButton*pushButton=new QPushButton(“QPushButton”,this); }; |
|---|
// widget.cpp Widget::Widget(QWidget *parent) : QWidget(parent) { resize(500,500); pushButton->installEventFilter(this); this->installEventFilter(this); } Widget::~Widget() { } bool Widget::eventFilter(QObject *watched, QEvent *event) { if(watched==pushButton){ if(event->type()==QEvent::MouseButtonPress){ QMouseEvent*e=static_cast<QMouseEvent*>(event); qDebug()<<e->type(); } } return false; } |

保证密集事件时主线程的相应
当处理事件时,可能会由于某个特定的事件造成阻塞,导致程序无反应,解决这个问题的办法为:[5.]
多线程
使用多线程将耗时的事件放入子线程处理,从而防止了主线程阻塞。
调用processEvents()
在耗时代码中频繁调用 Qapplication::processEvents()。这个函数告诉Qt。处理所有那些还没有被处理的各类事件,然后再将控制权返还给调用者。实际上,Qapplication::èxec() 就是一个不停调用processEvents()函数的while循环。 3
事件与信号和槽机制的区别
信号发射后,立即调用槽函数,没有消息队列。而事件发生后,要经过消息队列排序和消息传递过程。
自定义事件处理必须重写对应回调函数(CallBack),而信号和槽无需重写类。
其他组件
添加应用图标

调试和日志系统
调试函数
qDebug()
要使用qDebug() [4]_函数,你需要包含QDebug头文件。 5
qDebug有两种使用方式:
qDebug(const char *message, …)
和普通的printf()类似,只是输出数字、文本到调试器。
qDebug()<<
后面可以接Qobject对象用于打印对象信息,也可以接字符串和数字。
QWidget widget; qDebug()<<”&widget信息:”<<&widget; |
效果如下:

qWarning
使用方法与qDebug()6.1.1 相同。具体行为与宏QT_FATAL_WARNINGS的值有关。
qCritical()
使用方法与qDebug()6.1.1 相同 6。具体行为与宏QT_FATAL_CRITICALS的值有关。
qFatal()函数
qFatal()函数的使用方式与printf()相同,但是qFatal()函数在调用后终止程序运行。 7
只有当程序发生严重错误、不得不退出时才建议使用qFatal()。
qInfo
使用方法与qDebug()6.1.1 相同 8。具体行为与宏QT_NO_INFO_OUTPUT的值有关。
调试宏
Q_ASSERT(bool test)宏
若test为false,将输出断言警告,并终止程序:
int a=1; Q_ASSERT(a==0); |
效果如下:

Q_ASSERT_X()
该宏的完整定义如下:
Q_ASSERT_X(bool test, const char *where, const char *what)。
当test为false是发出断言警告并终止程序。
int a=1; Q_ASSERT_X(a==0,”a的值”,”a的值不为0”); |
效果如下:

CHECK_PTR()
检查指针的值。

消息句柄
QtMessageHandler
文件读取
QFileInfo
#include <QCoreApplication> #include <QFileInfo> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QString filename=R”(C:/Users/31951/Desktop/2.txt)”; QFileInfo fileinfo(filename); qDebug()<<”fileName: “<<fileinfo.fileName(); qDebug()<<”filePath: “<<fileinfo.filePath(); qDebug()<<”baseName: “<<fileinfo.baseName(); qDebug()<<”bundleName: “<<fileinfo.bundleName(); qDebug()<<”completeBaseName: “<<fileinfo.completeBaseName(); qDebug()<<”absolutePath: “<<fileinfo.absolutePath(); qDebug()<<”absoluteFilePath: “<<fileinfo.absoluteFilePath(); qDebug()<<”canonicalPath: “<<fileinfo.canonicalPath(); qDebug()<<”canonicalFilePath: “<<fileinfo.canonicalFilePath(); qDebug()<<”completeSuffix: “<<fileinfo.completeSuffix(); qDebug()<<”suffix: “<<fileinfo.suffix(); return a.exec(); } |

Model/View结构
Qt对传统的MVC结构进行改动,生成了适用于Qt的Model-View-Delegate(模型-视图-委托)。
Model用于储存数据,View用于显示数据,Delegate用于修改数据。
View和ViewWidget
QlistWidget
Widget::Widget(QWidget *parent) : QWidget(parent) { QHBoxLayout*HLlayout=new QHBoxLayout(this); // 布局 QListWidget*LWwidget=new QListWidget(this); // ListWidget QStringList SLlist; // 设置布局 this->setLayout(HLlayout); this->setWindowTitle(“ListWidget”); HLlayout->addWidget(new QLabel(“浏览器”,this)); HLlayout->addWidget(LWwidget); // 添加元素 LWwidget->addItem(“Chrome”); // 第一种方法 SLlist<<”Chrome”<<”Firefox”<<”IE”<<”Maxthon”; // 第二种方法 LWwidget->addItems(SLlist); // 第三种方法 QListWidgetItem *LWIitem=new QListWidgetItem(QIcon(“C:\FireFox.ico”),”FireFox”); LWwidget->addItem(LWIitem); // LWwidget->setViewMode(QListView::IconMode); // 以图标形式显示 10 } |
显示效果如下:

QtreeWidget
Widget::Widget(QWidget *parent) : QWidget(parent) { QHBoxLayout*HLlayout=new QHBoxLayout(this); QTreeWidget*TWwidget=new QTreeWidget(this); // 设置布局 this->setLayout(HLlayout); this->setWindowTitle(“TreeWidget”); HLlayout->addWidget(new QLabel(“TreeWidget”,this)); HLlayout->addWidget(TWwidget); // 添加元素 QTreeWidgetItem*root=new QTreeWidgetItem( TWwidget, QStringList(“Node1”)); QTreeWidgetItem*Node1=new QTreeWidgetItem( root,QStringList(“Node1.1”)); new QTreeWidgetItem( Node1,QStringList(“Node1.1.1”)); // 设置选择框 Node1->setCheckState(0,Qt::Checked); // 设置表头 TWwidget->setHeaderLabel(“章节”); //TWwidget->setHeaderHidden(true); // 隐藏表头 } |
显示效果如下:

QtableWidget
Widget::Widget(QWidget *parent) : QWidget(parent) { QHBoxLayout*HLlayout=new QHBoxLayout(this); QTableWidget*TabWwidget=new QTableWidget(this); QStringList headers; // 设置布局 this->setLayout(HLlayout); this->setWindowTitle(“TableWidget”); HLlayout->addWidget(new QLabel(“TableWidget”,this)); HLlayout->addWidget(TabWwidget); // 设置表格大小 TabWwidget->setColumnCount(3); TabWwidget->setRowCount(5); // 添加元素 TabWwidget->setItem(0,0,new QTableWidgetItem(QString(“001”))); TabWwidget->setItem(0,1,new QTableWidgetItem(QString(“002”))); TabWwidget->setItem(1,0,new QTableWidgetItem(QString(“小明”))); // 设置表头 headers<<”ID”<<”Name”<<”Age”; TabWwidget->setHorizontalHeaderLabels(headers); } |
显示效果如下:

View
View和ViewWidget的继承关系如下:

View的使用需要结合模型(Model)使用,而ViewWidget不需要使用模型,适用于少量数据的显示。
模型和模型索引
QstringListModel
Widget::Widget(QWidget *parent) : QWidget(parent) { QHBoxLayout*HLlayout=new QHBoxLayout(this); QListView*LVview=new QListView(this); QStringListModel *model=new QStringListModel; // 设置布局 this->setLayout(HLlayout); this->setWindowTitle(“TableWidget”); HLlayout->addWidget(new QLabel(“TableWidget”,this)); HLlayout->addWidget(LVview); // 添加模型数据 QStringList data{ “Appel”, “HuaWei”, “XiaoMi”, “Vivo”, “OPPO” }; model->setStringList(data); LVview->setModel(model); // 设置View的模型 // 在尾部插入数据 model->insertRow(model->rowCount()); QModelIndex index=model->index(model->rowCount()-1,0); model->setData(index,”Google”); // 排序 model->sort(5,Qt::AscendingOrder); } |
效果如下:

模型索引和角色
自定义委托
// spinboxdelegate.h文件 class SpinBoxDelegate : public QStyledItemDelegate { Q_OBJECT public: SpinBoxDelegate(QObject*parent=nullptr); ~SpinBoxDelegate()override=default; // QAbstractItemDelegate interface QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; // QAbstractItemDelegate interface void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; |
// spinboxdelegate.cpp文件 SpinBoxDelegate::SpinBoxDelegate(QObject*parent): QStyledItemDelegate (parent) { } QWidget *SpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option) Q_UNUSED(index) QSpinBox*editor=new QSpinBox(parent); editor->setMinimum(0); editor->setMaximum(100); return editor; } void SpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QVariant value=index.model()->data(index,Qt::EditRole); QSpinBox*spinbox=static_cast<QSpinBox*>(editor); spinbox->setValue(value.toInt()); } void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QSpinBox*spinbox=static_cast<QSpinBox*>(editor); spinbox->interpretText(); model->setData(index,spinbox->value(),Qt::EditRole); } void SpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index); editor->setGeometry(option.rect); } |
设置委托:
// 设置委托 SpinBoxDelegate*SBDdelegate=new SpinBoxDelegate(this); LVview->setItemDelegate(SBDdelegate); |
效果如下:

视图选择
视图的选择使用了QItemSelectionModel类,View和ViewWidget已经内置了选择模型,可以使用selectionModel()来获取选择模型。
选择模式
内置的选择模式有以下几种:
枚举:SelectionMode
常数 |
值 |
描述 |
|---|---|---|
QAbstractItemView: |
1 |
能且仅能选择一项,可以不选 |
SingleSelection |
||
QAbstractItemView: |
4 |
只能连续选取。可以不选 |
ContiguousSelection |
||
QAbstractItemView: |
3 |
按中Shift键时为连续选择,按中Ctrl键时为间隔选择 |
ExtendedSelection |
||
QAbstractItemView: |
2 |
一次选择一项,可选择多项,再次点击取消选择 |
MultiSelection |
||
QAbstractItemView: |
0 |
不可选择 |
NoSelection |
要设置选择模式,请使用setSelectionMode()函数。
排序过滤
模型内置了两种排序方式:
枚举:Qt::SortOrder
常数 |
值 |
描述 |
|---|---|---|
Qt::AscendingOrder |
0 |
按字母升序排列 |
Qt::DescendingOrder |
1 |
按字母降序排列 |
要进行排序,请使用QstringListModel:: sort()函数。
代理
更高级的排序过滤功能由代理类:QSortFilterProxyModel提供。
QSortFilterProxyModel提供了基于正则表达式、Unix通配符、普通文本的过滤功能和基于函数的排序功能。
在使用代理后,MVC的显示机理变为:
QListView*LVview=new QListView(this); QStringListModel *model=new QStringListModel; QSortFilterProxyModel*filtermodel=new QSortFilterProxyModel; LVview->setModel(filtermodel); // 添加模型数据 QStringList data{ “Appel”, “HuaWei”, “XiaoMi”, “Vivo”, “OPPO” }; model->setStringList(data); // 过滤数据 filtermodel->setSourceModel(model); filtermodel->setFilterRegExp(R”(^A)”); |
显示效果如下:

自定义模型
自定义只读模型
SQL
进程通信
剪切板
MIME数据类型
MIME是一种类型标准,MIME定义的标准可以获取数据的形式,进而进行解析,常用的MIME类型如下 11:
MIME类型 |
数据类型 |
|---|---|
text/html |
Html |
text/plain |
纯文本 |
text/uri-list |
Url |
application/x-color |
颜色 |
video/x-msvideo |
Avi视频 |
image/jpg |
Jpg格式图片 |
Qt通过QMimeData类实现对MIME数据的储存与获取。事实上,只要保证数据可以解析,可以人为规定一个MIME数据类型,即使没有标准库会的承认。
剪切板
QClipboard类通过储存MIME类型数据储存数据,实现了剪切板的操作。通过QApplication::clipboard()可以获取应用程序的剪切板指针,从而对剪切板进行操作。
不同的系统对剪切板概念的支持不尽相同,在Windows和macOS中,剪切板属于全局资源,任何对剪切板的操作都是对全局剪切板的操作。 12
Widget::Widget(QWidget *parent) : QWidget(parent) { QHBoxLayout*HLlayout=new QHBoxLayout(this); this->setLayout(HLlayout); QLabel*lbl=new QLabel; HLlayout->addWidget(lbl); QMimeData*mimedata=new QMimeData; mimedata->setData(“text/plain”,”<h1>Hello</h1>”); QClipboard*clipboard=QApplication::clipboard(); clipboard->setMimeData(mimedata); lbl->setText(clipboard->text()); } |
效果如下:


拖放
从拖放中读取数据
// widget.h class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget()override; // QWidget interface protected: void dragEnterEvent(QDragEnterEvent *event)override; void dropEvent(QDropEvent *event)override; private: QTextBrowser*textbrown=new QTextBrowser(this); QHBoxLayout*layout=new QHBoxLayout(this); }; |
// widget.cpp Widget::Widget(QWidget *parent) : QWidget(parent) { layout->addWidget(textbrown); setLayout(layout); setAcceptDrops(true); textbrown->setAcceptDrops(false); } Widget::~Widget() { } void Widget::dragEnterEvent(QDragEnterEvent *event) { if(event->mimeData()->hasText()){ event->acceptProposedAction(); } } void Widget::dropEvent(QDropEvent *event) { QList<QUrl>urls=event->mimeData()->urls(); if(urls.isEmpty())return; QString filename=urls.first().toLocalFile(); if(filename.isEmpty()) return; QFile file(filename); file.open(QFile::ReadOnly); if(!file.isOpen()){ qDebug()<<”文件打开失败”; return; } textbrown->setText(file.readAll()); file.close(); } |
效果如下:

该软件在加载时没有读取文件,又没有定义打开文件的接口,且无法编辑。只能通过拖放来更改窗口内容。
自定义拖放数据
要自定义拖放数据,有以下几种方法:[4.]
将自定义数据作为 QByteArray 对象,使用 QMimeData::setData()函数作为二进制
数据存储到 QMimeData 中,然后使用 QMimeData::data()读取
继承 QMimeData,重写其中的 formats()和 retrieveData()函数操作自定义数据
如果拖放操作仅仅发生在同一个应用程序,可以直接继承 QMimeData,然后使用任
意合适的数据结构进行存储
数据库
数据解析
解析XML
使用DOM解析XML
使用SAM解析XML
解析JSON
使用Qjson解析JSON
使用QjsonDocument解析JSON
网络
线程
绘图
双缓冲
请查看C++ GUI Qt4编程(第二版)第五章
Qt基本组件
菜单
QMenu用于提供主界面菜单、菜单栏菜单、弹出菜单。
QMenu的非继承属性共有五个:
属性名 |
作用 |
|---|---|
icon |
菜单图标 |
title |
菜单标题 |
separatorsCollapsible |
连续的分割线是否折叠(默认为true) 13 |
toolTipsVisible |
是否显示toolTips |
tearOffEnabled |
菜单是否可分离出一个副本(默认为false) |
QMenu常用的函数有以下几种:
返回值 |
函数 |
作用 |
|---|---|---|
QAction* |
addAction() |
重载函数,用于添加菜单项 |
QMeun* |
addMenu() |
重载函数,用于添加子菜单 |
QAction* |
addSeparator() |
添加一个分割线 |
QAction* |
insertMenu() |
插入一个菜单 |
QAction* |
insertSeparator() |
插入一个分割线 |
void |
popup() |
在指定位置弹出菜单 |
常用信号:
信号名 |
何时发射 |
|---|---|
aboutToHide() |
用户尝试隐藏菜单前 |
aboutToShow() |
用户尝试显示菜单前 |
hovered() |
有菜单项处于活动前 |
triggered() |
有菜单项被激活时 |
按钮
QCheckBox
QCheckBox定义了一个带有QLabel的复选框。
主要属性如下:
属性名 |
作用 |
|---|---|
tristate |
tri-state,复选框是否有三个状态,默认为false,仅有两个状态。 |
枚举类型:
Qt::CheckState
描述 |
值 |
解释 |
|---|---|---|
Qt::Unchecked |
0 |
未选中状态 |
Qt::PartiallyChecked |
1 |
半选中状态 |
Qt::Checked |
2 |
选中状态 |
常用函数
返回值 |
函数 |
作用 |
|---|---|---|
void |
setCheckState() |
设置checkbox的复选状 态,该函数适用于tristate为true时使用。 |
void |
setChecked() |
设置checkbox 是否已选择,适用于tristate为false时使用 |
常用信号:
信号名 |
何时发射 |
|---|---|
clicked() |
按钮点击后 |
pressed() |
按钮按下时 |
released() |
按钮释放时 |
toggled() |
勾选状态改变时 |
stateChanged() |
状态改变时 |
按钮组合
按钮组合实现了对一组按钮的控制,主要用于按钮组件的互斥和关联。按钮组合由QButtonGroup类实现,其继承自QObject,本质为按钮容器。 15
主要属性如下:
属性名 |
作用 |
|---|---|
exclusive |
容器内按钮是否互斥,默认为true,仅有一个按钮可以被选中 |
常用函数
返回值 |
函数 |
作用 |
|---|---|---|
QAbstractButton * |
QB uttonGroup::button() |
返回指定id的按钮,如 按钮不存在,则返回0 |
Q List<QAbstractButton *> |
QBu ttonGroup::buttons() |
返回容器内的所有按钮 |
QAbstractButton * |
QButtonGr oup::checkedButton() |
返回被点击过的按钮, 若无,则返回0 18 |
int |
QButt onGroup::checkedId() |
返回被点击的按钮 的id,若无,则返回-1 |
int |
QButtonGroup::id() |
返回指定按钮的id |
void |
Q ButtonGroup::setId() |
设置指定 按钮的id,id不能为-1 |
void |
QButtonG roup::removeButton() |
从按 钮组合中移除指定按钮 |
常用信号:
信号名 |
何时发射 |
|---|---|
buttonClicked(QAbstractButton *button) |
button被点击时 |
buttonClicked(int id) |
任何按钮被点击时 |
buttonPressed(QAbstractButton *button) |
button被按下时 |
buttonPressed(int id) |
任何按钮被按下时 |
buttonReleased(QAbstractButton *button) |
任何按钮被释放时 |
buttonReleased(int id) |
任何按钮被释放时 |
buttonToggled(QAbstractButton *button, bool checked) |
button状态切换时 |
buttonToggled(int id, bool checked) |
任何按钮状态被切换时 |
滚动条
使用QScrollArea创建滚动
使用QScrollArea作为Widget容器可以对Widget附加水平和垂直滚动条。
QScrollArea的继承关系如下:
Widget::Widget(QWidget *parent) : QWidget(parent) { QHBoxLayout*layout=new QHBoxLayout; setLayout(layout); QScrollArea*scrollarea=new QScrollArea(this); layout->addWidget(scrollarea); QLabel*lbl=new QLabel(this); lbl->resize(600,600); scrollarea->setWidget(lbl); } |
效果如下:

在无需滚动条时,QScrollArea的滚动条会自动隐藏。QScrollArea的滚动条仅在需要的方向上出现(水平或垂直方向)。这种方式又被称为“按需滚动”。
使用QScrollBar创建滚动条
容器
QToolBox
QToolBox对QWidget提供了列状组织,每次只显示QWidget列表中的一个:

QToolBox以item组织列表,每个item代表了一个QWidget。其继承关系如下:
主要属性如下:
属性名 |
作用 |
|---|---|
count |
QToolBox item的总数 |
currentIndex |
QToolBox当前显示的item的索引 |
常用函数
返回值 |
函数 |
作用 |
|---|---|---|
int |
addItem() |
添加item |
QWidget* |
currentWidget() |
返回当前显示的item对应的QWidget。 |
int |
indexof() |
返回特定QWidget对应的索引 |
int |
insertItem() |
将item插入带特定位置,若越 界则插入到底部。返回插入的item的索引 |
bool |
isItemEnabled() |
如果启用了位 置索引项,则返回true;否则返回false。 |
常用信号:
信号名 |
何时发射 |
|---|---|
currentChanged(int index) |
显示的item发生改变时 |
customContextMenuRequested(const QPoint &pos) |
请求上下文菜单时 19 |
QTabBar
QTabBar提供了一个标签栏(tab bar)。Qt提供了一个更简便的的类:QTabWidget。两者均继承自QWidget。
枚举:
QTabBar::ButtonPosition
描述 |
值 |
解释 |
|---|---|---|
QTabBar::LeftSide |
0 |
widget位于标签栏左侧 |
QTabBar::RightSide |
1 |
widget位于标签栏右侧 |
QTabBar::SelectionBehavior
描述 |
值 |
解释 |
|---|---|---|
QTabBar::SelectLeftTab |
0 |
当标签被移除时切换到左侧标签 |
QTabBar::SelectRightTab |
1 |
当标签被移除时切换到右侧标签 |
QTabBar::Shape
描述 |
值 |
解释 |
|---|---|---|
QTabBar::RoundedNorth |
0 |
阴影边界。expanding为false时横线位于下方 |
QTabBar::RoundedSouth |
1 |
阴影边界。expanding为false时横线位于下方 |
QTabBar::RoundedWest |
2 |
阴影边界。标签栏左侧竖排显示,文字从右向左看 |
QTabBar::RoundedEast |
3 |
阴影边界。标签栏左侧竖排显示,文字从左向右看 |
QTabBar::TriangularNorth |
4 |
线条边界。expanding为false时横线位于下方 |
QTabBar::TriangularSouth |
5 |
线条边界expanding为false时横线位于下方 |
QTabBar::TriangularWest |
6 |
线条边界标签栏左侧竖排显示,文字从右向左看 |
QTabBar::TriangularEast |
7 |
线条边界标签栏左侧竖排显示,文字从左向右看 |
主要属性如下:
属性名 |
作用 |
|---|---|
autoHide |
标签栏少于两个时自动隐藏,默认为false |
expanding |
自动 调正大小以占满所有可用空间,默认为true |
changeCurrentOnDrag |
当接受拖放事 件时自动切换到被drop的标签。默认为false |
count |
标签栏的数量 |
movable |
标签栏是 否可以通过拖动调整显示顺序。默认为false |
currentIndex |
返回当前 显示的标签页,若没有显示的标签页返回-1 |
selectionBehaviorOnRemove |
设定移除后显示的标签页,与 QTabBar::SelectionBehavior有关 |
documentMode |
该属性用于提示标签栏的绘制 |
shape |
|
drawBase |
标签栏是否绘制底线,默认为true |
tabsClosable |
是否在标签栏显示关闭按钮,默认为false |
elideMode |
越界时如何省略文本,与Qt::T extElideMode有关,默认值依赖于style |
usesScrollButtons |
标签栏过多时是否显示按钮 以滚动显示标签栏。默认值依赖于style设置 |
常用函数
返回值 |
函数 |
作用 |
|---|---|---|
int |
addTab() |
添加标签页 |
int |
insertTab() |
插入标签页 |
void |
setTabButton() |
在指定索引的QTabBar::But tonPosition位置上添加一个QWidget |
void |
setTabData() |
为指定位置设置数据 |
int |
tabAt() |
返回 指定坐标处的标签页索引,若无则返回-1 |
QWidget * |
tabButton() |
常用槽
返回值 |
函数 |
作用 |
|---|---|---|
void |
setCurrentIndex(int index) |
切换当前标签 |
常用信号:
信号名 |
何时发射 |
|---|---|
currentChanged(int index) |
当前标签页改变时 |
tabBarClicked(int index) |
标签页被点击时 |
tabBarDoubleClicked(int index) |
标签页被双击时 |
tabCloseRequested(int index) |
标签页被关闭前 |
tabMoved(int from, int to) |
标签页被移动时 |
QTabWidget
QTabWidget是Qt根据QTabBar为用户提供的便利类。
枚举:
QTabWidget::TabPosition
描述 |
值 |
解释 |
|---|---|---|
QTabWidget::North |
0 |
|
QTabWidget::South |
1 |
|
QTabWidget::West |
2 |
|
QTabWidget::East |
3 |
QTabWidget::TabShape
描述 |
值 |
解释 |
|---|---|---|
QTabWidget::Rounded |
0 |
圆形标签,默认选项 |
QTabWidget::Triangular |
1 |
三角形标签 |
主要属性如下:
属性名 |
作用 |
|---|---|
count |
标签栏的数量 |
currentIndex |
当前标签的索引 |
documentMode |
该属性用于提示标签栏的绘制 |
elideMode |
|
movable |
标签栏是否可以通过拖动调整显示顺序。默认为false |
tabBarAutoHide |
标签栏少于两个时自动隐藏,默认为false |
tabPosition |
|
tabShape |
根据 QTabWidget::TabShape设定标签栏显示位置 |
tabsClosable |
是否在标签栏显示关闭按钮,默认为false |
usesScrollButtons |
标签栏过多时是否 显示按钮以滚动显示标签栏。默认值依赖于style设置 |
常用函数
返回值 |
函数 |
作用 |
|---|---|---|
int |
addTab() |
添加标签页 |
int |
insertTab() |
插入标签页 |
QWidget * |
cornerWidget() |
返回位于Qt::Corner处的标签widget |
int |
currentIndex() |
返回当前标签页索引 |
QWidget * |
currentWidget() |
返回当前标签的widget |
void |
removeTab(int index) |
移除指定标签 |
常用槽
返回值 |
函数 |
作用 |
|---|---|---|
void |
setCurrentIndex(int index) |
切换当前标签 |
void |
setCurrentWidget(QWidget *widget) |
切换当前标签 |
常用信号:
信号名 |
何时发射 |
|---|---|
currentChanged(int index) |
当前标签页改变时 |
tabBarClicked(int index) |
标签页被点击时 |
tabBarDoubleClicked(int index) |
标签页被双击时 |
tabCloseRequested(int index) |
标签页被关闭前 |
示例:
添加QTabWidget的关闭标签功能:
QTabWidget*tw=new QTabWidget(this); tw->addTab(new QPushButton(this),”btn1”); tw->addTab(new QPushButton(this),”btn2”); tw->setTabsClosable(true); connect(tw,&QTabWidget::tabCloseRequested, tw,&QTabWidget::removeTab); // 添加关闭标签功能 |

QStackedWidget
QStackedWidget继承自QFrame。为widget提供了栈式显示,同一时刻仅显示一个widget。
主要属性如下:
属性名 |
作用 |
|---|---|
count |
储存了栈内widget的数量 |
currentIndex |
当前显示的widget的索引 |
常用函数
返回值 |
函数 |
作用 |
|---|---|---|
int |
addWidget() |
添加widget |
int |
indexOf() |
插入widget |
void |
removeWidget() |
删除widget |
QWidget * |
currentWidget() |
当前显示的widget |
int |
indexOf() |
指定widget的索引 |
QWidget * |
widget |
指定索引的widget |
常用槽
返回值 |
函数 |
作用 |
|---|---|---|
void |
setCurrentIndex(int index) |
设定显示的index |
void |
setCurrentWidget(QWidget *widget) |
设定显示的widget |
常用信号:
信号名 |
何时发射 |
|---|---|
currentChanged(int index) |
当前显示的widget改变时 |
widgetRemoved(int index) |
widget被移除时 |
演示:
QHBoxLayout*HL_layout=new QHBoxLayout(this); QStackedWidget*sw=new QStackedWidget(this); HL_layout->addWidget(sw); sw->addWidget(new QPushButton(“btn1”,this)); sw->addWidget(new QPushButton(“btn2”,this)); sw->addWidget(new QPushButton(“btn3”,this)); sw->addWidget(new QPushButton(“btn4”,this)); auto btn_remove=new QPushButton(“remove”,this); auto btn_pre=new QPushButton(“pre”,this); auto btn_next=new QPushButton(“next”,this); HL_layout->addWidget(btn_pre); HL_layout->addWidget(btn_next); HL_layout->addWidget(btn_remove); connect(btn_remove,&QPushButton::clicked,[sw](){ sw->removeWidget(sw->currentWidget()); }); connect(btn_pre,&QPushButton::clicked,[sw](){ int index=sw->currentIndex()-1; if(index<0) index=sw->count()-1; qDebug()<<”pre:”<<index; sw->setCurrentIndex(index); }); connect(btn_next,&QPushButton::clicked,[sw](){ int index=sw->currentIndex()+1; if(index>sw->count()-1) index=0; qDebug()<<”next:”<<index; sw->setCurrentIndex(index); }); |

QDockWidget
QDockWidget继承自QWidget,提供了一个dock widget,其既可以作为程序的一部分,又可单独分离成为一个桌面顶级窗口。
常用枚举
QDockWidget::DockWidgetFeature
描述 |
值 |
解释 |
|---|---|---|
QDockWidget ::DockWidgetClosable |
0x01 |
dock widget可以被关闭 |
QDockWidge t::DockWidgetMovable |
0x02 |
dock wid get可以在docks中移动 |
QDockWidget: :DockWidgetFloatable |
0x04 |
dock widget可 以分离成一个独立窗口 |
QDockWidget::DockWi dgetVerticalTitleBar |
0x08 |
dock widget将 标题栏垂直显示在左侧 |
QDockWidget::A llDockWidgetFeatures |
DockWidgetClosab le|DockWidgetMovable |DockWidgetFloatable |
(过时)该窗口可 以被移动、附加、分离 |
QDockWidget:: NoDockWidgetFeatures |
0x00 |
该dock widget无 法被移动、附加、分离 |
主要属性如下:
属性名 |
作用 |
|---|---|
allowedAreas |
dock widget可以被停靠的区域,其值与Qt::DockWi dgetArea有关,默认为Qt::AllDockWidgetAreas |
features |
dock wi dget的特征,与QDockWidget::DockWidgetFeature有关,默认为QDockWidget::AllDockWidgetFeatures |
floating |
dock widget是否可分离为一个独立的窗口 |
windowTitle |
窗口标题 |
常用函数
返回值 |
函数 |
作用 |
|---|---|---|
void |
setTitleBarWidget() |
使用一个自定义的widget作为dock widget的标题栏 |
void |
setWidget() |
设置dock widget的默认组件 |
QAction * |
toggleViewAction() |
该QAction可用于控制该dock widget |
常用信号:
信号名 |
何时发射 |
|---|---|
allowedAreasChanged() |
允许停靠区域发生改变时 |
dockLocationChanged() |
停靠区域发生改变时 |
featuresChanged() |
dock widget特征发生改变时 |
topLevelChanged() |
floating属性改变时 |
visibilityChanged() |
dock widget被隐藏/显示时 |
例:
QHBoxLayout*HL_layout=new QHBoxLayout(this); QDockWidget*dw=new QDockWidget(this); HL_layout->addWidget(dw); dw->setWidget(new QPushButton(“btn1”,this)); dw->setTitleBarWidget(new QPushButton(“btn_bar”,this)); |
效果如下:

QMdiArea
QMdiArea中文译为“多文档界面”,其提供了一个类似桌面的区域,其区域内可显示多个子窗口。并且所有子窗口的最大化、最小化等效果以QMdiArea为基准。其继承关系如下:
QMdiArea::AreaOption
描述 |
值 |
解释 |
|---|---|---|
QMdiArea::DontM aximizeSubWindowOnActivation |
0x1 |
当活动 子窗口最大化时,最大化下一个 被激活的子窗口。默认为true。 |
QMdiArea::ViewMode
描述 |
值 |
解释 |
|---|---|---|
QMdiArea::SubWindowView |
0 |
带有frames的子窗口(默认) |
QMdiArea::TabbedView |
1 |
带有标签栏的子窗口 |
QMdiArea::WindowOrder
描述 |
值 |
解释 |
|---|---|---|
QMdiArea::CreationOrder |
0 |
按子窗口的创造顺序返回 |
QMdiArea::StackingOrder |
1 |
按子窗口的前 后顺序返回,最靠前的排在最后 |
QMd iArea::ActivationHistoryOrder |
2 |
按子窗口的活动顺序返回 |
主要属性如下:
属性名 |
作用 |
|---|---|
activationOrder |
该属性储存了子窗 口列表的属性,与QMdiArea::WindowOrder有关 |
background |
该属性定义了子窗口的背景画刷。默认为灰色 |
documentMode |
此属性定义选项 栏是否在选项视图模式中设置为文档模式。默认为true |
tabPosition |
定义了标签模式下标签 栏的位置,与QTabWidget::TabPosition有关。 |
tabShape |
定义了标签形状,与QTabWi dget::TabShape有关。默认为QTabWidget::Rounded |
tabsClosable |
标签是否可关闭,默认为false |
tabsMovable |
标签是否可移动,默认为false |
viewMode |
该属性定义了子 窗口的显示方式,与QMdiArea::ViewMode有关 |
常用函数
返回值 |
函数 |
作用 |
|---|---|---|
QMdiSubWindow * |
addSubWindow() |
添加子窗口 |
void |
removeSubWindow() |
移除子窗口 |
QMdiSubWindow * |
activeSubWindow() |
返回当前被激 活的子窗口.若无返回0 |
QMdiSubWindow * |
currentSubWindow() |
返回当 前子窗口,若无则返回0 |
QList<QMdiSubWindow *> |
subWindowList() |
der**的顺序返回列表 |
常用槽
返回值 |
函数 |
作用 |
|---|---|---|
void |
activateNextSubWindow() |
激活下一个子窗口 |
void |
activatePreviousSubWindow() |
激活上一个子窗口 |
void |
cascadeSubWindows() |
层叠子窗口 |
void |
closeActiveSubWindow() |
关闭活动的子窗口 |
void |
closeAllSubWindows() |
关闭所有子窗口 |
void |
setActiveSubWindow() |
设置活动子窗口 |
void |
tileSubWindows() |
以平铺模式排列所有子窗口 |
常用信号:
信号名 |
何时发射 |
|---|---|
subWindowActivated(QMdiSubWindow *window) |
window被激活后,, 若windows=0,则QMdiArea反激活最 后一个活动窗口,此时没有激活窗口 |
例:
QHBoxLayout*HL_layout=new QHBoxLayout(this); QMdiArea*ma=new QMdiArea(this); HL_layout->addWidget(ma); ma->addSubWindow(new QPushButton(“btn1”,this)); ma->addSubWindow(new QPushButton(“btn2”,this)); ma->addSubWindow(new QPushButton(“btn3”,this)); ma->addSubWindow(new QPushButton(“btn4”,this)); ma->tileSubWindows(); |

QMdiSubWindow
QMdiSubWindow继承自QWidget,代表了QMdiArea中的一个子窗口。
QMdiSubWindow::SubWindowOption
描述 |
值 |
解释 |
|---|---|---|
QMd iSubWindow::RubberBandResize |
0x4 |
如果启用此选项 ,则使用rubber来表示子窗口的 大纲,用户将调整此选项的大小 ,而不是子窗口本身的大小。因 此,子窗口将保持其原始位置和 大小,直到完成调整大小操作, 此时它将接收一个QResizeEvent 。默认情况下,此选项是禁用的 |
Q MdiSubWindow::RubberBandMove |
0x8 |
如果启用此选项,则使 用rubber来表示子窗口的大纲, 用户将移动此控件而不是子窗口 本身。因此,子窗口将保持在原 来的位置,直到移动操作完成, 此时将向窗口发送QMoveEvent。 默认情况下,此选项是禁用的。 |
主要属性如下:
属性名 |
作用 |
|---|---|
keyboardPageStep |
使用page键移动窗口时的步长,默认为20px |
keyboardSingleStep |
使用键盘箭头移动窗口时的步长,默认为5px |
常用函数
返回值 |
函数 |
作用 |
|---|---|---|
QMdiArea * |
mdiArea() |
返回控件所处的MdieArea |
void |
setSystemMenu() |
设置子窗口的默认系统菜单 |
void |
setOption() |
设置子窗口的选项,与QMdi SubWindow::SubWindowOption相关 |
常用槽
返回值 |
函数 |
作用 |
|---|---|---|
void |
showShaded() |
调用此函数使子窗口进入阴影模式。 当子窗口为阴影时,只有标题栏是可见的。 |
void |
showSystemMenu() |
显示系统菜单 |
常用信号:
信号名 |
何时发射 |
|---|---|
aboutToActivate() |
子窗口被激活前 |
windowStateChanged() |
windows state改变后 |
例:
QHBoxLayout*HL_layout=new QHBoxLayout(this); QMdiArea*ma=new QMdiArea(this); HL_layout->addWidget(ma); ma->addSubWindow(new QPushButton(“btn1”,this)); ma->addSubWindow(new QPushButton(“btn2”,this)); ma->addSubWindow(new QPushButton(“btn3”,this)); ma->addSubWindow(new QPushButton(“btn4”,this)); ma->tileSubWindows(); foreach(QMdiSubWindow* x,ma->subWindowList()){ connect(x,&QMdiSubWindow::aboutToActivate,[x](){ qDebug()<<”子窗口”<<x<<”即将被激活”; QTest::qSleep(5000); }); } |

也由此可见:QMdiArea在创建时会逐个激活所有子窗口
输入组件
QLineEdit
QVBoxLayout*layout=new QVBoxLayout(this); QLineEdit*username=new QLineEdit(this); QLineEdit*passwd=new QLineEdit(this); QPushButton*itOk=new QPushButton(this); /// setLayout(layout); layout->addWidget(username); layout->addWidget(passwd); layout->addWidget(itOk); // 设置QLineEdit的属性 username->setClearButtonEnabled(true); username->setAcceptDrops(true); passwd->setEchoMode(QLineEdit::Password); passwd->setClearButtonEnabled(true); connect(username,&QLineEdit::editingFinished,[this](){ passwd->setFocus(); }); connect(passwd,&QLineEdit::editingFinished,[this](){ emit itOk->clicked(); }); connect(itOk,&QPushButton::clicked,[](){ qDebug(“clicked”); }); |
定时器
布局管理
QBoxLayout
QFormLayout
QGridLayout
QStackedLayout
数据
QCache
QDataWidgetMapper
Qt美化
QSS
QStyleOption
QStyleOptionComplex
QStyleOptionDockWidget
QStyleOptionFocusRect
QStyleOptionFrame
QStyleOptionGraphicsItem
QStyleOptionHeader
QStyleOptionProgressBar
QStyleOptionRubberBand
QStyleOptionTab
QStyleOptionTabBarBase
QStyleOptionTabWidgetFrame
QStyleOptionToolBar
QStyleOptionToolBox
QStyleOptionViewItem
QStyle
帮助系统
请查阅C++ GUI Qt4编程(第二版)第17章
Qt国际化
多媒体
录音
使用QAudioRecorder录制音频
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QAudioRecorder> #include <QDebug> #include <QFileDialog> #include <QLabel> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); void audioRecord(); void startRecord(); private: Ui::MainWindow *ui; QAudioRecorder record; QString fileName; QLabel statusFileName; }; #endif // MAINWINDOW_H |
#include “mainwindow.h” #include “ui_mainwindow.h” MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); ui->statusBar->addWidget(&statusFileName); audioRecord(); connect(ui->action_Open,&QAction::triggered,[&](){ fileName=QFileDialog::getOpenFileName(this,tr(“选择文件”)); statusFileName.setText(fileName); }); connect(ui->btn_Stop,&QPushButton::clicked,[this](){ record.stop(); }); connect(ui->btn_Pause,&QPushButton::clicked,[this](){ record.pause(); }); connect (ui->btn_Start,&QPushButton::clicked,this,&MainWindow::startRecord); } MainWindow::~MainWindow() { delete ui; } void MainWindow::audioRecord() { foreach(auto&str,record.audioInputs()) ui->comBox_AudioList->addItem(str); connect(ui->btn_Start,&QPushButton::clicked,[&](){ }); QAudioEncoderSettings settings; settings.setBitRate(96000); settings.setCodec(“audio/pcm”); settings.setQuality(QMultimedia::EncodingQuality::VeryHighQuality); settings.setChannelCount(8); record.setAudioSettings(settings); record.setAudioInput(record.defaultAudioInput()); } void MainWindow::startRecord() { record.setOutputLocation(QFileDialog::getOpenFileUrl()); record.record(); } |

自定义插件和库
自定义Qt Designer Widget插件
加载ActiveX控件
QAxWidget
Qt枚举
Qt::TextElideMode
描述 |
值 |
解释 |
|---|---|---|
Qt::ElideLeft |
0 |
省略开头 |
Qt::ElideRight |
1 |
省略结尾 |
Qt::ElideMiddle |
2 |
省略中间 |
Qt::ElideNone |
3 |
不显示省略号 |
Qt::Corner
描述 |
值 |
解释 |
|---|---|---|
Qt::TopLeftCorner |
0x00000 |
矩形左上角 |
Qt::TopRightCorner |
0x00001 |
矩形右上角 |
Qt::BottomLeftCorner |
0x00002 |
矩形左下角 |
Qt::BottomRightCorner |
0x00003 |
矩形右下角 |
Qt::DockWidgetArea
描述 |
值 |
解释 |
|---|---|---|
Qt::LeftDockWidgetArea |
0x1 |
|
Qt::RightDockWidgetArea |
0x2 |
|
Qt::TopDockWidgetArea |
0x4 |
|
Qt::BottomDockWidgetArea |
0x8 |
|
Qt::AllDockWidgetAreas |
DockWidgetArea_Mask |
|
Qt::NoDockWidgetArea |
0 |
常见错误
undefined reference to `vtable for’
方法一:
“QT中,类要支持信号与槽机制,需要继承自QObject并在头文件开头添加Q_OBJECT宏.
如果使用QtCreator创建类时,没有选择继承自QObject类或其子类,而在创建后手工修改继承自QObject并手工添加Q_OBJECT宏,则在编译时有可能会出现”undefined reference to `vtable for’…….”错误.
解决方法: 把新创建的类从项目中移除(主要不要从磁盘上删除),然后再添加进功能,QtCreator就会重新解析此类,再编译就不再会出现上述错误. [2.]
方法二:
使用QT编程时,当用户自定义了一个类,只要类中使用了信号或槽。Code::Blocks编译就会报错(undefined reference to `vtable for)。Google上有很多这个问题的回答,但很多说的很模糊,或者根本就不可行.其实,QT有自己的编译方法.
不用IDE写一个类,QT的编译步骤是:
cd 源代码目录
qmake -project 20
qmake project_name.pro
make (如果你装的是minGW的话,就用mingw32-make.exe)[3.]
undefined reference to

错误原因:函数仅定义,未实现
incomplete type
完整错误显示如下:
widget.h:19:20: error: allocation of incomplete type ‘QMenu’ qabstractitemview.h:56:7: note: forward declaration of ‘QMenu’ |
原因:未包含QMenu的头文件。
connect没有匹配的函数

原因:
信号或槽有重载
使用msvc编译

解决办法:msvc在编译UTF-8文件时,会将无BOM的文件当做本地编码(GB2312)处理,有BOM的才当作UTF-8文件,因此,,将文件改为UTF-8+BOM的即可:

然后重新保存文件。[6.]
MSVC qDebug()输出乱码

解决方法:[7.]
在头文件内添加一句宏 21:
#pragma execution_character_set(“utf-8”) |
QDesktopServices打开中文路径乱码
代码如下:
void MainWindow::openFile(const QModelIndex &modelindex) { QString filePath=FileLists->at(modelindex.row()); qDebug()<<filePath; QDesktopServices::openUrl(filePath); } |
效果如下:

解决方法如下:
将QDesktopServices::openUrl(filePath);替换为 QDesktopServices::openUrl(QUrl::fromLocalFile(filePath)); |
Fault tolerant heap shim applied to current process
问题原因:以前程序崩溃导致操作系统“记上了”。要解决,需要:
打开注册表,找到 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers\ ,选中Layers键值,从右侧列表中删除自己的那个程序路径即可。。
但是根本原因是程序的业务逻辑错了。我在使用connect+lambda表达式时出错了,后来换成connect+槽函数就没问题了
附录
优秀资源
如何理解Qt的viewport视图区?
https://jingyan.baidu.com/article/7082dc1c073a49e40a89bd9c.html
viewport视图区表示描述一个任一矩形的物理坐标系,而“window”窗口描述的是同样的矩形的逻辑坐标系。通常逻辑和物理坐标系是重合的,并且与绘图设备的矩形是一样的。

工具/原料
QtCreator4.4.1
Qt5.9.2
方法/步骤
通过窗口-视图变换,我们可以定制逻辑坐标系。同时使得绘图代码独立于绘图设备。譬如,逻辑坐标上从(-50,-50)到(50,50),(0,0)点在中心。那么painter的绘图窗口就可以如下图设置:
这样逻辑坐标(-50,-50)就对应到绘图设备的物理坐标(0,0)了。setViewport()函数和setWindow()函数是一对姐妹函数,一个设置物理设备坐标系,一个设置逻辑坐标系。默认情况,两者是一样的。通过设置“window”或者viewport矩形区域,我们就可以对这两个坐标系进行线性变换了。因此,一般我们为了防止变形,viewport和”window“会保持一个相同的宽高比。我们通常的绘图操作是在viewport坐标系实现的,独立于绘图设备的。这里窗口-视图区的转换仅仅是一个线性转换,并不会进行裁剪动作。意思就是如果你在当前设置的window中绘图,图形依然会通过同样的线性代数方式被转换到viewport。
视图区,窗口,和转换矩阵决定了逻辑QPainter坐标如何映射到绘图设备坐标系的方式。通常,world转换矩阵是唯一的矩阵,窗口和视图区的设置等价于绘图设备的设置。如同上图所描述的,虽然world,window和device的坐标系等价,但是因为我们可以进行转换,所以他们的坐标系是可以改变的。
这里我来看一个例子,说明一下window和viewport具体的区别。
上图中,我设置了Painter画一条对角线,先设置widget窗口的大小(600,600),然后画一条线,这里没有设置window和viewport,于是这条线该是多长就多长。
接下来,我设置painter的window区域(200,200),然后无论我怎么拉伸窗口,对角线都是跟随窗口变化的,如下图。
然后我设置window区域(600,600)。这时候,和我们刚开始看到的图形就是一样的了,也就是说刚才设置了window以后,窗口window被放大到铺满整个widget了。
通过打印widget的大小,我们发现widget的大小依然是(600,600),也就是说window没有设置成(200,200),经查证,Qt这里是会自动转换坐标系的,所以我们需要按下图这样设置一下坐标转换不使能。setViewTransformEnabled(false);
接下来,我再设置viewport。Viewport表示设备的坐标系。如下图,可以看到,viewport大小是(100,100),而drawLine是(600,600),但是线条并没有600,而是被viewport限制了,Painter画线按照设备viewport-window的转换的某种线性关系画了。
END
注意事项
这里要注意视图区即物理设备绘图区,窗口是指逻辑绘图区
参考文献
C++ GUI Qt4编程(第二版)
- 1
事实上,调用event->ignore()也可以
- 2
被控制的空间都要安装事件过滤器
- 3
在调用processEvents时最好加上QeventLoop::ExcludeUserInputEvents参数以忽略鼠标和键盘事件,防止由于用户操作导致的不可知后果。
事实上,只有在使用qDebug()第二中输出方式时才需要包含头文件,只使用第一种时无需包含。
- 5
在Windows下,qDebug()将信息输出到调试器,而不是stdout。
- 6
qCritical将信息打印到stderr。
- 7
qFatal()函数将信息输出到stderr。
- 8
qCritical将信息打印到stderr。
注:当QlistWidget显示模式为QListView::IconMode时,图标是可拖动的
- 10
注:当QlistWidget显示模式为QListView::IconMode时,图标是可拖动的
- 11
附录18.1 列举了大部分的MIME类型
- 12
当你在Win10下开启“剪贴板历史记录”时,你在剪切板中储存的数据可以使用win+V键在剪切板历史记录中看到(只支持部分数据)。
- 13
当separatorsCollapsible设置为true时,菜单开始、菜单结束的分割线会被隐藏,连续的分割线仅显示一个。
- 14
在Win10 1903 + Qt 5.12.3 环境下测试时,扁平化与非扁平化表现形式相同。
- 15
简单的一组按钮互斥可以通过设置autoExclusive属性实现。
该函数必须保证至少一个按钮的Checkable属性为true。












