网站首页 > 技术文章 正文
QT开发经验:
QT是一款跨平台的C++图形用户界面应用程序开发框架,适用于Windows、Linux、Mac等操作系统。QT开发经验丰富,可根据个人需求选择不同的开发方式。
在QT开发中,需要掌握的基础知识有QWidget、QPixmap、QGraphics等继承自QPaintDevice的概念。此外,可以通过使用QPainter类在QPaintDevice上进行绘制,这样的基础知识是开发过程中的必要知识。
1、QString类是我个人认为Qt所有类中的精华,封装的无可挑剔
内置了各种进制数据的转换,比如将数据转成10进制、16进制显示,或者将10进制、16进制数据转成字符串显示。这里很容易忽略的一点就是,很多人以为就是支持2进制、10进制、16进制之类的,其实不是的,里面实现了 2-36 之间的任意进制转换,可以自行翻阅源码查看实现。
char data[2];
data[0] = 0x10;
data[1] = 25;
//输出 2进制显示 "10000" "11001"
qDebug() << "2进制显示" << QString::number(data[0], 2) << QString::number(data[1], 2);
//输出 5进制显示 "31" "100"
qDebug() << "5进制显示" << QString::number(data[0], 5) << QString::number(data[1], 5);
//输出 10进制显示 "16" "25"
qDebug() << "10进制显示" << QString::number(data[0]) << QString::number(data[1]);
//输出 16进制显示 "10" "19"
qDebug() << "16进制显示" << QString::number(data[0], 16) << QString::number(data[1], 16);
2、QtSql模块封装了各种数据库操作
使得Qt操作各种数据库非常的简单,支持各种各样的数据库,最基础的ODBC方式也支持连接到各种数据库。有个很容易忽视的要点就是在连接sqlserver数据库的时候,你会发现第三方的数据库工具也没有配置数据库,但是可以连接成功,而在Qt中的常规数据库连接写法却不行,那是因为你代码写错了,要用另外一种写法。
//连接sqlite数据库
QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE");
//只需要指定数据库文件的绝对路径即可
database.setDatabaseName("d:/test.db");
//连接mysql数据库
QSqlDatabase database = QSqlDatabase::addDatabase("QMYSQL");
database.setDatabaseName("test");
database.setHostName("127.0.0.1");
database.setPort(3306);
database.setUserName("root");
database.setPassword("root");
//连接到sqlserver数据库
//方式一通过odbc数据源,前提是必须配置好数据源。
QSqlDatabase database = QSqlDatabase::addDatabase("QODBC");
database.setDatabaseName("数据源名称");
database.setUserName("sa");
database.setPassword("123456");
//方式二通过驱动字符串,无需配置数据源。设置数据库名称就带了主机地址端口和用户信息所有后面这些设置不需要,强烈建议推荐此方法。
QSqlDatabase database = QSqlDatabase::addDatabase("QODBC");
QStringList list;
list << QString("DRIVER={%1}").arg("SQL SERVER");
list << QString("SERVER=%1,%2").arg("127.0.0.1").arg(1433);
list << QString("DATABASE=%1").arg("test");
list << QString("UID=%1").arg("sa");
list << QString("PWD=%1").arg("123456");
database.setDatabaseName(list.join(";"));
//连接到postgresql数据库
QSqlDatabase database = QSqlDatabase::addDatabase("QPSQL");
database.setDatabaseName("test");
database.setHostName("127.0.0.1");
database.setPort(5432);
database.setUserName("postgres");
database.setPassword("123456");
//连接到oracle数据库
QSqlDatabase database = QSqlDatabase::addDatabase("QOCI");
database.setDatabaseName("test");
database.setHostName("127.0.0.1");
database.setPort(1521);
database.setUserName("system");
database.setPassword("123456");
//连接到人大金仓kingbase数据库(内核就是postgresql)
QSqlDatabase database = QSqlDatabase::addDatabase("QPSQL");
database.setDatabaseName("test");
database.setHostName("127.0.0.1");
database.setPort(54321);
database.setUserName("SYSTEM");
database.setPassword("123456");
//通过odbc数据源连接到各种数据库,前提是必须配置好数据源,只需要设置数据库名称为数据源的名称,填写用户名和密码就行,其他的主机地址和端口不需要。
QSqlDatabase database = QSqlDatabase::addDatabase("QODBC");
database.setDatabaseName("数据源名称");
database.setUserName("system");
database.setPassword("123456");
3、信号与槽
如果信号槽关联函数 connect(obj, SIGNAL(), this, SLOT()); 执行多次则会重复关联(意味着会执行多次),而取消信号槽关联函数 disconnect(obj, SIGNAL(), this, SLOT()); 只需要执行一次就可以将之前关联的(哪怕是重复关联过)全部清除。很多初学者会遇到为什么点一下居然执行多次的原因就在这里,很可能代码中写了 on_objName_clicked(); 这种Qt内置自动生成关联的槽函数,然后自己又在代码中调用 connect 绑定了一次,导致重复绑定。提个建议:其实Qt可以过滤下如果是完全一样的绑定则认为是一个而不是多个。
//为了保证永远只有一个关联可以在关联前面执行一次取消关联
disconnect(obj, SIGNAL(), this, SLOT());
connect(obj, SIGNAL(), this, SLOT());
//经过群里大佬提示,原来connect第五个参数填 UniqueConnection 就可以避免这个问题,按照官方文档说明这个参数会过滤重复的信号。
connect(obj, SIGNAL(), this, SLOT(), Qt::UniqueConnection);
4、智能指针QScopedPointer
通过对Qt自带Examples的源码研究你会发现,越往后的版本,越喜欢用智能指针QScopedPointer来定义对象,这样有个好处就是用的地方只管new就行,一直new下去,不用担心资源释放问题,智能指针会给你在合适的时机释放,相当于可以少写一行代码 xxx->deleteLater(); ,而且避免不必要的麻烦,不然很多地方你要判断 if (!xxx) 看下对象是否ok。
QWidget *widget;
//用的地方先new
widget = new QWidget;
//用完释放对象
widget->deleteLater();
//智能指针写法
QScopedPointer<QWidget> widget;
//只管new尽管new不用管释放
widget.reset(new QWidget);
5、控件布局函数setLayout
如果控件中存在布局,在调用setLayout重新设置布局的时候,会提示 QWidget::setLayout: Attempting to set QLayout ... 之类的信息,说是已经存在了布局,需要删除之前的布局才能重新设置布局,按道理Qt推荐的是调用 layout()->deleteLater() 方法去删除对象,更安全,但是在这里不起作用,你需要用 delete layout() 来删除,着实奇怪。
6、变量赋值与取值
在编写类中有时候需要对变量进行赋值和取值,这时候一般用 setxxx、getxxx 之类的函数进行处理,而且往往里面就一行代码,这时候你可能会思考为何不直接将变量改成public暴露出来使用,还可以省两个函数几行代码。其实用set get这样处理主要还是为了拓展性,比如后期如果需要对赋值进行过滤处理,或者该变量只允许读写中的一个,如果之前是直接使用的变量外,则使用的地方都要去修改规则,反而变得很糟糕。
7、线程处理
关于如何快速结束线程,调用terminate暴力结束容易出问题。一般来说我们都是采用标志位来结束线程,但是如果执行过程中的函数很耗时,或者在run中msleep休息的时间过久,容易导致要很长一段时间才能正确停止,此时可以考虑一个策略就是分割线程执行体,如果是函数体耗时可以在耗时的函数体中增加停止标志位的判断,使其快速跳出;如果是延时时间过久可以将延时时间拆分成多个小的时间轮片,每个小的休息间隔都判断停止标志位,这样也可以极大加快线程正常退出的速度而不用等待太久。
void Thread::run()
{
while (!stopped) {
doTask();
//下面这个延时太久导致退出很慢
//msleep(3000);
//特意每次做个小延时每次都去判断标志位等可以极大加快关闭速度
int count = 0;
while (!stopped) {
msleep(100);
count++;
//如果到了30次=30*100=3000毫秒也跳出
if (count == 30) {
break;
}
}
}
stopped = false;
}
void Thread::doTask()
{
while(1) {
if (stopped) {
return;
}
doTask1();
doTask2();
}
}
8、父类窗体
Qt中如果指定了同一个父类窗体,则控件都会覆盖在该父类窗体中,这就需要设置窗口小部件覆盖遮挡与层叠顺序。
//Qt对有共同父类窗体的控件优化到了极致,下面生成了1000个widget才新增不到3mb的内存。
for (int i = 0; i < 1000; ++i) {
QWidget *w = new QWidget(this);
w->setGeometry(0, 0, 100, 100);
w->show();
}
QWidget *w1, *w2, *w3;
//将w1控件移到最前面相当于在该父窗体中置顶
w1->raise();
//将w1控件移到最后面相当于在该父窗体中置底
w1->lower();
//将w1控件移到w2控件下面
w1->stackUnder(w2);
当我们关闭窗体的时候,按道理来说都会执行对应窗体的析构函数 ~MainWindow() 之类的,这是理想状态,当你的窗体还弹出了子窗体,就算你关闭了主窗体,会发现子窗体依然在,而且根本没有去析构主窗体,对应的子窗体也没有设置 setParent ,通常情况下,我们都是希望关闭了主窗体,对应子窗体自动关闭,这个时候怎么办呢?你需要重载 closeEvent 拿到关闭消息,主动去把子窗体释放。
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void closeEvent(QCloseEvent *);
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QLabel *lab;
};
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
lab = new QLabel;
lab->resize(400, 300);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::closeEvent(QCloseEvent *)
{
//先把子窗体释放
lab->deleteLater();
}
void MainWindow::on_pushButton_clicked()
{
lab->show();
}
9、鼠标键盘事件
关于Qt中 sendEvent 和 postEvent 主动模拟发送鼠标键盘事件的几点说明。
sendEvent是阻塞式,代码会立即执行,支持栈空间和堆空间事件对象的发送(局部对象和new分配的对象)。
postEvent是非阻塞式,会发送到事件队列中等待处理,只支持栈堆空间事件对象的发送(new分配的对象)。
new分配的事件对象被处理后,会由Qt内部自动摧毁,不用担心。
短时间内密集频繁的调用,推荐用postEvent,放入事件队列非常安全。否则用sendEvent很容易导致崩溃。
//下面这个会立即执行
QResizeEvent event(size(), size());
QApplication::sendEvent(this, &event);
//下面这个会立即执行
QResizeEvent *event = new QResizeEvent(size(), size());
QApplication::sendEvent(this, event);
//下面这个不会报错但是也不会执行因为事件对象是局部变量
QResizeEvent event(size(), size());
QApplication::postEvent(this, &event);
//下面的方式非常安全
QResizeEvent *event = new QResizeEvent(size(), size());
QApplication::postEvent(this, event);
除了上述基础知识和经验之外,开发者还需要熟悉Qt API、QML、QT设计师等工具,以便更好地开发和维护应用程序。
总的来说,QT开发是一项需要不断学习和实践的技术,我们需要不断提高自己的开发能力,才能开发出更加优质的应用程序。
猜你喜欢
- 2024-10-24 QT(17)- QNetworkAccessManager qnetworkinterface.allinterfaces
- 2024-10-24 Qt多线程的三种方法QThread qt多线程直接处理数据
- 2024-10-24 从零开始学Qt(86):TCP服务器端程序设计
- 2024-10-24 Qt Core学习日记——第九天QObjectData
- 2024-10-24 Qt智能指针--QSharedPointer qt智能指针.get和.data函数后计数会加吗
- 2024-10-24 QT(11)- QThread qt ui thread work thread
- 2024-10-24 Qt QVariant的用法 qt基本语法
- 2024-10-24 Qt5中QOverload的用法 qt5coredll
- 2024-10-24 Qt多线程编程之QThread qt中的多线程
- 2024-10-24 Qt QModbusReply类 qty是什么单位的缩写
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)