QT
1.
qt自定义事件已经事件循环原理:参考 threaddata的原理
qt编译顺序:参考
以前的Qt Doc(官方)
qt的属性
qt6在线下载安装:参考 qt Automation下载安装
qtcreator下载编译
CONFIG += qt 的作用
qtbase 编译
单元测试:参考
完整的测试代码:
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * Author: Alex * mail: abnk@qq.com * File Type: unix Name: test.cpp * Created Time: 2021-07-23 10:15 33 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include <QtTest/QtTest> // 参考: https://blog.51cto.com/u_9291927/2114179 // qmake -project "QT+=testlib // qmake && make && ./qt_test class TestQString: public QObject { Q_OBJECT private slots: void toUpper(); void toUpper_data(); }; void TestQString::toUpper() { // 原来没有测试数据的时候需要将测试数据一个一个的写到代码里面。 // QString str = "Hello"; // QVERIFY(str.toUpper() == "HELLO"); // QCOMPARE(str.toUpper(), QString("HELLO")); QFETCH(QString, lowerString); QFETCH(QString, upperResult); // 添加的数据只需要被调用一次就被批量测试完了 QCOMPARE(lowerString.toUpper(), upperResult); } // 为测试函数提供数据的函数必须与该测试函数同名,并加上_data后缀 void TestQString::toUpper_data() { QTest::addColumn<QString>("lowerString"); QTest::addColumn<QString>("upperResult"); QTest::newRow("all lower") << "hello" << "HELLO"; QTest::newRow("mixed") << "Hello" << "HELLO"; QTest::newRow("all upper") << "HELLO" << "HELLO"; } QTEST_APPLESS_MAIN(TestQString); // 同类名 #include "test.moc" // 文件名+.moc
2. QScopeGuard
析构的时候会做一些事情。
template <typename F> class [[nodiscard]] QScopeGuard { public: explicit QScopeGuard(F &&f) noexcept : m_func(std::move(f)) { } explicit QScopeGuard(const F &f) noexcept : m_func(f) { } QScopeGuard(QScopeGuard &&other) noexcept : m_func(std::move(other.m_func)) , m_invoke(qExchange(other.m_invoke, false)) { } ~QScopeGuard() noexcept { if (m_invoke) m_func(); } void dismiss() noexcept { m_invoke = false; } private: Q_DISABLE_COPY(QScopeGuard) F m_func; bool m_invoke = true; }; #ifdef __cpp_deduction_guides template <typename F> QScopeGuard(F(&)()) -> QScopeGuard<F(*)()>; #endif //! [qScopeGuard] template <typename F> [[nodiscard]] QScopeGuard<typename std::decay<F>::type> qScopeGuard(F &&f) { return QScopeGuard<typename std::decay<F>::type>(std::forward<F>(f)); }
3. run in main thread (use lambda)
/*! rewrite from: https://zhuanlan.zhihu.com/p/364710810 */ #include <QtCore/QCoreApplication> #include <QtCore/QObject> #include <QtCore/QThread> #include <QtCore/QDebug> #include <QtCore/QQueue> #include <functional> #include <unistd.h> // sleep using Callback = std::function <void(QQueue <void *>)>; class Worker : public QObject { Q_OBJECT public: Worker() { moveToThread(&m_thread); m_thread.start(); } public slots: void slotRunInSub() { // add callback // qDebug() << "run in sub thread slot:" << QThread::currentThreadId(); if (m_workerCallback) { m_workerCallback(m_queue); } emit mSig(); } signals: void mSig(); private: QThread m_thread; QQueue <void *> m_queue; Callback m_workerCallback; public: void setCallback(Callback &&func, void *arg) { m_queue.enqueue(arg); m_workerCallback = std::forward<Callback>(func); } }; class Dummy : public QObject { Q_OBJECT public: Dummy(QObject* parent=0) : QObject(parent) { QObject::connect(this, SIGNAL(sig()), &worker, SLOT(slotRunInSub())); // update QObject::connect(&worker, SIGNAL(mSig()), this, SLOT(slotRunInMain())); // process } // template <> void PostToChild(Callback &&func, void *arg) { // m_queue.enqueue(arg); worker.setCallback(std::forward<Callback>(func), arg); } void PostToMain(Callback &&func, void *arg) { m_queue.enqueue(arg); m_callback = std::forward<Callback>(func); sigToSubThd(); // call slot, note sub thread process data } public slots: void sigToSubThd() { emit sig(); } signals: void sig(); public slots: void slotRunInMain() { // update qDebug() << "run in main: " << QThread::currentThreadId(); if (m_callback) { m_callback(m_queue); } } private: Worker worker; QQueue <void *> m_queue; Callback m_callback; }; #include "main.moc" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() << "main thread: " << QThread::currentThreadId(); // QCoreApplication::instance()->thread(); Dummy dummy; int data = 1111; dummy.PostToChild([](QQueue <void *> queue){ // in sub thread // 1. memcpy data; // 2. process data; // 3. enqueue data; qDebug() << "run in sub thread slot:" << QThread::currentThreadId(); sleep(5); }, (void *)&data); // set cb, copy data and enqueue. int enqueueData = 2222; dummy.PostToMain([](QQueue <void *> queue){ // in sub thread void *deData = queue.dequeue(); qDebug() << "dequeue: " << *(int *)deData; // dequeue data, and update??? qDebug() << "run in main thread: " << QThread::currentThreadId(); }, (void *)&enqueueData); qDebug() << "async exec"; return a.exec(); }
and then, add concurent,
add ensure connect sub to main.
add move to main thread
add variable args function and declytype
moveToThread has memory hosting relationship
/*! rewrite from: https://zhuanlan.zhihu.com/p/364710810 */ #include <QtCore/QCoreApplication> #include <QtWidgets/QApplication> #include <QtCore/QObject> #include <QtCore/QThread> #include <QtCore/QDebug> #include <QtCore/QQueue> #include <functional> #include <unistd.h> // sleep #include <thread> #include <QStyle> #include <QTimer> #include <QPushButton> #include <QMainWindow> using Callback = std::function <void(QQueue <void *>)>; class Worker : public QObject { Q_OBJECT public: Worker() { moveToThread(&m_thread); m_thread.start(); } ~Worker() { if (m_thread.isRunning()) { // m_thread.deleteLater(); // m_thread.exit(0); m_thread.quit(); m_thread.wait(); } } public slots: void slotRunInSub() { // add callback // qDebug() << "run in sub thread slot:" << QThread::currentThreadId(); if (m_workerCallback) { m_workerCallback(m_queue); } emit mSig(); } signals: void mSig(); private: QThread m_thread; QQueue <void *> m_queue; Callback m_workerCallback; public: void setCallback(Callback &&func, void *arg) { m_queue.enqueue(arg); m_workerCallback = std::forward<Callback>(func); } }; class Dummy : public QObject { Q_OBJECT public: Dummy(QObject* parent=0) : QObject(parent) { // Guranentee connect main thread to child if (QCoreApplication::instance() && QCoreApplication::instance()->thread()) { moveToThread(QCoreApplication::instance()->thread()); QObject::connect(this, SIGNAL(signalConnectInMainToChild()), this, SLOT(slotConnectInMainToChild())); // qDebug() << "Dummy is constructor in main: " << QThread::currentThreadId(); } emit signalConnectInMainToChild(); } ~Dummy() { // if (worker) { // worker->deleteLater(); // worker = nullptr; // } } // template <> void PostToChild(Callback &&func, void *arg) { // m_queue.enqueue(arg); worker->setCallback(std::forward<Callback>(func), arg); } void PostToMain(Callback &&func, void *arg) { m_queue.enqueue(arg); m_callback = std::forward<Callback>(func); sigToSubThd(); // call slot, note sub thread process data } public slots: void sigToSubThd() { emit sig(); } void slotConnectInMainToChild() { worker = new Worker; qDebug() << "Dummy is connect in main: " << QThread::currentThreadId(); QObject::connect(this, SIGNAL(sig()), worker, SLOT(slotRunInSub())); // update QObject::connect(worker, SIGNAL(mSig()), this, SLOT(slotRunInMain())); // process } signals: void sig(); void signalConnectInMainToChild(); public slots: void slotRunInMain() { // update qDebug() << "run in main: " << QThread::currentThreadId(); if (m_callback) { m_callback(m_queue); } } private: Worker *worker; QQueue <void *> m_queue; Callback m_callback; }; #include "main.moc" int main(int argc, char *argv[]) { QApplication a(argc, argv); qDebug() << "main thread: " << QThread::currentThreadId(); // QCoreApplication::instance()->thread(); std::thread thd([]{ Dummy dummy; // dummy.signalConnectInMainToChild(); sleep(1); int data = 1111; dummy.PostToChild([](QQueue <void *> queue){ // in sub thread // 1. memcpy data; // 2. process data; // 3. enqueue data; qDebug() << "run in sub thread slot:" << QThread::currentThreadId(); // sleep(5); }, (void *)&data); // set cb, copy data and enqueue. int enqueueData = 2222; dummy.PostToMain([](QQueue <void *> queue){ // in sub thread void *deData = queue.dequeue(); qDebug() << "dequeue: " << *(int *)deData; // dequeue data, and update??? qDebug() << "run in main thread: " << QThread::currentThreadId(); }, (void *)&enqueueData); qDebug() << "async exec"; sleep(6); // qApp->quit(); }); thd.detach(); QMainWindow w; QWidget central; QPushButton button(¢ral); w.setCentralWidget(¢ral); #if 1 // analogue render event QTimer timer; QObject::connect(&timer, &QTimer::timeout, [&button]{ static bool flag = false; QStyle *style = QApplication::style(); QIcon openPicture = style->standardIcon(QStyle::SP_TitleBarMenuButton); QIcon inforIron = style->standardIcon(QStyle::SP_DialogYesButton); button.setIcon(flag ? openPicture : inforIron); flag = !flag; }); timer.setInterval(200); timer.start(); #endif #if 0 // single event ocupaid block QObject o; QTimer::singleShot(100, &o, [&button]{ // while(1) { sleep(1); qDebug() << QThread::currentThreadId(); static bool flag = false; QStyle* style = QApplication::style(); QIcon openPicture = style->standardIcon(QStyle::SP_TitleBarMenuButton); QIcon inforIron = style->standardIcon(QStyle::SP_MediaSkipBackward); button.setIcon(flag ? openPicture : inforIron); flag = !flag; } }); #endif w.show(); qDebug() << "enter exec"; return a.exec(); }
4. QRunnable
4. 国际化
5. 一些布局的技巧:
QHBoxLayout *hLayoutAppRm = new QHBoxLayout; mainLayout->addLayout(hLayoutAppRm); QLabel *iconLabel = new QLabel(); iconLabel->setFrameShape(QFrame::Box); DLabel *appNameLabel = new DLabel(m_model->getAppName()); appNameLabel->setFrameShape(QFrame::Box); appNameLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); DPushButton *appRmButton = new DPushButton("Uninstall"); DFontSizeManager::instance()->bind(appNameLabel, DFontSizeManager::T5, QFont::DemiBold); iconLabel->setPixmap(getAppIcon(m_model->getIcon(), QSize(48, 48))); hLayoutAppRm->addWidget(iconLabel,0); hLayoutAppRm->setSpacing(0); hLayoutAppRm->addWidget(appNameLabel, 1); hLayoutAppRm->addWidget(appRmButton, 0);
6. 关于 QObject 宏
QDbus:
Dtk里面的应用:
#include "mainwindow.h" #include <QApplication> #include <QDebug> #include <DListView> #include <QtDBus> #include <QObject> #include <DApplication> #include <DDBusSender> #include <QDBusPendingCallWatcher> DWIDGET_USE_NAMESPACE // #include "main.moc" int main(int argc, char *argv[]) { QApplication app(argc, argv); auto async = DDBusSender() .service("com.deepin.dde.ControlCenter") .interface("com.deepin.dde.ControlCenter") .path("/com/deepin/dde/ControlCenter") .method("Toggle") .call(); // QDBusPendingCall async = interface.asyncCall("setName", "Brion"); // 等待结束,async.waitForFinished () QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, nullptr); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, [](QDBusPendingCallWatcher *call) { QDBusPendingReply<QString> reply = *call; if (!reply.isError()) { QString name= reply.argumentAt<0>(); qDebug()<<"QDBusPendingReply name = "<<name; } call->deleteLater(); }); return app.exec(); }
示例:注册一个服务,分别调用set、get方法:
#include <QApplication> #include <QDebug> #include <DListView> #include <QtDBus> #include <QObject> #include <DApplication> #include <DDBusSender> #include <QDBusPendingCallWatcher> DWIDGET_USE_NAMESPACE void testFunc1() { if (!QDBusConnection::connectToBus(QDBusConnection::SessionBus,QString("c2box")).isConnected()) { fprintf(stderr, "Cannot connect to the D-Bus session bus.\n" "To start it, run:\n" "\teval `dbus-launch --auto-syntax`\n"); return; } if (!QDBusConnection::connectToBus(QDBusConnection::SessionBus,QString("c2box")).registerService("com.alex.one")) { fprintf(stderr, "%s\n", qPrintable(QDBusConnection::connectToBus(QDBusConnection::SessionBus,QString("c2box")).lastError().message())); exit(1); } // PlayerSetting* playerSetting = this; // 把playerSetting 作为interface,即把playerSetting对象中的 QDBusConnection::connectToBus(QDBusConnection::SessionBus,QString("c2box")).registerObject("alex.one.test", nullptr, QDBusConnection::ExportAllSlots); // http://seen.blog.chinaunix.net/uid-23023613-id-219100.html } class test: public QObject { Q_OBJECT //定义Interface名称为com.scorpio.test.value // Q_CLASSINFO("D-Bus Interface", "com.scorpio.test.value") Q_CLASSINFO("D-Bus Interface", "com.alex.daemon.Grub2.EditAuth") public: // test(int value) test() { EditShellAuthUsers << "alex" << "one"; } public slots: QString SetGrubEditShellAuth(const QString & user, const QString & passwd) { // EditShellAuthUsers << user.toString() << passwd.toString(); EditShellAuthUsers << user << passwd; return QDBusError(QDBusError::NoError, "").errorString(QDBusError::NoError); } QStringList GetGrubEditShellAuth() { return EditShellAuthUsers; } QDBusError DisableGrubEditShellAuth(const QString &user) { return QDBusError(QDBusError::NoError, ""); } private: // int m_value; QStringList EditShellAuthUsers; private: std::shared_ptr<DDBusData> m_dbusData; }; void testFunc2() { // 建立到session bus的连接 QDBusConnection connection = QDBusConnection::sessionBus(); // 在 session bus 上注册名为 com.scorpio.test 的服务 if (!connection.registerService("com.alex.daemon.Grub2")) { qInfo() << "error:" << connection.lastError().message(); exit(-1); } test t; // 注册名为 /test/objects 的对象,把类 Object 所有槽函数导出为 object 的 method connection.registerObject("/test/objects", &t, QDBusConnection::ExportAllSlots); qApp->exec(); } void testFunc3() { #if 1 //构造一个method_call消息,服务名称为:com.scorpio.test,对象路径为:/test/objects //接口名称为com.scorpio.test.value,method名称为value QDBusMessage message = QDBusMessage::createMethodCall("com.alex.daemon.Grub2", "/test/objects", "com.alex.daemon.Grub2.EditAuth", "SetGrubEditShellAuth"); // message.setArguments({QVariant(QString("alex")), QVariant(QString("one"))}); message.setArguments({QString("alex"), QString("one")}); //发送消息 QDBusMessage response = QDBusConnection::sessionBus().call(message); if (! response.errorMessage().isEmpty()) { qInfo() << response.errorMessage(); } #endif // qApp->exec(); //} //void testFunc4() { //构造一个method_call消息,服务名称为:com.scorpio.test,对象路径为:/test/objects //接口名称为com.scorpio.test.value,method名称为value QDBusMessage message2 = QDBusMessage::createMethodCall("com.alex.daemon.Grub2", "/test/objects", "com.alex.daemon.Grub2.EditAuth", "GetGrubEditShellAuth"); //发送消息 QDBusMessage response2 = QDBusConnection::sessionBus().call(message2); //判断method是否被正确返回 if (response2.type() == QDBusMessage::ReplyMessage) { //从返回参数获取返回值 qInfo() << response2.arguments().takeFirst().toList(); // int value = response.arguments().takeFirst().toInt(); // qInfo() << QString("value = %1").arg(value); } else { // qInfo() << "method called failed!"; qInfo() << response2.errorMessage(); } qApp->exec(); } #include "main.moc" int main(int argc, char *argv[]) { QApplication app(argc, argv); #if 1 //! 1. 注册一个服务 // 建立到session bus的连接 QDBusConnection connection = QDBusConnection::sessionBus(); // 在 session bus 上注册名为 com.scorpio.test 的服务 if (!connection.registerService("com.alex.daemon.Grub2")) { qInfo() << "error:" << connection.lastError().message(); exit(-1); } test t; // 注册名为 /test/objects 的对象,把类 Object 所有槽函数导出为 object 的 method connection.registerObject("/test/objects", &t, QDBusConnection::ExportAllSlots); // testFunc1(); // testFunc2(); #endif //! 1. 分别调用 set 和 get 方法 testFunc3(); // testFunc4(); return app.exec(); }
打印预览模糊的解决方案:
高分屏下 pixmap 要设置devicePixRatio
#if 0 // 模糊 QPainter painter(_printer); QImage image ("/home/alex/Pictures/Wallpapers/144883671.jpg"); image = image.scaled(_printer->pageRect().size(), Qt::AspectRatioMode::KeepAspectRatio, Qt::SmoothTransformation); painter.drawImage(0, 0, image); #else // 不模糊 QImage test_image("/home/alex/Pictures/Wallpapers/144883671.jpg"); // 直接传递原始数据 但绘制进缩放后的rect中 const QRect &page_rect = _printer->pageRect(); QSize target_size = test_image.size(); QRect target_rect; target_size.scale(qMin(test_image.width(), page_rect.width()), qMin(test_image.height(), test_image.height()), Qt::KeepAspectRatio); target_rect.setSize(target_size); target_rect.moveCenter(page_rect.center()); QPainter painter(_printer); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); painter.drawImage(target_rect, test_image); #endif
QPaintDevice:
QSurface
qt拖放
qt winid
ShellSurface *ShellSurface::fromQtWinId(WId wid) { QWindow *window = nullptr; for (auto win : qApp->allWindows()) { if (win->winId() == wid) { window = win; break; } } if (!window) { return nullptr; } return fromWindow(window); }
qApp
void DSplitScreenWidget::disabledByScreenGeometryAndWindowSize(QWidgetList wList) { QDesktopWidget *desktop = qApp->desktop(); QWidget *window = this->window(); if (Q_LIKELY(desktop) && Q_LIKELY(window)) { QRect screenRect = desktop->screenGeometry(window); // 窗口尺寸大于屏幕分辨率时 禁用控件 if (screenRect.width() < window->minimumWidth() || screenRect.height() < window->minimumHeight()) for (QWidget *w : wList) w->setDisabled(true); } }
qt 日志系统
qt 状态机:
参考 参考 参考(快速上手) 知乎参考
qt的md5、toHex等操作:
qt的一些基础组件:
linux上的ipc机制:深度详解
qdbus:
参考(org.freedesktop.DBus.Introspectable.Introspect)
防火墙,使用Dbus命令
命令行参数的用法:
窗口坐标转换:
绘制背景色:
void SettingLabel::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setPen(Qt::NoPen); if (DApplicationHelper::instance()->themeType() == DApplicationHelper::LightType) { painter.setBrush(QColor(0, 0, 0, 0.03 * 255)); } else { painter.setBrush(QColor(255, 255, 255, 0.03 * 255)); } if (underMouse()) { painter.setBrush(Qt::red); } painter.drawRoundedRect(rect(), 0, 0); return QWidget::paintEvent(event); }
dock网络连接绘制hover:
--- a/plugins/bluetooth/componments/bluetoothapplet.cpp +++ b/plugins/bluetooth/componments/bluetoothapplet.cpp @@ -107,6 +107,9 @@ void SettingLabel::paintEvent(QPaintEvent *event) } else { painter.setBrush(QColor(255, 255, 255, 0.03 * 255)); } + if (underMouse()) { + painter.setBrush(Qt::red); + } painter.drawRoundedRect(rect(), 0, 0); return QWidget::paintEvent(event);
QMetaObjectBuilder
QEventDispatcher:
为QThread安装event dipatcher:参考
定时器的设计
运行lambda在任意线程中的讨论:
event_fd 与 qt 的事件机制:
参考(epoll 中weakUp用法的精妙之处)
combobox的绘制:
主要由这些地方控制:
ChameleonStyle::drawComboBoxLabel
SC_ComboBoxListBoxPopup
ChameleonStyle::drawMenuItem
弹出窗口其实就是个菜单
qeventdispathcer:
poll和epoll源码剖析
查看所有 fd 包括定时器的:
ls -l /proc/`pidof epoll_dispatcher`/fd/
定时器:
响应主题的变化:
void GraphicsView::changeEvent(QEvent *e) themeTypeChanged GraphicsView::onThemeTypeChanged
hook的用法:
+// HookOverride(&QtWaylandClient::QWaylandScreen::physicalSize, [](QtWaylandClient::QWaylandScreen *s){ +// QSizeF physicalSize = HookCall(s, &QtWaylandClient::QWaylandScreen::physicalSize); +// qInfo() << "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx physicalSize: " << physicalSize; +// return physicalSize; +// });
窗口阴影:
dtk已提供圆角的DArrowRectangle,参照如下 demo 设置进行。 设置为setArrowWidth(40), setArrowHeight(26); 细节与 徐冰倩 沟通 指定为 DArrowRectangle::FloatWindow 则没有窗口阴影效果,DArrowRectangle::FloatWiddget 有窗口阴影效果但是没有模糊效果。这是底层窗口的类型差异导致的。 有窗口阴影效果的时候在非特效模式下会有黑框需要特别注意。 代码改动地方较多,会涉及到网络模块的插件、拖动时的 tooltip 等,建议应用自己修改。 int main(int argc, char **argv) { QApplication app(argc, argv); DArrowRectangle *pRectangle_2 = new DArrowRectangle(DArrowRectangle::ArrowBottom, DArrowRectangle::FloatWindow); pRectangle_2->setAttribute(Qt::WA_DeleteOnClose); pRectangle_2->setWindowFlag(Qt::ToolTip); pRectangle_2->setMargin(10); pRectangle_2->setShadowXOffset(0); pRectangle_2->setShadowYOffset(6); pRectangle_2->setShadowBlurRadius(20); pRectangle_2->setRadius(18); // 设置尖角宽高 pRectangle_2->setArrowWidth(50); pRectangle_2->setArrowHeight(30); pRectangle_2->setRadiusArrowStyleEnable(true); // 改尖角为圆角 pRectangle_2->setBorderColor(QColor(0, 0, 0, qRound(0.05 * 255))); // 设置阴影颜色 pRectangle_2->setRadiusArrowStyleEnable(true); pRectangle_2->setWindowFlags(Qt::X11BypassWindowManagerHint); // 禁止移动 // pRectangle_2->setAttribute(Qt::WA_TranslucentBackground); QLabel *pContentWidget_2 = new QLabel("深度音乐"); pContentWidget_2->setAlignment(Qt::AlignCenter); pContentWidget_2->setFixedSize(90, 40); pRectangle_2->setContent(pContentWidget_2); pRectangle_2->show(100, 100); return app.exec(); }