01:001-010
- 当编译发现大量错误的时候,从第一个看起,一个一个的解决,不要急着去看下一个错误,往往后面的错误都是由于前面的错误引起的,第一个解决后很可能都解决了。比如我们可能就写错了一行代码,编译提示几百个错误,你只要把这一行纠正了,其他错误也就没了。
- 定时器是个好东西,学会好使用它,有时候用QTimer::singleShot单次定时器和QMetaObject::invokeMethod可以解决意想不到的问题。比如在窗体初始化的时候加载一个耗时的操作,很容易卡主界面的显示,要在加载完以后才会显示界面,这就导致了体验很卡不友好的感觉,此时你可以将耗时的加载(有时候这些加载又必须在主线程,比如用QStackWidget堆栈窗体加载一些子窗体),延时或者异步进行加载,这样就会在界面显示后去执行,而不是卡住主界面。
1 //异步执行load函数
2 QMetaObject::invokeMethod(this, "load", Qt::QueuedConnection);
3 //延时10毫秒执行load函数
4 QTimer::singleShot(10, this, SLOT(load()));
- 默认QtCreator是单线程编译,可能设计之初考虑到尽量不过多占用系统资源,而现在的电脑都是多核心的,默认msvc编译器是多线程编译的不需要手动设置,而对于其他编译器,需要手动设置才行。
- 方法一:在每个项目的构建设置中(可以勾选一个 shadow build 的页面地方)的build步骤,make arguments增加一行 -j16 即可,此设置会保存在pro.user文件中,一旦删除就需要重新设置,不建议此方法;
- 方法二:在构建套件的环境中增加,工具->选项->构建套件(kits)->选中一个构建套件->environment->右侧change按钮->打开的输入框中填入 MAKEFLAGS=-j4 , 这样就可以不用每次设置多线程编译,只要是应用该构件套件的项目都会加上这个编译参数;
- 注意:-j后面接的是电脑的核心数,写多了不会有效果,要自己看下电脑的参数,或者填个-j4就行,毕竟现在电脑4核心应该是最基本的;
- 大概从2019年开始的新版本的QtCreator默认已经会根据电脑的核心自动设置多线程编译,比如识别到你的电脑是16核心的就会默认设置-j16参数进行编译;
- QtCreator8版本开始,选项配置菜单从工具移到了编辑菜单中,很多人有点不适应说怎么没有了,其实稍微鼠标划一划找一找就看到了。
-
如果你想顺利用QtCreator部署安卓程序,首先你要在 Android Studio 里面配置成功,编译一个程序能够在手机上或者模拟器中跑起来,把坑全部趟平。
-
很多时候找到Qt对应封装的方法后,记得多看看该函数的重载,多个参数的,你会发现不一样的世界,有时候会恍然大悟,原来Qt已经帮我们封装好了,比如QString、QColor的重载参数极其丰富,很多你做梦都想要的功能就在里面。
-
可以在pro文件中写上版本号、程序图标、产品名称、版权所有、文件说明等信息(Qt5才支持),其实在windows上就是qmake的时候会自动将此信息转换成rc文件。对于早期的Qt4版本你可以手动写rc文件实现。
1 #程序版本
2 VERSION = 2025.10.01
3 #程序图标
4 RC_ICONS = main.ico
5 #产品名称
6 QMAKE_TARGET_PRODUCT = quc
7 #版权所有
8 QMAKE_TARGET_COPYRIGHT = feiyangqingyun
9 #文件说明
10 QMAKE_TARGET_DESCRIPTION = QQ: 517216493 WX: feiyangqingyun
- 管理员运行程序,限定在MSVC编译器,在项目pro文件中增加如下代码。
1 QMAKE_LFLAGS += /MANIFESTUAC:"level='requireAdministrator' uiAccess='false'" #以管理员运行
2 QMAKE_LFLAGS += /SUBSYSTEM:WINDOWS,"5.01" #VS2013 在XP运行
- 运行文件附带调试输出窗口,这个非常有用,很多时候当我们发布程序阶段,我们会遇到程序双击无法运行也不报错提示(开发机器上一切正常),都不知道发生了什么,甚至任务管理器可以看到运行了但是没有界面弹出来,此时就需要在项目的pro文件中加上一行CONFIG += console,带界面的程序也会自动弹出调试窗口打印输出信息,方便找问题,一般没法正常运行的程序都会打印一些提示信息缺啥之类的。
1 TEMPLATE = app
2 MOC_DIR = temp/moc
3 RCC_DIR = temp/rcc
4 UI_DIR = temp/ui
5 OBJECTS_DIR = temp/obj
6 #就是下面这行用来设置运行文件附带调试输出窗口
7 CONFIG += console
-
绘制平铺背景QPainter::drawTiledPixmap,绘制圆角矩形QPainter::drawRoundedRect(),而不是QPainter::drawRoundRect(),这两个函数非常容易搞混。
-
指定控件移除旧的样式。
1 //移除原有样式
2 style()->unpolish(ui->btn);
3 //必须要有下面这行不然还是不会卸载
4 ui->btn->setStyleSheet("");
5 //重新设置新的该控件的样式。
6 style()->polish(ui->btn);
02:011-020
- 获取类的属性和方法
1 //拿到控件元对象
2 const QMetaObject *metaObject = widget->metaObject();
3
4 //所有属性的数量
5 int propertyCount = metaObject->propertyCount();
6 //propertyOffset是自定义的属性开始的位置
7 int propertyOffset = metaObject->propertyOffset();
8 //循环取出控件的自定义属性, int i = 0 表示所有属性
9 for (int i = propertyOffset; i < propertyCount; ++i) {
10 QMetaProperty metaProperty = metaObject->property(i);
11 const char *name = metaProperty.name();
12 const char *type = metaProperty.typeName();
13 QVariant value = widget->property(name);
14 qDebug() << name << type << value;
15 }
16
17 //所有方法的数量
18 int methodCount = metaObject->methodCount();
19 //methodOffset是自定义的方法开始的位置
20 int methodOffset = metaObject->methodOffset();
21 //循环取出控件的自定义方法, int i = 0 表示所有方法
22 for (int i = methodOffset; i < methodCount; ++i) {
23 QMetaMethod metaMethod = metaObject->method(i);
24 const char *name = metaMethod.name();
25 const char *type = metaMethod.typeName();
26 qDebug() << name << type;
27 }
- Qt内置图标封装在QStyle中,大概七十多个图标,可以直接拿来用。
1 SP_TitleBarMenuButton,
2 SP_TitleBarMinButton,
3 SP_TitleBarMaxButton,
4 SP_TitleBarCloseButton,
5 SP_MessageBoxInformation,
6 SP_MessageBoxWarning,
7 SP_MessageBoxCritical,
8 SP_MessageBoxQuestion,
9 ...
10 //下面这样取出来使用就行
11 QPixmap pixmap = this->style()->standardPixmap(QStyle::SP_TitleBarMenuButton);
12 ui->label->setPixmap(pixmap);
- 根据操作系统位数判断加载
1 win32 {
2 contains(DEFINES, WIN64) {
3 DESTDIR = $$PWD/../bin64
4 } else {
5 DESTDIR = $$PWD/../bin32
6 }
7 }
-
Qt5增强了很多安全性验证,如果出现setGeometry: Unable to set geometry,请将该控件的可见移到加入布局之后。
-
可以将控件A添加到布局,然后控件B设置该布局,这种灵活性提高了控件的组合度,比如可以在文本框左侧右侧增加一个搜索按钮,按钮设置图标即可。
1 QPushButton *btn = new QPushButton;
2 btn->resize(30, ui->lineEdit->height());
3 QHBoxLayout *layout = new QHBoxLayout(ui->lineEdit);
4 layout->setMargin(0);
5 layout->addStretch();
6 layout->addWidget(btn);
-
对QLCDNumber控件设置样式,需要将QLCDNumber的segmentstyle设置为flat,不然你会发现没效果。
-
巧妙的使用 findChildren 可以查找该控件下的所有子控件。 findChild 为查找单个。
1 //查找指定类名objectName的控件
2 QList<QWidget *> widgets = fatherWidget.findChildren<QWidget *>("widgetname");
3 //查找所有QPushButton
4 QList<QPushButton *> allPButtons = fatherWidget.findChildren<QPushButton *>();
5 //查找一级子控件,不然会一直遍历所有子控件
6 QList<QPushButton *> childButtons = fatherWidget.findChildren<QPushButton *>(QString(), Qt::FindDirectChildrenOnly);
- 巧妙的使用inherits判断是否属于某种类。
1 QTimer *timer = new QTimer; // QTimer inherits QObject
2 timer->inherits("QTimer"); // returns true
3 timer->inherits("QObject"); // returns true
4 timer->inherits("QAbstractButton"); // returns false
-
使用弱属性机制,可以存储临时的值用于传递判断。可以通过widget->dynamicPropertyNames()列出所有弱属性名称,然后通过widget->property("name")取出对应的弱属性的值。
-
在开发时, 无论是出于维护的便捷性, 还是节省内存资源的考虑, 都应该有一个 qss 文件来存放所有的样式表, 而不应该将 setStyleSheet 写的到处都是。如果是初学阶段或者测试阶段可以直接UI上右键设置样式表,正式项目还是建议统一到一个qss样式表文件比较好,统一管理。
03:021-030【XSY】
-
如果出现Z-order assignment: is not a valid widget.错误提示,用记事本打开对应的ui文件,找到为空的地方,删除即可。
-
善于利用QComboBox的addItem的第二个参数设置用户数据,可以实现很多效果,使用itemData取出来。特别注意的是第二个参数是QVariant类型,这就不要太灵活了,意味着可以附带万能的数据比如结构体,这样就可以带一堆数据了,而不是一个数据。比如下拉框选择学号,对应元素可以附带该学生的姓名、班级、成绩等。很多人以为只能附带QString、int之类的数据,因为通常的用法也是那两种。
1 QStringList listVideoOpenInterval, listVideoOpenIntervalx;
2 listVideoOpenInterval << "0.0 秒" << "0.1 秒" << "0.3 秒" << "0.5 秒" << "1.0 秒" << "2.0 秒";
3 listVideoOpenIntervalx << "0" << "100" << "300" << "500" << "1000" << "2000";
4 for (int i = 0; i < listVideoOpenInterval.count(); ++i) {
5 ui->cboxVideoOpenInterval->addItem(listVideoOpenInterval.at(i), listVideoOpenIntervalx.at(i));
6 }
7 //取出对应的值
8 int indexVideoOpenInterval = ui->cboxVideoOpenInterval->currentIndex();
9 indexVideoOpenInterval = ui->cboxVideoOpenInterval->itemData(indexVideoOpenInterval).toInt();
-
如果用了webengine模块,发布程序的时候带上QtWebEngineProcess.exe、translations文件夹、resources文件夹,不然无法正常运行。
-
在MFC程序或者VB/C#等窗体程序中,每个控件都有一个句柄,而且用句柄工具移过去会自动识别,但是在Qt程序中默认Qt是一个窗体一个句柄,如果要让每个控件都拥有独立的句柄,在main函数中要做如下设置。
1 int main(int argc, char *argv[])
2 {
3 QApplication a(argc, argv);
4 a.setAttribute(Qt::AA_NativeWindows);
5 }
- Qt编写的Android程序防止程序被关闭。
1 #if defined(Q_OS_ANDROID)
2 QAndroidService a(argc, argv);
3 return a.exec()
4 #else
5 QApplication a(argc, argv);
6 return a.exec();
7 #endif
- 可以对整体的指示器设置样式,而不需要单独对每个控件的指示器设置,
1 *::down-arrow{}
2 *::menu-indicator{}
3 *::up-arrow:disabled{}
4 *::up-arrow:off{}
- 可以指定位置设置背景图片。
1 QMainWindow > .QWidget {
2 background-color: gainsboro;
3 background-image: url(:/images/xxoo.png);
4 background-position: top right;
5 background-repeat: no-repeat
6 }
- 嵌入式linux运行Qt程序
1 //Qt4写法
2 ./HelloQt -qws &
3
4 //Qt5写法 xcb 可以改成 linuxfb eglfs vnc wayland 等,有哪个就用哪个挨个测试
5 ./HelloQt --platform xcb
6 ./HelloQt --platform linuxfb
7 ./HelloQt --platform wayland
-
如果发现QtCreator中的构建套件不正常了或者坏了(比如不能正确识别环境中的qmake或者编译器、打开项目不能正常生成影子构建目录),请找到两个目录(C:\Users\Administrator\AppData\Local\QtProject、C:\Users\Administrator\AppData\Roaming\QtProject)删除即可,删除后重新打开QtCreator进行构建套件的配置就行。
-
QMediaPlayer是个壳(也可以叫框架),依赖本地解码器,视频这块默认基本上就播放个MP4甚至连MP4都不能播放,如果要支持其他格式需要下载k-lite或者LAV Filters安装即可(k-lite或者LAV Filters是指windows上的,其他系统上自行搜索,貌似嵌入式linux上依赖GStreamer(sudo apt-get install gstreamer1.0-libav ubuntu-restricted-extras),并未完整验证,报错提示 Your GStreamer installation is missing a plug-in,需要命令安装 sudo apt-get install ubuntu-restricted-extras)。如果需要做功能强劲的播放器,初学者建议用vlc、mpv,终极万能大法用ffmpeg(解码出来的视频可以用QOpenGLWidget走GPU绘制或者转成QImage绘制,音频数据可以用QAudioOutput播放)。
04:031-040 【XSY2024/14】
- 判断编译器类型、编译器版本、操作系统。
1 //pro中判断编译器版本
2 greaterThan(MSC_VER, 1900) {
3 }
4
5 //GCC编译器
6 #ifdef __GNUC__
7 #if __GNUC__ >= 3 // GCC3.0 以上
8
9 //MSVC编译器
10 #ifdef _MSC_VER
11 #if _MSC_VER >=1000 // VC++4.0 以上
12 #if _MSC_VER >=1100 // VC++5.0 以上
13 #if _MSC_VER >=1200 // VC++6.0 以上
14 #if _MSC_VER >=1300 // VC2003 以上
15 #if _MSC_VER >=1400 // VC2005 以上
16 #if _MSC_VER >=1500 // VC2008 以上
17 #if _MSC_VER >=1600 // VC2010 以上
18 #if _MSC_VER >=1700 // VC2012 以上
19 #if _MSC_VER >=1800 // VC2013 以上
20 #if _MSC_VER >=1900 // VC2015 以上
21
22 //Visual Studio版本与MSVC版本号的对应关系
23 MSC 1.0 _MSC_VER == 100
24 MSC 2.0 _MSC_VER == 200
25 MSC 3.0 _MSC_VER == 300
26 MSC 4.0 _MSC_VER == 400
27 MSC 5.0 _MSC_VER == 500
28 MSC 6.0 _MSC_VER == 600
29 MSC 7.0 _MSC_VER == 700
30 MSVC++ 1.0 _MSC_VER == 800
31 MSVC++ 2.0 _MSC_VER == 900
32 MSVC++ 4.0 _MSC_VER == 1000 (Developer Studio 4.0)
33 MSVC++ 4.2 _MSC_VER == 1020 (Developer Studio 4.2)
34 MSVC++ 5.0 _MSC_VER == 1100 (Visual Studio 97 version 5.0)
35 MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0 version 6.0)
36 MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002 version 7.0)
37 MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003 version 7.1)
38 MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005 version 8.0)
39 MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008 version 9.0)
40 MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010 version 10.0)
41 MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012 version 11.0)
42 MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013 version 12.0)
43 MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015 version 14.0)
44 MSVC++ 14.1 _MSC_VER == 1910 (Visual Studio 2017 version 15.0)
45 MSVC++ 14.11 _MSC_VER == 1911 (Visual Studio 2017 version 15.3)
46 MSVC++ 14.12 _MSC_VER == 1912 (Visual Studio 2017 version 15.5)
47 MSVC++ 14.13 _MSC_VER == 1913 (Visual Studio 2017 version 15.6)
48 MSVC++ 14.14 _MSC_VER == 1914 (Visual Studio 2017 version 15.7)
49 MSVC++ 14.15 _MSC_VER == 1915 (Visual Studio 2017 version 15.8)
50 MSVC++ 14.16 _MSC_VER == 1916 (Visual Studio 2017 version 15.9)
51 MSVC++ 14.2 _MSC_VER == 1920 (Visual Studio 2019 Version 16.0)
52 MSVC++ 14.21 _MSC_VER == 1921 (Visual Studio 2019 Version 16.1)
53 MSVC++ 14.22 _MSC_VER == 1922 (Visual Studio 2019 Version 16.2)
54 MSVC++ 14.30 _MSC_VER == 1930 (Visual Studio 2022 Version 17.0)
55 MSVC++ 14.31 _MSC_VER == 1931 (Visual Studio 2022 Version 17.1)
56 MSVC++ 14.32 _MSC_VER == 1932 (Visual Studio 2022 Version 17.2)
57
58 //Borland C++
59 #ifdef __BORLANDC__
60
61 //Cygwin
62 #ifdef __CYGWIN__
63 #ifdef __CYGWIN32__
64
65 //mingw
66 #ifdef __MINGW32__
67
68 //windows
69 #ifdef _WIN32 //32bit
70 #ifdef _WIN64 //64bit
71 #ifdef _WINDOWS //图形界面程序
72 #ifdef _CONSOLE //控制台程序
73
74 //Windows(95/98/Me/NT/2000/XP/Vista)和Windows CE都定义了
75 #if (WINVER >= 0x030a) // Windows 3.1以上
76 #if (WINVER >= 0x0400) // Windows 95/NT4.0以上
77 #if (WINVER >= 0x0410) // Windows 98以上
78 #if (WINVER >= 0x0500) // Windows Me/2000以上
79 #if (WINVER >= 0x0501) // Windows XP以上
80 #if (WINVER >= 0x0600) // Windows Vista以上
81
82 //_WIN32_WINNT 内核版本
83 #if (_WIN32_WINNT >= 0x0500) // Windows 2000以上
84 #if (_WIN32_WINNT >= 0x0501) // Windows XP以上
85 #if (_WIN32_WINNT >= 0x0600) // Windows Vista以上
- 在pro中判断Qt版本及构建套件位数
1 #打印版本信息
2 message(qt version: $$QT_VERSION)
3 #判断当前qt版本号
4 QT_VERSION = $$[QT_VERSION]
5 QT_VERSION = $$split(QT_VERSION, ".")
6 QT_VER_MAJ = $$member(QT_VERSION, 0)
7 QT_VER_MIN = $$member(QT_VERSION, 1)
8 #下面是表示 Qt5.5及以上版本
9 greaterThan(QT_VER_MAJ, 4) {
10 greaterThan(QT_VER_MIN, 4) {
11 #自己根据需要做一些处理
12 }}
13
14 #QT_ARCH是Qt5新增的,在Qt4上没效果
15 #打印当前Qt构建套件的信息
16 message($$QT_ARCH)
17 #表示arm平台构建套件
18 contains(QT_ARCH, arm) {}
19 #表示32位的构建套件
20 contains(QT_ARCH, i386) {}
21 #表示64位的构建套件
22 contains(QT_ARCH, x86_64) {}
23
24 #其实Qt内置了主版本号和子版本号变量
25 #判断当前qt版本号
26 message($$QT_ARCH : $$QT_VERSION -> $$QT_MAJOR_VERSION . $$QT_MINOR_VERSION)
27
28 #下面的含义是如果版本 < 4.8
29 lessThan(QT_MAJOR_VERSION, 5) {
30 lessThan(QT_MINOR_VERSION, 8) {
31 #这里放要做的处理
32 }}
33
34 #下面的含义是如果版本 < 5.12.0
35 REQ_QT_MAJOR = 5
36 REQ_QT_MINOR = 12
37 REQ_QT_PATCH = 0
38 lessThan(QT_MAJOR_VERSION, $$REQ_QT_MAJOR)|lessThan(QT_MINOR_VERSION, $$REQ_QT_MINOR)|lessThan(QT_MINOR_VERSION, $$REQ_QT_PATCH) {
39 #这里放要做的处理
40 }
41
42 #下面的含义是如果版本 >= 5.5
43 greaterThan(QT_MAJOR_VERSION, 4) {
44 greaterThan(QT_MINOR_VERSION, 4) {
45 #这里放要做的处理
46 }}
47
48 //代码中判断版本不要太简单
49 #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
50 //这里放要做的处理
51 #endif
52
53 //下面表示 >= 5.0.0
54 #if QT_VERSION >= 0x050000
55 ...
56 #endif
57
58 //下面表示 < 5.12.10
59 #if QT_VERSION < 0x050C0A
60 ...
61 #endif
- Qt最小化后恢复界面可能会出现假死冻结现象,加上代码
1 void showEvent(QShowEvent *e)
2 {
3 setAttribute(Qt::WA_Mapped);
4 QWidget::showEvent(e);
5 }
-
获取标题栏高度:style()->pixelMetric(QStyle::PM_TitleBarHeight); PM_TitleBarHeight点进去你会发现新大陆,有一堆玩意在里面。
-
设置高分屏属性以便支持2K4K等高分辨率,尤其是手机app。必须写在main函数的QApplication a(argc, argv);的前面。
1 #if (QT_VERSION >= QT_VERSION_CHECK(5,6,0))
2 QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
3 #endif
4 QApplication a(argc, argv);
- 如果运行程序出现 Fault tolerant heap shim applied to current process. This is usually due to previous crashes. 错误。
- 第一步:输入命令 regedit 打开注册表;
- 第二步:找到节点 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers\;
- 第三步:选中Layers键值,从右侧列表中删除自己的那个程序路径即可。
-
Qt内置了QFormLayout表单布局用于自动生成标签+输入框的组合的表单界面,设置布局用的很少,一般用的最多的是横向布局、垂直布局、表格布局。
-
qml播放视频在linux需要安装 sudo apt-get install libpulse-dev。
-
可以直接继承QSqlQueryModel实现自定义的QueryModel,比如某一列字体颜色,占位符,其他样式等,重写QVariant CustomSqlModel::data(const QModelIndex &index, int role) const。
-
Qt5以后提供了类QScroller直接将控件滚动。
1 //禁用横向滚动条
2 ui->listWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
3 //禁用纵向滚动条
4 ui->listWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
5 //设置横向按照像素值为单位滚动
6 ui->listWidget->setHorizontalScrollMode(QListWidget::ScrollPerPixel);
7 //设置纵向按照像素值为单位滚动
8 ui->listWidget->setVerticalScrollMode(QListWidget::ScrollPerPixel);
9 //设置滚动对象以及滚动方式为鼠标左键拉动滚动
10 QScroller::grabGesture(ui->listWidget, QScroller::LeftMouseButtonGesture);
11 //还有个QScrollerProperties可以设置滚动的一些参数
05:041-050
- 如果使用sqlite数据库不想产生数据库文件,可以创建内存数据库。
1 QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
2 db.setDatabaseName(":memory:");
-
清空数据表并重置自增ID,sql = truncate table table_name。
-
QtChart模块从Qt5.7开始自带,最低编译要求Qt5.4。在安装的时候记得勾选,默认不勾选。使用该模块需要引入命名空间。
1 #include <QChartView>
2 QT_CHARTS_USE_NAMESPACE
3 class CustomChart : public QChartView
-
QPushButton左对齐文字,需要设置样式表QPushButton{text-align:left;}
-
QLabel有三种设置文本的方法,掌握好Qt的属性系统,举一反三,可以做出很多效果。
1 //常规办法
2 ui->label->setText("hello");
3 //取巧办法
4 ui->label->setProperty("text", "hello");
5 //属性大法
6 ui->label->setStyleSheet("qproperty-text:hello;");
- 巧妙的用QEventLoop开启事件循环,可以使得很多同步获取返回结果而不阻塞界面。查看源码得知,原来QEventLoop内部新建了线程执行。
1 QEventLoop loop;
2 connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
3 loop.exec();
- Qt中也支持多种预定义变量组合判断,去掉生成空的debug和release目录,在pro文件中加一行 CONFIG -= debug_and_release。
1 #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
2 #endif
3
4 #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
5 #endif
6
7 #ifdef Q_OS_WIN
8 //windows系统
9 #else
10 //非windows系统
11 #endif
12
13 //下面写法编译会报错
14 #ifdef Q_OS_WIN
15 #elif Q_OS_LINUX
16 #endif
17
18 //正确写法
19 #if defined(Q_OS_WIN)
20 #elif defined(Q_OS_LINUX)
21 #endif
-
新版的Qtcreator增强了语法检查,会弹出很多警告提示等,可以在插件列表中关闭clang打头的几个即可,Help》About Plugins。也可以设置代码检查级别,Tools》Options 》C++ 》Code Model。
-
QSqlTableModel的rowCount方法,默认最大返回256,如果超过256,可以将表格拉到底部,会自动加载剩余的,每次最大加载256条数据,如果需要打印或者导出数据,记得最好采用sql语句去查询,而不是使用QSqlTableModel的rowCount方法。不然永远最大只会导出256条数据。 如果数据量很小,也可以采用如下方法:
1 //主动加载所有数据,不然获取到的行数<=256
2 while(model->canFetchMore()) {
3 model->fetchMore();
4 }
- 如果需要指定无边框窗体,但是又需要保留操作系统的边框特性,比如自由拉伸边框,可以使用 setWindowFlags(Qt::CustomizeWindowHint),这样会保留一个系统白边框。
06:051-060
- 在某些http post数据的时候,如果采用的是&字符串连接的数据发送,中文解析乱码的话,需要将中文进行URL转码。
1 QString content = "测试中文";
2 QString note = content.toUtf8().toPercentEncoding();
-
Qt默认不支持大资源文件,比如添加了字体文件,需要pro文件开启。 CONFIG += resources_big
-
Qt中继承QWidget之后,样式表不起作用,解决办法有三个。强烈推荐方法一。
- 方法一:设置属性 this->setAttribute(Qt::WA_StyledBackground, true);
- 方法二:改成继承QFrame,因为QFrame自带paintEvent函数已做了实现,在使用样式表时会进行解析和绘制。
- 方法三:重新实现QWidget的paintEvent函数时,使用QStylePainter绘制。
1 void Widget::paintEvent(QPaintEvent *)
2 {
3 QStyleOption option;
4 option.initFrom(this);
5 QPainter painter(this);
6 style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this);
7 }
-
有时候在界面上加了弹簧,需要动态改变弹簧对应的拉伸策略,对应方法为changeSize,很多人会选择使用set开头去找,找不到的。
-
在使用QFile的过程中,不建议频繁的打开文件写入然后再关闭文件,比如间隔5ms输出日志,IO性能瓶颈很大,这种情况建议先打开文件不要关闭,等待合适的时机比如析构函数中或者日期变了需要重新变换日志文件的时候关闭文件。不然短时间内大量的打开关闭文件会很卡,文件越大越卡。
-
在很多网络应用程序,需要自定义心跳包来保持连接,不然断电或者非法关闭程序,对方不能立即识别或者要很久(一般至少要30s)才能识别到,需要进行超时检测,但是有些程序没有提供心跳协议,此时需要启用系统层的保活程序,此方法适用于TCP连接。
1 int fd = tcpSocket->socketDescriptor();
2 int keepAlive = 1; //开启keepalive属性,缺省值:0(关闭)
3 int keepIdle = 5; //如果在5秒内没有任何数据交互,则进行探测,缺省值:7200(s)
4 int keepInterval = 2; //探测时发探测包的时间间隔为2秒,缺省值:75(s)
5 int keepCount = 2; //探测重试的次数,全部超时则认定连接失效,缺省值:9(次)
6 setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
7 setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, (void *)&keepIdle, sizeof(keepIdle));
8 setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
9 setsockopt(fd, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));
-
如果程序打包好以后弹出提示 This application failed to start because it could not find or load the Qt platform plugin 一般都是因为platforms插件目录未打包或者打包错了的原因导致的。
-
非常不建议tr中包含中文,尽管现在的新版Qt支持中文到其他语言的翻译,但是很不规范,也不知道TMD是谁教的(后面发现我在刚学Qt的时候也发布了一些demo到网上也是tr包含中文的,当时就狠狠的打了自己一巴掌),tr的本意是包含英文,然后翻译到其他语言比如中文,现在大量的初学者滥用tr,如果没有翻译的需求,禁用tr,tr需要开销的,Qt默认会认为他需要翻译,会额外进行特殊处理。
-
很多人Qt和Qt Creator傻傻分不清楚,经常问Qt什么版本结果发一个Qt Creator的版本过来,Qt Creator是使用Qt编写的集成开发环境IDE,和宇宙第一的Visual Studio一样,他可以是msvc编译器的(windows对应的Qt集成安装环境中自带的Qt Cerator是msvc的),也可以是mingw编译的,还可以是gcc的。如果是自定义控件插件,需要集成到Qt Creator中,必须保证该插件的动态库文件(dll或者so等文件)对应的编译器和Qt版本以及位数和Qt Creator的版本完全一致才行,否则基本不大可能集成进去。特别注意的是Qt集成环境安装包中的Qt版本和Qt Creator版本未必完全一致,必须擦亮眼睛看清楚,有些是完全一致的。由于新版的Qt要求在线安装,而且在线安装选择器中Qt Creator的版本无法选择,新版的Qt Creator用的是Qt6编译的,所以就出现了win7系统不支持的情况,推荐用win10或者win11系统做开发环境。你可以在高版本的Qt Creator中做开发,选择支持win7的套件版本比如5.15或者选择支持xp的套件版本5.6即可,发布后依然可以正常在低版本的系统运行。
-
超过两处相同处理的代码,建议单独写成函数。代码尽量规范精简,比如 if(a == 123) 要写成 if (123 == a),值在前面,再比如 if (ok == true) 要写成 if (ok),if (ok == false) 要写成 if (!ok)等。
07:061-070
-
很多人问Qt嵌入式平台用哪个好,这里统一回答(当前时间节点2018年):imx6+335x比较稳定,性能高就用RK3288 RK3399,便宜的话就用全志H3,玩一玩可以用树莓派香橙派。
-
对于大段的注释代码,建议用 #if 0 #endif 将代码块包含起来,而不是将该段代码选中然后全部双斜杠注释,下次要打开这段代码的话,又需要重新选中一次取消,如果采用的是 #if 0则只要把0改成1即可,开发效率提升很多。
-
Qt打包发布,有很多办法,Qt5以后提供了打包工具windeployqt(linux上为linuxdeployqt,mac上为macdeployqt)可以很方便的将应用程序打包,使用下来发现也不是万能的,有时候会多打包一些没有依赖的文件,有时候又会忘记打包一些插件尤其是用了qml的情况下,而且不能识别第三方库,比如程序依赖ffmpeg,则对应的库需要自行拷贝,终极大法就是将你的可执行文件复制到Qt安装目录下的bin目录,然后整个一起打包,挨个删除不大可能依赖的组件,直到删到正常运行为止。
-
Qt中的动画,底层用的是QElapsedTimer定时器来完成处理,比如产生一些指定规则算法的数据,然后对属性进行处理。
-
在绘制无背景颜色只有边框颜色的圆形时候,可以用绘制360度的圆弧替代,效果完全一致。
1 QRect rect(-radius, -radius, radius * 2, radius * 2);
2 //以下两种方法二选一,其实绘制360度的圆弧=绘制无背景的圆形
3 painter->drawArc(rect, 0, 360 * 16);
4 painter->drawEllipse(rect);
-
不要把d指针看的很玄乎,其实就是在类的实现文件定义了一个私有类,用来存放局部变量,个人建议在做一些小项目时,没有太大必要引入这种机制,会降低代码可读性,增加复杂性,新手接受项目后会看的很懵逼。
-
很多人在绘制的时候,设置画笔以为就只可以设置个单调的颜色,其实QPen还可以设置brush,这样灵活性就提高不知道多少倍,比如设置QPen的brush以后,可以使用各种渐变,比如绘制渐变颜色的进度条和文字等,而不再是单调的一种颜色。
-
很多控件都带有viewport,比如QTextEdit/QTableWidget/QScrollArea,有时候对这些控件直接处理的时候发现不起作用,需要对其viewport()设置才行,比如设置滚动条区域背景透明,需要使用scrollArea->viewport()->setStyleSheet("background-color:transparent;");而不是scrollArea->setStyleSheet("QScrollArea{background-color:transparent;}");
-
有时候设置了鼠标跟踪setMouseTracking为真,如果该窗体上面还有其他控件,当鼠标移到其他控件上面的时候,父类的鼠标移动事件MouseMove识别不到了,此时需要用到HoverMove事件,需要先设置 setAttribute(Qt::WA_Hover, true);
-
Qt封装的QDateTime日期时间类非常强大,可以字符串和日期时间相互转换,也可以毫秒数和日期时间相互转换,还可以1970经过的秒数和日期时间相互转换等。
1 QDateTime dateTime;
2 QString dateTime_str = dateTime.currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
3 //从字符串转换为毫秒(需完整的年月日时分秒)
4 datetime.fromString("2011-09-10 12:07:50:541", "yyyy-MM-dd hh:mm:ss:zzz").toMSecsSinceEpoch();
5 //从字符串转换为秒(需完整的年月日时分秒)
6 datetime.fromString("2011-09-10 12:07:50:541", "yyyy-MM-dd hh:mm:ss:zzz").toTime_t();
7 //从毫秒转换到年月日时分秒
8 datetime.fromMSecsSinceEpoch(1315193829218).toString("yyyy-MM-dd hh:mm:ss:zzz");
9 //从秒转换到年月日时分秒(若有zzz,则为000)
10 datetime.fromTime_t(1315193829).toString("yyyy-MM-dd hh:mm:ss[:zzz]");
08:071-080 【===========2024/1/5】
-
在我们使用QList、QStringList、QByteArray等链表或者数组的过程中,如果只需要取值,而不是赋值,强烈建议使用 at() 取值而不是 [] 操作符,在官方书籍《C++ GUI Qt 4编程(第二版)》的书中有特别的强调说明,此教材的原作者据说是Qt开发的核心人员编写的,所以还是比较权威,至于使用 at() 与使用 [] 操作符速度效率的比较,网上也有网友做过此类对比。原文在书的212页,这样描述的:Qt对所有的容器和许多其他类都使用隐含共享,隐含共享是Qt对不希望修改的数据决不进行复制的保证,为了使隐含共享的作用发挥得最好,可以采用两个新的编程习惯。第一种习惯是对于一个(非常量的)向量或者列表进行只读存取时,使用 at() 函数而不用 [] 操作符,因为Qt的容器类不能辨别 [] 操作符是否将出现在一个赋值的左边还是右边,他假设最坏的情况出现并且强制执行深层赋值,而 at() 函数则不被允许出现在一个赋值的左边。
-
如果是dialog窗体,需要在exec以后还能让其他代码继续执行,请在dialog窗体exec前增加一行代码,否则会阻塞窗体消息。
1 QDialog dialog;
2 dialog.setWindowModality(Qt::WindowModal);
3 dialog.exec();
-
安全的删除Qt的对象类,强烈建议使用deleteLater而不是delete,因为deleteLater会选择在合适的时机进行释放,而delete会立即释放,很可能会出错崩溃。如果要批量删除对象集合,可以用qDeleteAll,比如 qDeleteAll(btns);
-
在QTableView控件中,如果需要自定义的列按钮、复选框、下拉框等其他模式显示,可以采用自定义委托QItemDelegate来实现,如果需要禁用某列,则在自定义委托的重载createEditor函数返回0即可。自定义委托对应的控件在进入编辑状态的时候出现,如果想一直出现,则需要重载paint函数用drawPrimitive或者drawControl来绘制。
-
将 QApplication::style() 对应的drawPrimitive、drawControl、drawItemText、drawItemPixmap等几个方法用熟悉了,再结合QStyleOption属性,可以玩转各种自定义委托,还可以直接使用paint函数中的painter进行各种绘制,各种牛逼的表格、树状列表、下拉框等,绝对屌炸天。QApplication::style()->drawControl 的第4个参数如果不设置,则绘制出来的控件不会应用样式表。
-
在使用setItemWidget或者setCellWidget的过程中,有时候会发现设置的控件没有居中显示而是默认的左对齐,而且不会自动拉伸填充,对于追求完美的程序员来说,这个可不大好看,有个终极通用办法就是,将这个控件放到一个widget的布局中,然后将widget添加到item中,这样就完美解决了,而且这样可以组合多个控件产生复杂的控件。
1 //实例化进度条控件
2 QProgressBar *progress = new QProgressBar;
3 //增加widget+布局巧妙实现居中
4 QWidget *widget = new QWidget;
5 QHBoxLayout *layout = new QHBoxLayout;
6 layout->setSpacing(0);
7 layout->setMargin(0);
8 layout->addWidget(progress);
9 widget->setLayout(layout);
10 ui->tableWidget->setCellWidget(0, 0, widget);
- 很多时候需要在已知背景色的情况下,能够清晰的绘制文字,这个时候需要计算对应的文字颜色。
1 //根据背景色自动计算合适的前景色
2 double gray = (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255;
3 QColor textColor = gray > 0.5 ? Qt::black : Qt::white;
- 对QTableView、QTableWidget、QTreeView、QTreeWidget禁用列拖动。
1 #if (QT_VERSION < QT_VERSION_CHECK(5,0,0))
2 ui->tableView->horizontalHeader()->setResizeMode(0, QHeaderView::Fixed);
3 ui->treeView->header()->setResizeMode(0, QHeaderView::Fixed);
4 #else
5 ui->tableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
6 ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Fixed);
7 #endif
- 从Qt4转到Qt5,有些类的方法已经废弃或者过时了,如果想要在Qt5中启用Qt4的方法,比如QHeadVew的setMovable,可以在你的pro或者pri文件中加上一行即可:DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
09:081-090
- Qt中的QColor对颜色封装的很完美,支持各种转换,比如rgb、hsb、cmy、hsl,对应的是toRgb、toHsv、toCmyk、toHsl,还支持透明度设置,颜色值还能转成16进制格式显示。
1 QColor color(255, 0, 0, 100);
2 qDebug() << color.name() << color.name(QColor::HexArgb);
3 //输出 #ff0000 #64ff0000
- QVariant类型异常的强大,可以说是万能的类型,在进行配置文件的存储的时候,经常会用到QVariant的转换,QVariant默认自带了toString、toFloat等各种转换,但是还是不够,比如有时候需要从QVariant转到QColor,而却没有提供toColor的函数,这个时候就要用到万能办法。
1 if (variant.typeName() == "QColor") {
2 QColor color = variant.value<QColor>();
3 QFont font = variant.value<QFont>();
4 QString nodeValue = color.name(QColor::HexArgb);
5 }
-
Qt中的QString和const char *之间转换,最好用toStdString().c_str()而不是toLocal8Bit().constData(),比如在setProperty中如果用后者,字符串中文就会不正确,英文正常。
-
Qt的信号槽机制非常牛逼,也是Qt的独特的核心功能之一,有时候我们在很多窗体中传递信号来实现更新或者处理,如果窗体层级比较多,比如窗体A的父类是窗体B,窗体B的父类是窗体C,窗体C有个子窗体D,如果窗体A一个信号要传递给窗体D,问题来了,必须先经过窗体B中转到窗体C再到窗体D才行,这样的话各种信号关联信号的connect会非常多而且管理起来比较乱,可以考虑增加一个全局的单例类AppEvent,公共的信号放这里,然后窗体A对应信号绑定到AppEvent,窗体D绑定AppEvent的信号到对应的槽函数即可,干净清爽整洁。
-
QTextEdit右键菜单默认英文的,如果想要中文显示,加载widgets.qm文件即可,一个Qt程序中可以安装多个翻译文件,不冲突。
-
Qt中有个全局的焦点切换信号focusChanged,可以用它做自定义的输入法。Qt4中默认会安装输入法上下文,比如在main函数打印a.inputContext会显示值,这个默认安装的输入法上下文,会拦截两个牛逼的信号QEvent::RequestSoftwareInputPanel和QEvent::CloseSoftwareInputPanel,以至于就算你安装了全局的事件过滤器依然识别不到这两个信号,你只需要在main函数执行a.setInputContext(0)即可,意思是安装输入法上下文为空。Qt5.7以后提供了内置的输入法,可以通过在main函数最前面加上 qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard")); 来启用。
-
在Qt5.10以后,表格控件QTableWidget或者QTableView的默认最小列宽改成了15,以前的版本是0,所以在新版的qt中,如果设置表格的列宽过小,不会应用,取的是最小的列宽。所以如果要设置更小的列宽需要重新设置ui->tableView->horizontalHeader()->setMinimumSectionSize(0);
-
Qt源码中内置了一些未公开的不能直接使用的黑科技,都藏在对应模块的private中,比如gui-private widgets-private等,比如zip文件解压类QZipReader、压缩类QZipWriter就在gui-private模块中,需要在pro中引入QT += gui-private才能使用。
1 #include "QtGui/private/qzipreader_p.h"
2 #include "QtGui/private/qzipwriter_p.h"
3
4 QZipReader reader(dirPath);
5 QString path("");
6 //解压文件夹到当前目录
7 reader.extractAll(path);
8 //文件夹名称
9 QZipReader::FileInfo fileInfo = reader.entryInfoAt(0);
10 //解压文件
11 QFile file(filePath);
12 file.open(QIODevice::WriteOnly);
13 file.write(reader.fileData(QString::fromLocal8Bit("%1").arg(filePath)));
14 file.close();
15 reader.close();
16
17 QZipWriter *writer = new QZipWriter(dirPath);
18 //添加文件夹
19 writer->addDirectory(unCompress);
20 //添加文件
21 QFile file(filePath);
22 file.open(QIODevice::ReadOnly);
23 writer->addFile(data, file.readAll());
24 file.close();
25 writer->close();
-
理论上串口和网络收发数据都是默认异步的,操作系统自动调度,完全不会卡住界面,网上那些说收发数据卡住界面主线程的都是不正确的,真正的耗时是在运算以及运算后的处理,而不是收发数据,在一些小数据量运算处理的项目中,一般不建议动用线程去处理,线程需要调度开销的,不要什么东西都往线程里边扔,线程不是万能的。只有当真正需要将一些很耗时的操作比如编码解码等,才需要移到线程处理。
-
在构造函数中获取控件的宽高很可能是不正确的,需要在控件首次显示以后再获取才是正确的,控件是在首次显示以后才会设置好正确的宽高值,记住是在首次显示以后,而不是构造函数或者程序启动好以后,如果程序启动好以后有些容器控件比如QTabWidget中的没有显示的页面的控件,你去获取宽高很可能也是不正确的,万无一失的办法就是首次显示以后去获取。
10:091-100
-
数据库处理一般建议在主线程,如果非要在其他线程,务必记得打开数据库也要在那个线程,即在那个线程使用数据库就在那个线程打开,不能打开数据库在主线程,执行sql在子线程,很可能出问题。
-
新版的QTcpServer类在64位版本的Qt下很可能不会进入incomingConnection函数,那是因为Qt5对应的incomingConnection函数参数变了,由之前的int改成了qintptr,改成qintptr有个好处,在32位上自动是quint32而在64位上自动是quint64,如果在Qt5中继续写的参数是int则在32位上没有问题在64位上才有问题,所以为了兼容Qt4和Qt5,必须按照不一样的参数写。
#if (QT_VERSION > QT_VERSION_CHECK(5,0,0))
void incomingConnection(qintptr handle);
#else
void incomingConnection(int handle);
#endif
-
Qt支持所有的界面控件比如QPushButton、QLineEdit自动关联 on_控件名_信号(参数) 信号槽,比如按钮的单击信号 on_pushButton_clicked(),然后直接实现槽函数即可。
-
QWebEngineView控件由于使用了opengl,在某些电脑上可能由于opengl的驱动过低会导致花屏或者各种奇奇怪怪的问题,比如showfullscreen的情况下鼠标右键失效,需要在main函数启用软件opengl渲染。
1 #if (QT_VERSION > QT_VERSION_CHECK(5,4,0))
2 //下面两种方法都可以,Qt默认采用的是AA_UseDesktopOpenGL
3 QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
4 //QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
5 #endif
6 QApplication a(argc, argv);
另外一个方法解决 全屏+QWebEngineView控件一起会产生右键菜单无法弹出的bug,需要上移一个像素
1 QRect rect = qApp->desktop()->geometry();
2 rect.setY(-1);
3 rect.setHeight(rect.height());
4 this->setGeometry(rect);
- QStyle内置了很多方法用处很大,比如精确获取滑动条鼠标按下处的值。
QStyle::sliderValueFromPosition(minimum(), maximum(), event->x(), width());
- 用QFile读写文件的时候,推荐用QTextStream文件流的方式来读写文件,速度快很多,基本上会有30%的提升,文件越大性能区别越大。
1 //从文件加载英文属性与中文属性对照表
2 QFile file(":/propertyname.txt");
3 if (file.open(QFile::ReadOnly)) {
4 //QTextStream方法读取速度至少快百分之30
5 #if 0
6 while(!file.atEnd()) {
7 QString line = file.readLine();
8 appendName(line);
9 }
10 #else
11 QTextStream in(&file);
12 while (!in.atEnd()) {
13 QString line = in.readLine();
14 appendName(line);
15 }
16 #endif
17 file.close();
18 }
- 用QFile.readAll()读取QSS文件默认是ANSI格式,不支持UTF8,如果在QtCreator中打开qss文件来编辑保存,这样很可能导致qss加载以后没有效果。
1 void frmMain::initStyle()
2 {
3 //加载样式表
4 QString qss;
5 //QFile file(":/qss/psblack.css");
6 //QFile file(":/qss/flatwhite.css");
7 QFile file(":/qss/lightblue.css");
8 if (file.open(QFile::ReadOnly)) {
9 #if 1
10 //用QTextStream读取样式文件不用区分文件编码 带bom也行
11 QStringList list;
12 QTextStream in(&file);
13 //in.setCodec("utf-8");
14 while (!in.atEnd()) {
15 QString line;
16 in >> line;
17 list << line;
18 }
19
20 qss = list.join("\n");
21 #else
22 //用readAll读取默认支持的是ANSI格式,如果不小心用creator打开编辑过了很可能打不开
23 qss = QLatin1String(file.readAll());
24 #endif
25 QString paletteColor = qss.mid(20, 7);
26 qApp->setPalette(QPalette(QColor(paletteColor)));
27 qApp->setStyleSheet(qss);
28 file.close();
29 }
30 }
- QString内置了很多转换函数,比如可以调用toDouble转为double数据,但是当你转完并打印的时候你会发现精确少了,只剩下三位了,其实原始数据还是完整的精确度的,只是打印的时候优化成了三位,如果要保证完整的精确度,可以调用 qSetRealNumberPrecision 函数设置精确度位数即可。
1 QString s1, s2;
2 s1 = "666.5567124";
3 s2.setNum(888.5632123, 'f', 7);
4 qDebug() << qSetRealNumberPrecision(10) << s1.toDouble() << s2.toDouble();
- 用QScriptValueIterator解析数据的时候,会发现总是会多一个节点内容,并且内容为空,如果需要跳过则增加一行代码。
1 while (it.hasNext()) {
2 it.next();
3 if (it.flags() & QScriptValue::SkipInEnumeration)
4 continue;
5 qDebug() << it.name();
6 }
- setPixmap是最糟糕的贴图方式,一般只用来简单的不是很频繁的贴图,频繁的建议painter绘制,默认双缓冲,在高级点用opengl绘制,利用GPU。