C++(Qt)开发经验

3 篇文章 0 订阅
1. visual studio 编译报错 ''无法解析的外部命令(外部符号)":可能是函数中参数类型的前置声明有问题,比如将struct ClassName;不小心声明为class ClassName;
2. 在Visual Studio 2019开发使用QQuick时,运行时卡在了setcontextProperty函数执行时,可能是生成了类图并添加到了了工程(项目)中,删掉或移除多余的文件(类图)即可
3. 在Windows上使用qmake构建带有 Address Sanitizer 的Qt程序:
# # app.pro
include(../win32-clang-msvc/qmake.conf)
# Generate debug information
CONFIG += force_debug_info
# Force release build as debug builds are not supported
CONFIG += release
QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += -fsanitize=address
QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO += -fsanitize=address
# # Add the path to the clang ASAN runtime and link against
QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO += /LIBPATH:\"C:/Program Files (x86)/Microsoft Visual Studio/2019/Professional/VC/Tools/Llvm/x64/lib/clang/10.0.0/lib/windows\"
QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO += clang_rt.asan_dynamic-x86_64.lib /wholearchive:clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
//main.cpp
#include <QApplication>
#include <QByteArray>
#include <QDebug>
#include <QTimer>
 
int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
 
    QTimer t;
    t.setInterval(0);
    t.setSingleShot(true);
    {
        QByteArray ba = QByteArrayLiteral("Test byte array literal");
        t.connect(&t, &QTimer::timeout, &t, [d=ba.data()](){
            qDebug() << d;
        });
    }
    t.start();
 
    return app.exec();
}

运行程序,我们会看到如下输出

=================================================================
==15844==ERROR: AddressSanitizer: heap-use-after-free on address 0x12643130a628 at pc 0x7ff81ca3e54a bp 0x00b1e08f86e0 sp 0x00b1e08f8728
READ of size 24 at 0x12643130a628 thread T0
    #0 0x7ff81ca3e572 in _asan_wrap_strlen+0x1b2 (C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\Llvm\x64\lib\clang\10.0.0\lib\windows\clang_rt.asan_dynamic-x86_64.dll+0x18002e572)
    #1 0x7ff813417f6e in QString::fromUtf8 C:\qt5\qtbase\src\corelib\tools\qstring.h:572
    #2 0x7ff7780318aa in QtPrivate::QFunctorSlotObject<`lambda at C:\test-build-with-sanitized-qt\main.cpp:15:45',0,QtPrivate::List<>,void>::impl C:\qt-build-sanitized\include\QtCore\qobjectdefs_impl.h:439
    #3 0x7ff813cd4fc3 in QMetaObject::activate C:\qt5\qtbase\src\corelib\kernel\qobject.cpp:3781
    #4 0x7ff813cf1671 in QTimer::timerEvent C:\qt5\qtbase\src\corelib\kernel\qtimer.cpp:255
    #5 0x7ff813cbfcf5 in QObject::event C:\qt5\qtbase\src\corelib\kernel\qobject.cpp:1247
    #6 0x7ff8158ac7a7 in QApplicationPrivate::notify_helper C:\qt5\qtbase\src\widgets\kernel\qapplication.cpp:3737
    #7 0x7ff8158b2470 in QApplication::notify C:\qt5\qtbase\src\widgets\kernel\qapplication.cpp:3598
    #8 0x7ff813c19f8d in QCoreApplication::notifyInternal2 C:\qt5\qtbase\src\corelib\kernel\qcoreapplication.cpp:1084
    #9 0x7ff813d7ef0d in QEventDispatcherWin32::event C:\qt5\qtbase\src\corelib\kernel\qeventdispatcher_win.cpp:1064
    #10 0x7ff8158ac7a7 in QApplicationPrivate::notify_helper C:\qt5\qtbase\src\widgets\kernel\qapplication.cpp:3737
    #11 0x7ff8158b2470 in QApplication::notify C:\qt5\qtbase\src\widgets\kernel\qapplication.cpp:3598
    #12 0x7ff813c19f8d in QCoreApplication::notifyInternal2 C:\qt5\qtbase\src\corelib\kernel\qcoreapplication.cpp:1084
    #13 0x7ff813c1f045 in QCoreApplicationPrivate::sendPostedEvents C:\qt5\qtbase\src\corelib\kernel\qcoreapplication.cpp:1821
    #14 0x7ff84512b0b5 in QWindowsGuiEventDispatcher::sendPostedEvents C:\qt5\qtbase\src\platformsupport\eventdispatchers\qwindowsguieventdispatcher.cpp:81
    #15 0x7ff813d6fdee in qt_internal_proc C:\qt5\qtbase\src\corelib\kernel\qeventdispatcher_win.cpp:245
    #16 0x7ff88793e857 in CallWindowProcW+0x3f7 (C:\WINDOWS\System32\USER32.dll+0x18000e857)
    #17 0x7ff88793e298 in DispatchMessageW+0x258 (C:\WINDOWS\System32\USER32.dll+0x18000e298)
    #18 0x7ff813d757d5 in QEventDispatcherWin32::processEvents C:\qt5\qtbase\src\corelib\kernel\qeventdispatcher_win.cpp:639
    #19 0x7ff84512b046 in QWindowsGuiEventDispatcher::processEvents C:\qt5\qtbase\src\platformsupport\eventdispatchers\qwindowsguieventdispatcher.cpp:74
    #20 0x7ff813c0aaaf in QEventLoop::exec C:\qt5\qtbase\src\corelib\kernel\qeventloop.cpp:225
    #21 0x7ff813c1bf60 in QCoreApplication::exec C:\qt5\qtbase\src\corelib\kernel\qcoreapplication.cpp:1385
    #22 0x7ff778031443 in main C:\test-build-with-sanitized-qt\main.cpp:21
    #23 0x7ff7780326b3 in __scrt_common_main_seh D:\agent\_work\9\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
    #24 0x7ff888866fd3 in BaseThreadInitThunk+0x13 (C:\WINDOWS\System32\KERNEL32.DLL+0x180016fd3)
    #25 0x7ff8890fcec0 in RtlUserThreadStart+0x20 (C:\WINDOWS\SYSTEM32\ntdll.dll+0x18004cec0)
0x12643130a628 is located 24 bytes inside of 48-byte region [0x12643130a610,0x12643130a640)
freed by thread T0 here:
    #0 0x7ff81ca45094 in _asan_memmove+0x344 (C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\Llvm\x64\lib\clang\10.0.0\lib\windows\clang_rt.asan_dynamic-x86_64.dll+0x180035094)
    #1 0x7ff77803141f in main C:\test-build-with-sanitized-qt\main.cpp:18
    #2 0x7ff7780326b3 in __scrt_common_main_seh D:\agent\_work\9\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
    #3 0x7ff888866fd3 in BaseThreadInitThunk+0x13 (C:\WINDOWS\System32\KERNEL32.DLL+0x180016fd3)
    #4 0x7ff8890fcec0 in RtlUserThreadStart+0x20 (C:\WINDOWS\SYSTEM32\ntdll.dll+0x18004cec0)
previously allocated by thread T0 here:
    #0 0x7ff81ca451a4 in _asan_memmove+0x454 (C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\Llvm\x64\lib\clang\10.0.0\lib\windows\clang_rt.asan_dynamic-x86_64.dll+0x1800351a4)
    #1 0x7ff813503701 in QArrayData::allocate C:\qt5\qtbase\src\corelib\tools\qarraydata.cpp:118
    #2 0x7ff81350bcaf in QByteArray::reallocData C:\qt5\qtbase\src\corelib\tools\qbytearray.cpp:1905
    #3 0x7ff81341493b in QByteArray::data C:\qt5\qtbase\src\corelib\tools\qbytearray.h:569
    #4 0x7ff778031313 in main C:\test-build-with-sanitized-qt\main.cpp:15
    #5 0x7ff7780326b3 in __scrt_common_main_seh D:\agent\_work\9\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
    #6 0x7ff888866fd3 in BaseThreadInitThunk+0x13 (C:\WINDOWS\System32\KERNEL32.DLL+0x180016fd3)
    #7 0x7ff8890fcec0 in RtlUserThreadStart+0x20 (C:\WINDOWS\SYSTEM32\ntdll.dll+0x18004cec0)
SUMMARY: AddressSanitizer: heap-use-after-free (C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\Llvm\x64\lib\clang\10.0.0\lib\windows\clang_rt.asan_dynamic-x86_64.dll+0x18002e572) in _asan_wrap_strlen+0x1b2
Shadow bytes around the buggy address:
  0x04a8b7561470: fa fa fd fd fd fd fd fa fa fa fd fd fd fd fd fa
  0x04a8b7561480: fa fa fd fd fd fd fd fa fa fa fd fd fd fd fd fa
  0x04a8b7561490: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00
  0x04a8b75614a0: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 01 fa
  0x04a8b75614b0: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 00
=>0x04a8b75614c0: fa fa fd fd fd[fd]fd fd fa fa 00 00 00 00 00 00
  0x04a8b75614d0: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00
  0x04a8b75614e0: fa fa fd fd fd fd fd fa fa fa fd fd fd fd fd fa
  0x04a8b75614f0: fa fa fd fd fd fd fd fa fa fa fd fd fd fd fd fa
  0x04a8b7561500: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 fa
  0x04a8b7561510: fa fa 00 00 00 00 00 04 fa fa 00 00 00 00 00 04
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable: 00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone: fa
  Freed heap region: fd
  Stack left redzone: f1
  Stack mid redzone: f2
  Stack right redzone: f3
  Stack after return: f5
  Stack use after scope: f8
  Global redzone: f9
  Global init order: f6
  Poisoned by user: f7
  Container overflow: fc
  Array cookie: ac
  Intra object redzone: bb
  ASan internal: fe
  Left alloca redzone: ca
  Right alloca redzone: cb
  Shadow gap: cc
==15844==ABORTING
4. 在Linux使用qmake构建带有 Address Sanitizer 的Qt程序:
# # application.pro
QT += widgets gui core
TEMPLATE = app
TARGET = application
CONFIG += sanitizer
CONFIG += sanitizer_address
# CONFIG += sanitize_undefined
# CONFIG += sanitize_leak
# CONFIG += sanitize_thread

QMAKE_CXXFLAGS+= -ggdb

SOURCES += main.cpp
5. Qt程序部署后,阴影等特效不起作用,甚至阴影变成黑色背景:
+	检查屏幕色深:根据官方文档中的说明,屏幕色深低于16-bits的话是特效是不起作用的
+	如果是在Linux上,查看日志或控制台输出,观察是否有类似

libGL.so.1: unable load to driver: s3g这样的输出,如有有,请检查显卡驱动,或自己程序发布时是否多带了一下系统的库,将程序多带的libstdc++.so* libX11* libxcb* libGL*都删掉可以解决此问题

6. 为QT应用添加管理员权限
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "/MANIFESTUAC:\"level='requireAdministrator' uiAccess='false'\"")
7. 下载文件限速:
-	通过QNetworkReply的setReadBufferSize理论上可以实现,但是Qt官方一直没解决这个函数的bug。
-	可以使用QTcpSocket的setReadBufferSize实现,这里暂时没有发现bug(5.9之后)
-	**建议使用libcurl实现**, 例如: `curl_easy_setopt (curl, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t) down_speed * 1024);`具体可参考[这篇博客](https://blog.csdn.net/tao_627/article/details/39047253)。
  1. 基于QAbstractItemView(如QTableview、QListview)的item拖拽和多选拖拽,当view->setSelecitonMode(QAbstractItemView::ExtendedSelection);的选择模式后,我们可以使用Ctrl按键进行多选和反选、也可以拖动多选。但是无法在dragMovEventdropEvent中使用Qt::ControlModifier作为拖动复制的控制键。
    解决方法:
if (event->modifiers() & Qt::ControlModifier) {
	QPoint pos = event->pos();
	QPersistentModelIndex index = indexAt(pos);
	QItemSelectionModel::SelectionFlags command = selectionCommand(index, event);
	if (index.isValid()) {
		command &= ~QItemSelectionModel::Toggle;
		auto ctrlDragSelectionFlag = selectionModel()->isSelected(index) 
														? QItemSelectionModel::Deselect
														: QItemSelectionModel::Select;
		command |= ctrlDragSelectionFlag;
		// 如果在 setSelectionBehavior()中设置了SelectRows模式
		// command |= QItemSelectionModel::Rows;
		if( 0 == (command & QItemSelectionModel::Current) ) 
			selection(QRect(pos, Qsize(1, 1)), command);
	}
}

继承并重写tableview或listview的mousePressEvent和mouseReleaseEvent,并在这两个函数中分别写入以上代码,再调用父类的函数即可。如:

void MyListView::mousePressEvent(QMouseEvent* event) {
	// 调用上述代码
	...
	QListView::mousePressEvent(event);
}
9. 不同系统下打开文件夹并选定指定的文件
> 参考网址: [stackoverflow](https://stackoverflow.com/questions/13680415/how-to-open-explorer-with-a-specific-file-selected) 
+  Windows下比较简单,直接在程序中调用命令行即可。如:
`explorer /select,E:\DevSidecar.zip` , 效果如下所示![在这里插入图片描述](https://img-blog.csdnimg.cn/6bd9917ca737448381ae4a9046ba02d9.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBATmljZUJsdWVDaGFp,size_20,color_FFFFFF,t_70,g_se,x_16)

在这里插入图片描述

  • Linux下调用DBus,可先用qdbusviewer工具进行查看

参考网址:freedesktop.org

auto m = QDBusMessage::createMethodCall("org.freedesktop.FileManager1",
										"/org/freedesktop/FileManager1",
										"org.freedesktop.FileManager1",
										"ShowItems");
m << QStringList{ QUrl::fromLocalFile("/home/foo/file1_name").toString(QUrl::FullyEncoded), "file:///home/foo/file2_name" } << "" ;
bool ret = QDBusConnection::sessionBus().send(m); // ret == true
10. 如果想同时在Linux和Windows下第二次用QFile打开续写一个文件,请使用QFile(filePath).open(QIODevice::ReadWrite)模式打开。
11. Qt程序在Linux (Qtcreator里)下运行默认不显示qDebug打印的日志,可以在启动前设置环境变量QT_LOGGING_RULES=*.debug=true,可以在新版的QtCreator里看到这个环境变量默认是QT_LOGGING_RULES=*.debug=false
12. 设置环境变量 export QT_DEBUG_PLUGINS=1 可以看到QtCreator等Qt应用加载插件报错的详细信息
13. 如果在程序中对自己的配置文件进行写操作,为防止写入失败,请使用QSaveFile类进行写入操作,如果有必要,请设置setDirectWriteFallback(true),配置文件尽量不用ini文件和xml,请尽量使用JSON文件作为配置文件。
14. Qt-Bug, 复现步骤:
  • 环境:Windows10 或 Windows11, 在设置中开启自动隐藏任务栏选项(不隐藏任务栏不会有此bug
    自动隐藏任务栏

  • Qt版本:

    • 5.9.9版本无此bug
    • 5.13.x有此bug
    • 5.15.x有此bug
    • 6.2.0 – 6.2.2有此bug
  • 控件: 无边框窗口

  • 复现步骤:

    1. 点击最大化按钮(调用showMaximized(), 窗口最大化)
    2. 点击最小化按钮(调用showMinimized(), 窗口最小化)
    3. 点击任务栏程序图标,使窗口显示出来
    4. 点击还原(最大化)按钮 (调用showNormal(), 窗口还原)
    5. bug出现: 调用isMaximized()判断返回true, 预期返回false

在这里插入图片描述

问题分析: 通过查看Qt源码和进源码调试,推测可能由修改QTBUG-46763后引起的新bug

#include "widget.h"
#include "ui_widget.h"
#include <QStyle>
#include <QWindow>
#include <QDebug>


Widget::Widget(QWidget *parent)
    : QWidget(parent)
      , ui(new Ui::Widget)
{
    ui->setupUi(this);
//    setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
    setWindowFlag(Qt::FramelessWindowHint);
    connect(ui->btnMin, &QPushButton::clicked, this, &QWidget::showMinimized);
    connect(ui->btnQuit, &QPushButton::clicked, this, &QWidget::close);
    connect(ui->btnMax, &QPushButton::clicked, this, [this]{
        qDebug() << "before"<< windowState() << windowHandle()->windowState();
       if (isMaximized()) {
       	   // 去掉这行注释即可解决bug
           // windowHandle()->setWindowState(Qt::WindowNoState);
           showNormal();
           qWarning() << "showNormal";
           ui->btnMax->setProperty("isMaxed", false);
       } else {
           showMaximized();
           qWarning() << "showMaximized";
           ui->btnMax->setProperty("isMaxed", true);
       }
       qDebug() << "after"<< windowState() << windowHandle()->windowState();
    });
}

Widget::~Widget()
{
    delete ui;
}


15. 获取Caps Lock按键状态
#if defined (Q_OS_LINUX)
#include <QX11Info>
#include <X11/Xlib.h>
#define XK_MISCELLANY
#include <X11/keysymdef.h>
#elif defined (Q_OS_WIN)
#include <windows.h>
#include <winuser.h>
#endif

bool getCapsLockToggled()
{
#if defined (Q_OS_LINUX)
	auto dpy = QX11Info::display();
	XKeyboardState x;
	XGetKeyboardControl(dpy, &x);
	if (x.led_mask & 0x01)
		return true;
	else
		return false;
#elif defined (Q_OS_WIN)
	auto ret = GetKeyState(VK_CAPITAL);
	if (ret & 0x01)
		return true;
	else
		return false;
#endif
// TODO: Q_OS_MAC(太穷没见过mac,暂时不写)
}
16. 禁止子进程继承句柄
// 使用场景, 做rotating file log时,如果有子进程,那么日志文件句柄被继承,无法关闭,提示被占用
// 需要在open后禁用子进程继承句柄
	FILE* fp = fopen("app.log", "a+");
#if defined(_WIN32)
	int fd = _fileno(logfp);
	HANDLE handel = _get_osfhandle(fd);
	SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0x0);
#else // unix
// TODO
#endif
17. deepin(UOS)适配
  1. 使用xrdb -query查询出来的dpi/96.0 就是缩放比例,计算出缩放比例后,在构造QApp对象前设置QT_SCREEN_SCALE_FACTORS=缩放比例, 不要使用qApp->setAttribute(Qt::AA_EnableHighDpiScaling);, 会导致窗口不显示,deepin桌面也会自动对应用进行缩放,不计算dpi也可以。
  2. 系统内置了环境变量 QT_PLUGIN_PATH,可以通过设置环境变量 export QT_DEBUG_PLUGINS=1观察程序从哪里加载plugins。
  3. 系统默认启动Qt应用使用的wayland,在启动脚本中添加启动参数 exec yourapp -platform xcb即可。
  4. 加载字体需要设置环境变量 export QT_QPA_FONTDIR="$TOPDIR/bin/plugins/fonts"
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值