文章目录
1 概述
1.1 优点
- 跨平台,几乎支持所有的平台
- 接口设计良好,使用简单
- 一定程度上简化了内存回收机制
- 开发效率高,能够快速的构建应用程序。
- 社区氛围良好,市场份额上升
- 支持嵌入式开发
1.2 QT成功使用案例
- Linux桌面环境KDE
- WPS office软件
- Skype网络电话
- Google Earth
- VLC多媒体播放器
- VirtualBox
1.3 安装教程
从5.15之后了都是在线安装了。
1.3.1 在线安装流程
在线安装流程:
https://blog.csdn.net/Python_0011/article/details/131699443
1.3.2 离线安装流程
【选择组件】
2 创建工程
2.1 快捷键
2.1.1 常用快捷键
Go back: Alt + Left 返回,光标上一次到的那个位置,如从一个文本到了另一个中。
Go Forward: Alt + Right前进
2.1.2 修改快捷键
有时Qt Creator快捷键与系统中的快捷键冲突了,可以自定义或者修改原来的快捷键,步骤如下: 工具-》选项-》环境-》键盘-》,此处比如切换书签的快捷键,Ctrl+M显示红色,就说明冲突了,我们选中这一行,点击Record重新记录,再点击Apply和OK即可
2.2 proj文件
3 对象树
4 信号和槽
4.1 自定义信号和槽
- 定义信号
singals
后面的函数都可以作为信号,信号只有声明,没有实现。
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = nullptr);
signals:
void hungry();
void hungry(QString foodName);
};
- 定义槽
public slots
后面的函数都可以作为slot,slot需要有实现。
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = nullptr);
signals:
public slots:
void treat();
void treat(QString foodName);
};
void Student::treat()
{
qDebug() << "请老师吃饭";
}
void Student::treat(QString foodName) {
qDebug() << "请老师吃饭, 老师要吃" << foodName.toUtf8().data();
}
- 建立连接
调用connect函数建立连接。
void (Teacher::*hungryMethod)(QString) = &Teacher::hungry;
void (Student::*treatMethod)(QString) = &Student::treat;
connect(teacher, hungryMethod, student, treatMethod);
- 发出信号
void MainWindow::classOver() {
emit this->teacher->hungry("Noodles");
}
【自定义信号或者槽出现重载的情况的解决】
使用函数指针的方式来明确指向的函数的地址。
void (Teacher::*hungryMethod)(QString) = &Teacher::hungry;
void (Student::*treatMethod)(QString) = &Student::treat;
【一个小知识点:QString转为char*】
void Student::treat(QString foodName) {
qDebug() << "请老师吃饭, 老师要吃" << foodName.toUtf8().data();
}
4.1.1 信号连接信号
//信号连接信号
void (Teacher::*hungryMethodPure)(void) = &Teacher::hungry;
void (Student::*treatMethodPure)(void) = &Student::treat;
connect(teacher, hungryMethodPure, student, treatMethodPure);
connect(classOverButton, &QPushButton::clicked, teacher, hungryMethodPure);
4.1.2 一个信号连接多个槽函数
4.1.3 多个信号连接同一个槽函数
4.1.3 信号和槽函数的参数列表必须一一对应,但是信号的参数个数可以多于槽
不匹配的情况下会报如下错误:
4.1.4 断开信号
disconnect(classOverButton, &QPushButton::clicked, teacher, hungryMethodPure);
4.2 QT4版本的信号和槽
【优点】
参数直观
【缺点】
编译器不会检测参数类型
4.3 lambda表达式
4.3.1 mutable关键字
//mutable关键字表示可以修改值传递的变量,但是修改的是拷贝,并不是变量本身
QPushButton* myBtn = new QPushButton(this);
QPushButton* myBtn2 = new QPushButton(this);
myBtn2->move(100, 100);
int m = 10;
connect(myBtn, &QPushButton::clicked, this, [m]()mutable {m = 110; qDebug() << m;});
connect(myBtn2, &QPushButton::clicked, this, [=](){qDebug() << m;});
qDebug() << m;
4.3.2 返回值
void testReturnInLambda() {
int ret = []()->int{return 1000;}();
qDebug() << "ret = " << ret;
}
4.3.3 利用lambda表达式实现槽函数功能
/**
* @brief testOnButtonClick,利用lambda表达式实现槽函数功能
*
* @param mainWindow
*/
void testOnButtonClick(MainWindow* mainWindow) {
QPushButton* button = new QPushButton("Clock window", mainWindow);
button->move(100, 100);
mainWindow->connect(button, &QPushButton::clicked, mainWindow, [=](){
mainWindow->getStudent()->treat("Coco cola");
mainWindow->close();
});
}
4.4 小结
5 控件
5.1 窗口
5.1.1 菜单栏
最多只有一个
5.1.2 工具栏
可以有多个
5.1.3 状态栏
5.2 PushButton
5.3 对话框
5.3.1 模态对话框
不可以操作其他窗口了。
void showModeDialog(QWidget* parent) {
QDialog dialog(parent);
dialog.resize(200, 100);
dialog.exec();
}
5.3.2 非模态对话框
还可以操作其他窗口
void showNoneModeDialog(QWidget* parent) {
QDialog* dialog = new QDialog(parent);
dialog->resize(200, 100);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->show();
}
5.3.3 消息对话框
5.3.3.1 消息对话框是模态对话框
5.3.3.2 使用案例
void showMessageBox(QWidget* parent, int dialogType) {
switch (dialogType) {
case 1:
{
QMessageBox::critical(parent, "critial", "critial happened");
break;
}
case 2:
{
QMessageBox::information(parent, "information", "information happened");
break;
}
case 3:
{
QMessageBox::StandardButton result = QMessageBox::question(parent, "ques", "question happened", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (result == QMessageBox::Yes) {
qDebug() << "Yes";
} else {
qDebug() << "No";
}
break;
}
case 4:
{
QMessageBox::warning(parent, "warning", "warning happened");
break;
}
default:
QMessageBox::critical(parent, "critial", "critial happened");
break;
}
}
5.3.4 颜色对话框
void showColorDialog(QWidget* parent) {
QColor color = QColorDialog::getColor(QColor(255, 0, 0));
QString str = QString("[%1, %2, %3]").arg(color.red()).arg(color.green()).arg(color.blue());
qDebug() << str;
}
5.3.4 文件对话框
void showFileDialog(QWidget* parent) {
QString filePath = QFileDialog::getOpenFileName(parent, "Sk Open File", "C:\\Users\\imt2047\\Desktop", "(*.png)");
qDebug() << filePath;
}
5.3.4 字体对话框
void showFontDialog(QWidget* parent) {
bool flag;
QFont font = QFontDialog::getFont(&flag, QFont("Times New Roman", 14));
QString res = QString("Family:%1, Size:%2, Bold:%3, Italic:%4").arg(font.family()).arg(font.pointSize()).arg(font.bold())
.arg(font.italic());
qDebug() << res;
}
5.4 按钮组
5.4.1 Radio button
设置默认选中
ui->rBtnMan->setChecked(true);
5.4.2 Check box
5.5 ListWidget
void MainWindow::initListWidget() {
// QListWidgetItem* item = new QListWidgetItem("Hello hello, jin gou bei, jin goubei");
// item->setTextAlignment(Qt::AlignHCenter);
// ui->listWidget->addItem(item);
QStringList list;
list << "Hello hello, jin gou bei, jin goubei" << "Hello hello, jin gou bei, jin goubei"
<< "Hello hello, jin gou bei, jin goubei" << "Hello hello, jin gou bei, jin goubei";
ui->listWidget->addItems(list);
}
5.6 TableWidget
void MainWindow::initTableWidget() {
ui->tableWidget->setColumnCount(3);
ui->tableWidget->setRowCount(5);
ui->tableWidget->setHorizontalHeaderLabels(QStringList()<<"Name" << "Sex" << "Age");
QStringList nameList;
nameList << "yase" << "zhoayun" << "zhangfei" << "guanyu" << "huamulan";
QList<QString> sexList;
sexList<< "Male" << "Male" << "Male" << "Male" << "Female";
for (int i = 0; i < 5; i++) {
int columnIndex = 0;
ui->tableWidget->setItem(i, columnIndex++, new QTableWidgetItem(nameList[i]));
ui->tableWidget->setItem(i, columnIndex++, new QTableWidgetItem(sexList[i]));
ui->tableWidget->setItem(i, columnIndex++, new QTableWidgetItem(QString::number(i)));
}
}
5.7 ComboBox
void MainWindow::initComboBox() {
ui->comboBox->addItem("Benz");
ui->comboBox->addItem("BMW");
ui->comboBox->addItem("125 Moto");
connect(ui->selectCarPushButton, &QPushButton::clicked, this, [=](){
// ui->comboBox->setCurrentIndex(2);
ui->comboBox->setCurrentText("BMW");
});
}
5.8 封装自定义控件
5.8.1 添加一个新的Widget类
5.8.2 设计自定义的控件
拖拖拽拽
5.8.3 界面引用自定义的控件
5.8.4 实现自定义控件功能
spinbox和slider联动。
void SmallWidget::setValue(int value) {
ui->spinBox->setValue(value);
}
int SmallWidget::getValue() {
return ui->spinBox->value();
}
void SmallWidget::initBaseFunction() {
// SpinBox数值变化,Slider跟随滑动
void (QSpinBox::* valueChangedSignal)(int) = &QSpinBox::valueChanged;
connect(ui->spinBox, valueChangedSignal, ui->horizontalSlider, &QSlider::setValue);
// Slider滑动,SpinBox跟随数值变化
connect(ui->horizontalSlider, &QSlider::valueChanged, ui->spinBox, &QSpinBox::setValue);
}
5.9 TableView
5.9.1 model
import sys
from datetime import datetime
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data):
super(TableModel, self).__init__()
self._data = data
def data(self, index, role):
if role == Qt.DisplayRole:
# Get the raw value
value = self._data[index.row()][index.column()]
# Perform per-type checks and render accordingly.
if isinstance(value, datetime):
# Render time to YYY-MM-DD.
return value.strftime("%Y-%m-%d")
if isinstance(value, float):
# Render float to 2 dp
return "%.2f" % value
if isinstance(value, str):
# Render strings with quotes
return '"%s"' % value
# Default (anything not captured above: e.g. int)
return value
def rowCount(self, index):
# The length of the outer list.
return len(self._data)
def columnCount(self, index):
# The following takes the first sub-list, and returns
# the length (only works if all rows are an equal length)
return len(self._data[0])
5.9.2 view
import sys
from datetime import datetime
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from tablemodel import TableModel
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.table = QtWidgets.QTableView()
data = [
[4, 9, 2],
[1, -1, 'hello'],
[3.023, 5, -5],
[3, 3, datetime(2017, 10, 1)],
[7.555, 8, 9],
]
self.model = TableModel(data)
self.table.setModel(self.model)
self.setCentralWidget(self.table)
client
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from mainwindow import MainWindow
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
6 事件
6.1 鼠标Event
#ifndef MYLABEL_H
#define MYLABEL_H
#include <QLabel>
#include <QDebug>
#include <QMouseEvent>
class MyLabel : public QLabel
{
Q_OBJECT
public:
explicit MyLabel(QWidget* parent = 0);
/**
* @brief enterEvent: 覆写基类方法
* @param event
*/
void enterEvent(QEvent *event);
void leaveEvent(QEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void mouseDoubleClickEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
};
#endif // MYLABEL_H
#include "mylabel.h"
MyLabel::MyLabel(QWidget* parent):QLabel(parent)
{
// 开启鼠标追踪
setMouseTracking(true);
}
void MyLabel::enterEvent(QEvent *event) {
qDebug() << "mouse in in la.";
}
void MyLabel::leaveEvent(QEvent *event) {
qDebug() << "mouse out out la.";
}
void MyLabel::mousePressEvent(QMouseEvent *event) {
// 如果鼠标左键和右键按下,提示信息
QString string = QString("Mouse click happened");
if (event->button() == Qt::LeftButton) {
string = QString("Mouse left button click, x = %1, y = %2").arg(event->x()).arg(event->y());
} else if (event->button() == Qt::RightButton) {
string = QString("Mouse right button click, x = %1, y = %2").arg(event->x()).arg(event->y());
} else {
}
qDebug() << string;
}
void MyLabel::mouseReleaseEvent(QMouseEvent *event) {
QString string = QString("Mouse release, x = %1, y = %2").arg(event->x()).arg(event->y());
qDebug() << string;
}
void MyLabel::mouseDoubleClickEvent(QMouseEvent *event) {
QString string = QString("Mouse double click, x = %1, y = %2").arg(event->x()).arg(event->y());
qDebug() << string;
}
void MyLabel::mouseMoveEvent(QMouseEvent *event){
if (event->buttons() & Qt::LeftButton) {
QString string = QString("Mouse moving, x = %1, y = %2").arg(event->x()).arg(event->y());
qDebug() << string;
}
}
6.2 定时器
6.2.1 Event方式
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void timerEvent(QTimerEvent* event);
private:
Ui::MainWindow *ui;
int timerId1;
int timerId2;
void onTimer1Event(QTimerEvent* event);
void onTimer2Event(QTimerEvent* event);
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 启动定时器
timerId1 = startTimer(1000);
timerId2 = startTimer(2000);
}
void MainWindow::timerEvent(QTimerEvent* event) {
if (event ->timerId() == timerId1) {
onTimer1Event(event);
} else if (event->timerId() == timerId2) {
onTimer2Event(event);
}
}
void MainWindow::onTimer1Event(QTimerEvent* event) {
static int num = 1;
ui->label->setText(QString::number(num++));
}
void MainWindow::onTimer2Event(QTimerEvent* event) {
static int num = 1;
ui->label_2->setText(QString::number(num++));
}
6.2.1 QTimer对象
void startQTimer(MainWindow* parent) {
QTimer* timer = new QTimer(parent);
parent->connect(timer, &QTimer::timeout, parent, [=](){
static int num = 1;
parent->getUi()->label_3->setText(QString::number(num++));
});
timer->start(500);
parent->connect(parent->getUi()->pauseButton, &QPushButton::clicked, parent, [=](){
timer->stop();
});
parent->connect(parent->getUi()->resumeButton, &QPushButton::clicked, parent, [=](){
timer->start(500);
});
}
6.3 事件分发器
bool MyLabel::event(QEvent *e) {
// 拦截处理鼠标按下事件
if (e->type() == QEvent::MouseButtonPress) {
QMouseEvent* event = static_cast<QMouseEvent*>(e);
handleMousePressEvent(event, "event()");
return true;
}
// 交给父类处理
return QLabel::event(e);
}
6.4 事件过滤器
6.4.1 使用步骤
- 控件安装事件过滤器
- 覆写eventFilter函数。
6.4.2 代码示例
void MainWindow::initLabel() {
//1. install event filter
ui->label->installEventFilter(this);
}
/**
* 2. override eventFilter
*
* @brief MainWindow::eventFilter
* @param object
* @param e
* @return
*/
bool MainWindow::eventFilter(QObject* object, QEvent* e) {
if (object == ui->label) {
// 拦截处理鼠标按下事件
if (e->type() == QEvent::MouseButtonPress) {
QMouseEvent* event = static_cast<QMouseEvent*>(e);
handleMousePressEvent(event, "eventFilter()");
return true;
}
}
return QMainWindow::eventFilter(object, e);
}
7 资源文件
7.1 添加使用资源文件
-
将资源文件拷贝到目录中
-
创建资源文件(qrc文件)
-
编辑资源文件
添加前缀,添加文件。
-
使用资源文件
void MainWindow::initFileMenuItems() {
QAction* newAction = new QAction("New");
//使用QT资源: : + 前缀名 + 资源名
newAction->setIcon(QIcon(":/image/robot_hand.svg"));
this->fileMenuItems.append(newAction);
this->fileMenuItems.append(new QAction("Open"));
}
8 国际化
8.1 操作步骤
- 生成ts文件
- pro文件增加内容
TRANSLATIONS = ListTableWidgetTest_EN.ts \
ListTableWidgetTest_CN.ts
- 使用lupdate生成ts文件
- 查看生成的ts文件
生成的文件就在工程目录中。ts文件就是xml文件。
- 生成qm文件
使用linguist生成。
- 代码中加载qm文件,安装translator,retranslateUi
void MainWindow::on_selectLanguangeComboBox_activated(int index)
{
switch(index) {
// chinese
case 0:
translator->load(":/language/res/ListTableWidgetTest_CN.qm");
isChinese = true;
break;
// english
case 1:
translator->load(":/language/res/ListTableWidgetTest_EN.qm");
isChinese = false;
break;
default:
break;
}
qApp->installTranslator(translator);
ui->retranslateUi(this);
}
- 注意ComboBox
retranslateUi刷新UI的时候,会把组合框默认选择第一个。如果同组合框切换语言的时候,要处理下这个选择框。
解决办法就是记录全局变量,记录每一次选择的原因。 如果组合框因为刷新的原因发生了不一致,就根据全局变量重新恢复。
void MainWindow::changeEvent(QEvent *e) {
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
if (!isChinese && ui->comboBox->currentIndex() == 0) {
ui->selectLanguangeComboBox->setCurrentIndex(1);
}
break;
default:
break;
}
}
9 本文例程
https://download.csdn.net/download/kaikai_sk/88496589
https://download.csdn.net/download/kaikai_sk/88496592
https://download.csdn.net/download/kaikai_sk/88496597
10 参考资料
[1] https://www.bilibili.com/video/BV1g4411H78N/?p=11&spm_id_from=pageDriver&vd_source=f4dcb991bbc4da0932ef216329aefb60
[2] https://blog.csdn.net/weixin_51081223/article/details/121671615
[3] https://cloud.tencent.com/developer/article/2245901
[4] https://blog.csdn.net/ihmhm12345/article/details/127408975
[5] 创建PyQT项目: https://www.pythonguis.com/tutorials/first-steps-qt-creator/
[6] ui转换为py,并生成exe: https://www.cnblogs.com/linyfeng/p/11223707.html
[7] QT Linguist: https://blog.csdn.net/liang19890820/article/details/50274409
[8] QT 国际化:https://blog.csdn.net/liang19890820/article/details/50276673
[9] QT 国际化:https://www.bilibili.com/video/BV1yx411t7cX/?spm_id_from=333.337.search-card.all.click&vd_source=f4dcb991bbc4da0932ef216329aefb60
[10] Android事件分发机制:https://www.jianshu.com/p/38015afcdb58
[11] QT事件机制:https://blog.csdn.net/luolaihua2018/article/details/110797592
[12] QT事件机制:https://blog.csdn.net/tqs_1220/article/details/82563070
[13] QT事件机制:https://www.cnblogs.com/Braveliu/p/7417476.html
[14] 在Qt Creator中查看QT源码:https://blog.csdn.net/ihmhm12345/article/details/127408975
[15] PyQt TableView demo: https://www.pythonguis.com/tutorials/qtableview-modelviews-numpy-pandas/
[16] Qt信号和槽机制详解: https://c.biancheng.net/view/9414.html
[17] Qt一篇全面的信号和槽函数机制总结: https://zhuanlan.zhihu.com/p/603617075