From 1c14242f4e6e62766d2244482dacda781504de3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=AE=E7=94=9F=E8=8B=A5=E6=A2=A6?= <1070753498@qq.com> Date: Thu, 18 Apr 2024 19:08:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8E=BB=E6=8E=89=20=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- README.md | 45 +- common.pri | 4 +- doc/Qt-App.jpg | Bin 22046 -> 44710 bytes src/3rdparty/breakpad.cc | 8 +- src/apps/app/CMakeLists.txt | 2 +- src/apps/app/app.rc | 10 +- src/apps/app/main.cc | 23 +- src/apps/crashreport/app.rc | 10 +- src/apps/crashreport/crashwidgets.cc | 30 +- src/apps/crashreport/crashwidgets.hpp | 1 - src/apps/crashreport/main.cc | 26 +- src/core/corewidget.hpp | 2 +- src/extensionsystem/iplugin.h | 26 +- src/extensionsystem/pluginmanager.h | 12 +- src/extensionsystem/pluginspec.h | 20 +- src/gui/commonwidget.cc | 11 +- src/gui/waitwidget.cpp | 5 +- src/plugins/CMakeLists.txt | 6 +- src/plugins/aboutplugin/CMakeLists.txt | 6 + src/plugins/aboutplugin/aboutplugin.cc | 22 + src/plugins/aboutplugin/aboutplugin.hpp | 29 + src/plugins/aboutplugin/aboutplugin.json | 11 + src/plugins/aboutplugin/aboutplugin.pro | 22 + src/plugins/aboutplugin/aboutwidget.cc | 53 ++ src/plugins/aboutplugin/aboutwidget.hpp | 20 + src/plugins/coreplugin/coreplugin.json | 6 +- src/plugins/coreplugin/mainwindow.cpp | 73 +- src/plugins/coreplugin/mainwindow.h | 1 - src/plugins/coreplugin/plugindialog.cpp | 26 +- src/plugins/guiplugin/CMakeLists.txt | 5 + src/plugins/guiplugin/guiplugin.cc | 20 + src/plugins/guiplugin/guiplugin.hpp | 29 + src/plugins/guiplugin/guiplugin.json | 11 + src/plugins/guiplugin/guiplugin.pro | 23 + src/plugins/guiplugin/guiwidget.cc | 89 +++ src/plugins/guiplugin/guiwidget.hpp | 25 + src/plugins/hashplugin/hashplugin.cc | 2 +- src/plugins/hashplugin/hashplugin.json | 10 +- src/plugins/hashplugin/hashwidget.cc | 34 +- src/plugins/helloplugin/CMakeLists.txt | 4 + src/plugins/helloplugin/helloplugin.cc | 20 + src/plugins/helloplugin/helloplugin.hpp | 29 + src/plugins/helloplugin/helloplugin.json | 11 + src/plugins/helloplugin/helloplugin.pro | 21 + src/plugins/helloplugin/hellowidget.cc | 113 +++ src/plugins/helloplugin/hellowidget.hpp | 32 + src/plugins/plugins.pro | 8 +- src/plugins/serialplugin/CMakeLists.txt | 21 - src/plugins/serialplugin/serialplugin.cc | 20 - src/plugins/serialplugin/serialplugin.hpp | 29 - src/plugins/serialplugin/serialplugin.json | 11 - src/plugins/serialplugin/serialplugin.pro | 28 - src/plugins/serialplugin/serialport.cpp | 80 --- src/plugins/serialplugin/serialport.h | 33 - src/plugins/serialplugin/serialsettings.cc | 1 - src/plugins/serialplugin/serialsettings.hpp | 20 - src/plugins/serialplugin/serialwidget.cpp | 562 --------------- src/plugins/serialplugin/serialwidget.h | 39 - src/plugins/systeminfoplugin/CMakeLists.txt | 6 + .../systeminfoplugin/systeminfoplugin.cc | 20 + .../systeminfoplugin/systeminfoplugin.hpp | 29 + .../systeminfoplugin/systeminfoplugin.json | 11 + .../systeminfoplugin/systeminfoplugin.pro | 22 + .../systeminfoplugin/systeminfowidget.cc | 32 + .../systeminfoplugin/systeminfowidget.hpp | 20 + src/plugins/tcpplugin/CMakeLists.txt | 21 - src/plugins/tcpplugin/tcpclient.cpp | 119 ---- src/plugins/tcpplugin/tcpclient.h | 42 -- src/plugins/tcpplugin/tcpplugin.cc | 20 - src/plugins/tcpplugin/tcpplugin.hpp | 29 - src/plugins/tcpplugin/tcpplugin.json | 11 - src/plugins/tcpplugin/tcpplugin.pro | 28 - src/plugins/tcpplugin/tcpserver.cpp | 140 ---- src/plugins/tcpplugin/tcpserver.h | 39 - src/plugins/tcpplugin/tcpwidget.cpp | 671 ------------------ src/plugins/tcpplugin/tcpwidget.h | 48 -- src/resource/qss/carshdialog.css | 3 - src/resource/qss/common.css | 634 ++++++++++------- src/resource/qss/commonwidget.css | 201 +++--- src/resource/qss/corewidget.css | 6 - src/resource/qss/sidebarbutton.css | 62 +- src/resource/qss/specific.css | 13 + src/resource/resource.qrc | 3 +- src/utils/CMakeLists.txt | 2 + src/utils/appinfo.cc | 3 + src/utils/appinfo.hpp | 17 + src/utils/logasync.cpp | 15 +- src/utils/logasync.h | 2 +- src/utils/utils.cpp | 21 +- src/utils/utils.h | 3 +- src/utils/utils.pro | 2 + translations/qt-app_zh_CN.ts | 634 +++-------------- vcpkg.json | 2 +- 94 files changed, 1603 insertions(+), 3170 deletions(-) create mode 100644 src/plugins/aboutplugin/CMakeLists.txt create mode 100644 src/plugins/aboutplugin/aboutplugin.cc create mode 100644 src/plugins/aboutplugin/aboutplugin.hpp create mode 100644 src/plugins/aboutplugin/aboutplugin.json create mode 100644 src/plugins/aboutplugin/aboutplugin.pro create mode 100644 src/plugins/aboutplugin/aboutwidget.cc create mode 100644 src/plugins/aboutplugin/aboutwidget.hpp create mode 100644 src/plugins/guiplugin/CMakeLists.txt create mode 100644 src/plugins/guiplugin/guiplugin.cc create mode 100644 src/plugins/guiplugin/guiplugin.hpp create mode 100644 src/plugins/guiplugin/guiplugin.json create mode 100644 src/plugins/guiplugin/guiplugin.pro create mode 100644 src/plugins/guiplugin/guiwidget.cc create mode 100644 src/plugins/guiplugin/guiwidget.hpp create mode 100644 src/plugins/helloplugin/CMakeLists.txt create mode 100644 src/plugins/helloplugin/helloplugin.cc create mode 100644 src/plugins/helloplugin/helloplugin.hpp create mode 100644 src/plugins/helloplugin/helloplugin.json create mode 100644 src/plugins/helloplugin/helloplugin.pro create mode 100644 src/plugins/helloplugin/hellowidget.cc create mode 100644 src/plugins/helloplugin/hellowidget.hpp delete mode 100644 src/plugins/serialplugin/CMakeLists.txt delete mode 100644 src/plugins/serialplugin/serialplugin.cc delete mode 100644 src/plugins/serialplugin/serialplugin.hpp delete mode 100644 src/plugins/serialplugin/serialplugin.json delete mode 100644 src/plugins/serialplugin/serialplugin.pro delete mode 100644 src/plugins/serialplugin/serialport.cpp delete mode 100644 src/plugins/serialplugin/serialport.h delete mode 100644 src/plugins/serialplugin/serialsettings.cc delete mode 100644 src/plugins/serialplugin/serialsettings.hpp delete mode 100644 src/plugins/serialplugin/serialwidget.cpp delete mode 100644 src/plugins/serialplugin/serialwidget.h create mode 100644 src/plugins/systeminfoplugin/CMakeLists.txt create mode 100644 src/plugins/systeminfoplugin/systeminfoplugin.cc create mode 100644 src/plugins/systeminfoplugin/systeminfoplugin.hpp create mode 100644 src/plugins/systeminfoplugin/systeminfoplugin.json create mode 100644 src/plugins/systeminfoplugin/systeminfoplugin.pro create mode 100644 src/plugins/systeminfoplugin/systeminfowidget.cc create mode 100644 src/plugins/systeminfoplugin/systeminfowidget.hpp delete mode 100644 src/plugins/tcpplugin/CMakeLists.txt delete mode 100644 src/plugins/tcpplugin/tcpclient.cpp delete mode 100644 src/plugins/tcpplugin/tcpclient.h delete mode 100644 src/plugins/tcpplugin/tcpplugin.cc delete mode 100644 src/plugins/tcpplugin/tcpplugin.hpp delete mode 100644 src/plugins/tcpplugin/tcpplugin.json delete mode 100644 src/plugins/tcpplugin/tcpplugin.pro delete mode 100644 src/plugins/tcpplugin/tcpserver.cpp delete mode 100644 src/plugins/tcpplugin/tcpserver.h delete mode 100644 src/plugins/tcpplugin/tcpwidget.cpp delete mode 100644 src/plugins/tcpplugin/tcpwidget.h delete mode 100644 src/resource/qss/carshdialog.css delete mode 100644 src/resource/qss/corewidget.css create mode 100644 src/resource/qss/specific.css create mode 100644 src/utils/appinfo.cc create mode 100644 src/utils/appinfo.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 577bb35..5599867 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ endif() project( Qt-App - VERSION 0.0.1 + VERSION 0.1.1 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) diff --git a/README.md b/README.md index f0a98c5..dd578e8 100644 --- a/README.md +++ b/README.md @@ -25,26 +25,33 @@ ## 代码结构 -1. [3rdparty](3rdparty):第三方库; - 1. [qtlockedfile](3rdparty/qtlockedfile):Qt文件锁; - 2. [qtsingleapplication](3rdparty/qtsingleapplication):Qt单实例; - 3. [breakpad](3rdparty/breakpad.hpp):基于Google Breakpad封装的崩溃捕捉; -2. [aggregate](aggregate):聚合; -3. [apps](apps):应用程序; - 1. [app](apps/app):Qt-App; - 2. [crashreport](apps/crashreport):CrashReport; -4. [cmake](cmake):封装的CMake实用函数; +1. [cmake](cmake):封装的CMake实用函数; 1. [utils](cmake/utils.cmake):实用函数; -5. [core](core):插件都继承于此; -6. [extensionsystem](extensionsystem):插件系统,代码来自于Qt-Creator,做了一些修改; -7. [gui](gui):封装的界面组件; -8. [plugins](plugins):插件; - 1. [coreplugin](plugins/coreplugin):核心插件,主界面、菜单、工具栏、状态栏、设置、插件管理器等; - 2. [hashplugin](plugins/hashplugin):哈希插件,QT提供的哈希算法; - 3. [serialplugin](plugins/serialplugin):串口插件; - 4. [tcpplugin](plugins/tcpplugin):TCP插件; -9. [resource](resource):图片和QSS文件; -10. [utils](utils):工具函数封装; +2. [doc](doc):文档说明和图片; +3. [examples](examples):示例代码; +4. [packaging](packaging):打包和发布; +5. [src](src):源码; + 1. [3rdparty](src/3rdparty):第三方库; + 1. [qtlockedfile](src/3rdparty/qtlockedfile):Qt文件锁; + 2. [qtsingleapplication](src/3rdparty/qtsingleapplication):Qt单实例; + 3. [breakpad](src/3rdparty/breakpad.hpp):基于Google Breakpad封装的崩溃捕捉; + 2. [aggregate](src/aggregate):聚合; + 3. [apps](src/apps):应用程序; + 1. [app](src/apps/app):Qt-App; + 2. [crashreport](src/apps/crashreport):CrashReport; + 4. [core](src/core):插件都继承于此; + 5. [extensionsystem](src/extensionsystem):插件系统,代码来自于Qt-Creator,做了一些修改; + 6. [gui](src/gui):封装的界面组件; + 7. [plugins](src/plugins):插件; + 1. [aboutplugin](src/plugins/aboutplugin):关于插件; + 2. [coreplugin](src/plugins/coreplugin):核心插件,主界面、菜单、工具栏、状态栏、设置、插件管理器等; + 3. [guiplugin](src/plugins/guiplugin):GUI插件,一些基于QSS样式定制的GUI组件; + 4. [hashplugin](src/plugins/hashplugin):哈希插件,QT提供的哈希算法; + 5. [helloplugin](src/plugins/helloplugin):Hello插件,用于测试插件开发; + 6. [systeminfoplugin](src/plugins/systeminfoplugin):系统信息插件; + 8. [resource](resource):图片和QSS文件; + 9. [utils](utils):工具函数封装; +6. [translations](translations):翻译文件; ## 问题和备注 diff --git a/common.pri b/common.pri index cc33e6d..c25022c 100644 --- a/common.pri +++ b/common.pri @@ -34,12 +34,12 @@ defineReplace(replaceLibName) { } isEmpty(RC_LANG): RC_LANG = 0x0004 -isEmpty(VERSION): VERSION = 0.0.0.1 +isEmpty(VERSION): VERSION = 0.1.1.0 CONFIG += skip_target_version_ext isEmpty(QMAKE_TARGET_COMPANY): QMAKE_TARGET_COMPANY = The Youth. isEmpty(QMAKE_TARGET_DESCRIPTION): QMAKE_TARGET_DESCRIPTION = Qt-App -isEmpty(QMAKE_TARGET_COPYRIGHT): QMAKE_TARGET_COPYRIGHT = Copyright (C) 2023 Youth. +isEmpty(QMAKE_TARGET_COPYRIGHT): QMAKE_TARGET_COPYRIGHT = Copyright (C) 2017-2024 Youth. isEmpty(QMAKE_TARGET_PRODUCT): QMAKE_TARGET_PRODUCT = Qt-App isEmpty(QMAKE_TARGET_ORIGINAL_FILENAME): QMAKE_TARGET_ORIGINAL_FILENAME = Qt-App isEmpty(QMAKE_TARGET_INTERNALNAME): QMAKE_TARGET_INTERNALNAME = Qt-App diff --git a/doc/Qt-App.jpg b/doc/Qt-App.jpg index 73d09116a46879fbc67dccdf193db2bf3e3adb57..eb5723ad07a3015602373d39092a603ce7ab3d25 100644 GIT binary patch literal 44710 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 PdltYiCtAMxGWLG}xw-+I literal 22046 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 diff --git a/src/3rdparty/breakpad.cc b/src/3rdparty/breakpad.cc index e463e92..40a41a7 100644 --- a/src/3rdparty/breakpad.cc +++ b/src/3rdparty/breakpad.cc @@ -21,7 +21,7 @@ namespace Utils { -QString getCrashPath() +auto getCrashPath() -> QString { const QString path = Utils::getConfigPath() + "/crashes"; Utils::generateDirectorys(path); @@ -52,12 +52,12 @@ void createEnvironment() // 程序崩溃回调函数; #if defined(Q_OS_WIN) -bool callback(const wchar_t *dump_path, +auto callback(const wchar_t *dump_path, const wchar_t *id, void *, EXCEPTION_POINTERS *, MDRawAssertionInfo *, - bool succeeded) + bool succeeded) -> bool { if (succeeded) { qInfo() << "Create dump file success:" << QString::fromWCharArray(dump_path) @@ -126,7 +126,7 @@ struct BreakPad::BreakPadPrivate QScopedPointer exceptionHandlerPtr; }; -BreakPad *BreakPad::instance() +auto BreakPad::instance() -> BreakPad * { static BreakPad breakPad; return &breakPad; diff --git a/src/apps/app/CMakeLists.txt b/src/apps/app/CMakeLists.txt index cbe4a5a..abb35da 100644 --- a/src/apps/app/CMakeLists.txt +++ b/src/apps/app/CMakeLists.txt @@ -12,7 +12,7 @@ elseif(CMAKE_HOST_APPLE) MACOSX_BUNDLE_EXECUTABLE_NAME "${PROJECT_NAME}" MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}" MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}" - MACOSX_BUNDLE_COPYRIGHT "(C) 2023 Youth" + MACOSX_BUNDLE_COPYRIGHT "(C) 2024 Youth" MACOSX_BUNDLE_GUI_IDENTIFIER "com.Youth.${PROJECT_NAME}" MACOSX_BUNDLE_ICON_FILE app.icns MACOSX_BUNDLE_INFO_STRING "APPL???") diff --git a/src/apps/app/app.rc b/src/apps/app/app.rc index 90eed1b..e651d3e 100644 --- a/src/apps/app/app.rc +++ b/src/apps/app/app.rc @@ -3,8 +3,8 @@ IDI_ICON1 ICON "app.ico" VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,0,0,1 - PRODUCTVERSION 0,0,0,1 + FILEVERSION 0,1,1,0 + PRODUCTVERSION 0,1,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS VS_FF_DEBUG @@ -21,11 +21,11 @@ VS_VERSION_INFO VERSIONINFO BEGIN VALUE "CompanyName", "The Youth.\0" VALUE "FileDescription", "Qt-App\0" - VALUE "FileVersion", "0.0.0.1\0" - VALUE "LegalCopyright", "Copyright (C) 2023 Youth.\0" + VALUE "FileVersion", "0.1.1.0\0" + VALUE "LegalCopyright", "Copyright (C) 2017-2024 Youth.\0" VALUE "OriginalFilename", "Qt-App\0" VALUE "ProductName", "Qt-App\0" - VALUE "ProductVersion", "0.0.0.1\0" + VALUE "ProductVersion", "0.1.1.0\0" VALUE "InternalName", "Qt-App\0" VALUE "Comments", "Qt-App\0" VALUE "LegalTrademarks", "Youth\0" diff --git a/src/apps/app/main.cc b/src/apps/app/main.cc index 684befb..b1ecf70 100644 --- a/src/apps/app/main.cc +++ b/src/apps/app/main.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -12,8 +13,6 @@ #include #include -#define AppName "Qt-App" - void initResource() { Resource r; // 这样才可以使用qrc @@ -24,12 +23,12 @@ void initResource() void setAppInfo() { - qApp->setApplicationVersion("0.0.1"); - qApp->setApplicationDisplayName(AppName); - qApp->setApplicationName(AppName); - qApp->setDesktopFileName(AppName); - qApp->setOrganizationDomain("Youth"); - qApp->setOrganizationName("Youth"); + qApp->setApplicationVersion(Utils::version.toString()); + qApp->setApplicationDisplayName(Utils::appName); + qApp->setApplicationName(Utils::appName); + qApp->setDesktopFileName(Utils::appName); + qApp->setOrganizationDomain(Utils::organizationDomain); + qApp->setOrganizationName(Utils::organzationName); qApp->setWindowIcon(QIcon(":/icon/icon/app.png")); } @@ -38,8 +37,7 @@ void setQss() Utils::setQSS({":/qss/qss/common.css", ":/qss/qss/commonwidget.css", ":/qss/qss/sidebarbutton.css", - ":/qss/qss/carshdialog.css", - ":/qss/qss/corewidget.css"}); + ":/qss/qss/specific.css"}); } auto main(int argc, char *argv[]) -> int @@ -53,7 +51,7 @@ auto main(int argc, char *argv[]) -> int #endif Utils::setHighDpiEnvironmentVariable(); SharedTools::QtSingleApplication::setAttribute(Qt::AA_ShareOpenGLContexts); - SharedTools::QtSingleApplication app(AppName, argc, argv); + SharedTools::QtSingleApplication app(Utils::appName, argc, argv); if (app.isRunning()) { qWarning() << "This is already running"; if (app.sendMessage("raise_window_noop", 5000)) { @@ -88,8 +86,7 @@ auto main(int argc, char *argv[]) -> int log->startWork(); initResource(); - Utils::printBuildInfo(); - //Utils::setUTF8Code(); + qInfo().noquote() << "\n\n" + Utils::systemInfo() + "\n\n"; Utils::setGlobalThreadPoolMaxSize(); Utils::loadFonts(app.applicationDirPath() + "/fonts"); setQss(); diff --git a/src/apps/crashreport/app.rc b/src/apps/crashreport/app.rc index 90eed1b..9f12bae 100644 --- a/src/apps/crashreport/app.rc +++ b/src/apps/crashreport/app.rc @@ -3,8 +3,8 @@ IDI_ICON1 ICON "app.ico" VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,0,0,1 - PRODUCTVERSION 0,0,0,1 + FILEVERSION 0,1,1,0 + PRODUCTVERSION 0,1,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS VS_FF_DEBUG @@ -21,11 +21,11 @@ VS_VERSION_INFO VERSIONINFO BEGIN VALUE "CompanyName", "The Youth.\0" VALUE "FileDescription", "Qt-App\0" - VALUE "FileVersion", "0.0.0.1\0" - VALUE "LegalCopyright", "Copyright (C) 2023 Youth.\0" + VALUE "FileVersion", "0.1.1.0\0" + VALUE "LegalCopyright", "Copyright (C) 2024 Youth.\0" VALUE "OriginalFilename", "Qt-App\0" VALUE "ProductName", "Qt-App\0" - VALUE "ProductVersion", "0.0.0.1\0" + VALUE "ProductVersion", "0.1.1.0\0" VALUE "InternalName", "Qt-App\0" VALUE "Comments", "Qt-App\0" VALUE "LegalTrademarks", "Youth\0" diff --git a/src/apps/crashreport/crashwidgets.cc b/src/apps/crashreport/crashwidgets.cc index dd048ae..446e17d 100644 --- a/src/apps/crashreport/crashwidgets.cc +++ b/src/apps/crashreport/crashwidgets.cc @@ -18,7 +18,7 @@ void openDir(const QString &path) class CrashWidgets::CrashWidgetsPrivate { public: - CrashWidgetsPrivate(CrashWidgets *q) + explicit CrashWidgetsPrivate(CrashWidgets *q) : q_ptr(q) { auto appArgs = qApp->arguments(); @@ -45,7 +45,7 @@ CrashWidgets::CrashWidgets(QWidget *parent) resize(450, 450); } -CrashWidgets::~CrashWidgets() {} +CrashWidgets::~CrashWidgets() = default; void CrashWidgets::onOpenCrashPath() { @@ -58,24 +58,24 @@ void CrashWidgets::onRestart() QProcess::startDetached(d_ptr->appPath, d_ptr->args); } -void CrashWidgets::onQuit() -{ - QMetaObject::invokeMethod(qApp, &QApplication::quit, Qt::QueuedConnection); -} - void CrashWidgets::setupUI() { - auto crashButton = new QPushButton(tr("Path of Crash File"), this); - auto restartButton = new QPushButton(tr("Restart"), this); - auto closeButton = new QPushButton(tr("Close"), this); + auto *crashButton = new QPushButton(tr("Path of Crash File"), this); + auto *restartButton = new QPushButton(tr("Restart"), this); + auto *closeButton = new QPushButton(tr("Close"), this); crashButton->setObjectName("BlueButton"); restartButton->setObjectName("BlueButton"); closeButton->setObjectName("BlueButton"); connect(crashButton, &QPushButton::clicked, this, &CrashWidgets::onOpenCrashPath); connect(restartButton, &QPushButton::clicked, this, &CrashWidgets::onRestart); - connect(closeButton, &QPushButton::clicked, this, &CrashWidgets::onQuit); - - auto crashLabel = new QLabel(this); + connect( + closeButton, + &QPushButton::clicked, + this, + [] { Utils::quitApplication(); }, + Qt::QueuedConnection); + + auto *crashLabel = new QLabel(this); crashLabel->setObjectName("CrashLabel"); crashLabel->setWordWrap(true); crashLabel->setAlignment(Qt::AlignCenter); @@ -87,8 +87,8 @@ void CrashWidgets::setupUI() "Contact Me - Email: \n" "1070753498@qq.com")); - auto widget = new QWidget(this); - auto layout = new QVBoxLayout(widget); + auto *widget = new QWidget(this); + auto *layout = new QVBoxLayout(widget); layout->addStretch(); layout->addWidget(crashLabel); layout->addStretch(); diff --git a/src/apps/crashreport/crashwidgets.hpp b/src/apps/crashreport/crashwidgets.hpp index 293ecdf..b5152b0 100644 --- a/src/apps/crashreport/crashwidgets.hpp +++ b/src/apps/crashreport/crashwidgets.hpp @@ -15,7 +15,6 @@ class CrashWidgets : public GUI::CommonWidget private slots: void onOpenCrashPath(); void onRestart(); - void onQuit(); private: void setupUI(); diff --git a/src/apps/crashreport/main.cc b/src/apps/crashreport/main.cc index c85bdd3..cbf7eed 100644 --- a/src/apps/crashreport/main.cc +++ b/src/apps/crashreport/main.cc @@ -3,6 +3,7 @@ #include <3rdparty/breakpad.hpp> #include <3rdparty/qtsingleapplication/qtsingleapplication.h> #include +#include #include #include #include @@ -10,8 +11,6 @@ #include #include -#define AppName "CrashReport" - void initResource() { Resource r; // 这样才可以使用qrc @@ -22,19 +21,21 @@ void initResource() void setAppInfo() { - qApp->setApplicationVersion("0.0.1"); - qApp->setApplicationDisplayName(AppName); - qApp->setApplicationName(AppName); - qApp->setDesktopFileName(AppName); - qApp->setOrganizationDomain("Youth"); - qApp->setOrganizationName("Youth"); + qApp->setApplicationVersion(Utils::version.toString()); + qApp->setApplicationDisplayName(Utils::crashName); + qApp->setApplicationName(Utils::crashName); + qApp->setDesktopFileName(Utils::crashName); + qApp->setOrganizationDomain(Utils::organizationDomain); + qApp->setOrganizationName(Utils::organzationName); qApp->setWindowIcon(QIcon(":/icon/icon/crash.png")); } void setQss() { - Utils::setQSS( - {":/qss/qss/common.css", ":/qss/qss/commonwidget.css", ":/qss/qss/carshdialog.css"}); + Utils::setQSS({":/qss/qss/common.css", + ":/qss/qss/commonwidget.css", + ":/qss/qss/sidebarbutton.css", + ":/qss/qss/specific.css"}); } auto main(int argc, char *argv[]) -> int @@ -48,7 +49,7 @@ auto main(int argc, char *argv[]) -> int #endif Utils::setHighDpiEnvironmentVariable(); SharedTools::QtSingleApplication::setAttribute(Qt::AA_ShareOpenGLContexts); - SharedTools::QtSingleApplication app(AppName, argc, argv); + SharedTools::QtSingleApplication app(Utils::crashName, argc, argv); if (app.isRunning()) { qWarning() << "This is already running"; if (app.sendMessage("raise_window_noop", 5000)) { @@ -79,8 +80,7 @@ auto main(int argc, char *argv[]) -> int log->startWork(); initResource(); - Utils::printBuildInfo(); - //Utils::setUTF8Code(); + qInfo().noquote() << "\n\n" + Utils::systemInfo() + "\n\n"; Utils::setGlobalThreadPoolMaxSize(); Utils::loadFonts(app.applicationDirPath() + "/fonts"); setQss(); diff --git a/src/core/corewidget.hpp b/src/core/corewidget.hpp index 62c4fa0..31337c3 100644 --- a/src/core/corewidget.hpp +++ b/src/core/corewidget.hpp @@ -13,7 +13,7 @@ class CORE_EXPORT CoreWidget : public QObject { Q_OBJECT public: - enum Type { Tool, About }; + enum Type { Main, Help }; explicit CoreWidget(QObject *parent = nullptr); ~CoreWidget() override; diff --git a/src/extensionsystem/iplugin.h b/src/extensionsystem/iplugin.h index 06b5634..27cf29b 100644 --- a/src/extensionsystem/iplugin.h +++ b/src/extensionsystem/iplugin.h @@ -11,7 +11,9 @@ namespace ExtensionSystem { -namespace Internal { class IPluginPrivate; } +namespace Internal { +class IPluginPrivate; +} using TestCreator = std::function; @@ -20,10 +22,7 @@ class EXTENSIONSYSTEM_EXPORT IPlugin : public QObject Q_OBJECT public: - enum ShutdownFlag { - SynchronousShutdown, - AsynchronousShutdown - }; + enum ShutdownFlag { SynchronousShutdown, AsynchronousShutdown }; IPlugin(); ~IPlugin() override; @@ -33,18 +32,23 @@ class EXTENSIONSYSTEM_EXPORT IPlugin : public QObject virtual auto delayedInitialize() -> bool { return false; } virtual auto aboutToShutdown() -> ShutdownFlag { return SynchronousShutdown; } virtual auto remoteCommand(const QStringList & /* options */, - const QString & /* workingDirectory */, - const QStringList & /* arguments */) -> QObject * { return nullptr; } - + const QString & /* workingDirectory */, + const QStringList & /* arguments */) -> QObject * + { + return nullptr; + } // Deprecated in 10.0, use addTest() - [[nodiscard]] virtual QVector createTestObjects() const; + [[nodiscard]] virtual auto createTestObjects() const -> QVector; protected: virtual void initialize() {} - template - void addTest(Args && ...args) { addTestCreator([args...] { return new Test(args...); }); } + template + void addTest(Args &&...args) + { + addTestCreator([args...] { return new Test(args...); }); + } void addTestCreator(const TestCreator &creator); signals: diff --git a/src/extensionsystem/pluginmanager.h b/src/extensionsystem/pluginmanager.h index 4da1118..2bbda7e 100644 --- a/src/extensionsystem/pluginmanager.h +++ b/src/extensionsystem/pluginmanager.h @@ -36,7 +36,7 @@ class EXTENSIONSYSTEM_EXPORT PluginManager : public QObject // Object pool operations static void addObject(QObject *obj); static void removeObject(QObject *obj); - static QVector allObjects(); + static auto allObjects() -> QVector; static auto listLock() -> QReadWriteLock *; // This is useful for soft dependencies using pure interfaces. @@ -65,7 +65,7 @@ class EXTENSIONSYSTEM_EXPORT PluginManager : public QObject } template - static QVector getObjects() + static auto getObjects() -> QVector { QReadLocker lock(listLock()); QVector results; @@ -79,7 +79,7 @@ class EXTENSIONSYSTEM_EXPORT PluginManager : public QObject } template - static QVector getObjects(Predicate predicate) + static auto getObjects(Predicate predicate) -> QVector { QReadLocker lock(listLock()); QVector results; @@ -95,14 +95,14 @@ class EXTENSIONSYSTEM_EXPORT PluginManager : public QObject static auto getObjectByName(const QString &name) -> QObject *; // Plugin operations - static QVector loadQueue(); + static auto loadQueue() -> QVector; static void loadPlugins(); static auto pluginPaths() -> QStringList; static void setPluginPaths(const QStringList &paths); static auto pluginIID() -> QString; static void setPluginIID(const QString &iid); - static const QVector plugins(); - static QHash> pluginCollections(); + static auto plugins() -> const QVector; + static auto pluginCollections() -> QHash>; static auto hasError() -> bool; static auto allErrors() -> const QStringList; static auto pluginsRequiringPlugin(PluginSpec *spec) -> const QSet; diff --git a/src/extensionsystem/pluginspec.h b/src/extensionsystem/pluginspec.h index 0419f70..d0fa42c 100644 --- a/src/extensionsystem/pluginspec.h +++ b/src/extensionsystem/pluginspec.h @@ -22,22 +22,20 @@ class OptionsParser; class PluginSpecPrivate; class PluginManagerPrivate; -} // Internal +} // namespace Internal class IPlugin; class PluginView; struct EXTENSIONSYSTEM_EXPORT PluginDependency { - enum Type { - Required, - Optional, - Test - }; + enum Type { Required, Optional, Test }; - PluginDependency() : type(Required) {} + PluginDependency() + : type(Required) + {} - friend size_t qHash(const PluginDependency &value); + friend auto qHash(const PluginDependency &value) -> size_t; QString name; QString version; @@ -56,7 +54,7 @@ struct EXTENSIONSYSTEM_EXPORT PluginArgumentDescription class EXTENSIONSYSTEM_EXPORT PluginSpec { public: - enum State { Invalid, Read, Resolved, Loaded, Initialized, Running, Stopped, Deleted}; + enum State { Invalid, Read, Resolved, Loaded, Initialized, Running, Stopped, Deleted }; ~PluginSpec(); @@ -82,11 +80,11 @@ class EXTENSIONSYSTEM_EXPORT PluginSpec [[nodiscard]] auto isEnabledIndirectly() const -> bool; [[nodiscard]] auto isForceEnabled() const -> bool; [[nodiscard]] auto isForceDisabled() const -> bool; - [[nodiscard]] QVector dependencies() const; + [[nodiscard]] auto dependencies() const -> QVector; [[nodiscard]] auto metaData() const -> QJsonObject; using PluginArgumentDescriptions = QVector; - [[nodiscard]] PluginArgumentDescriptions argumentDescriptions() const; + [[nodiscard]] auto argumentDescriptions() const -> PluginArgumentDescriptions; // other information, valid after 'Read' state is reached [[nodiscard]] auto location() const -> QString; diff --git a/src/gui/commonwidget.cc b/src/gui/commonwidget.cc index 7ba8648..7c1180e 100644 --- a/src/gui/commonwidget.cc +++ b/src/gui/commonwidget.cc @@ -9,7 +9,7 @@ namespace GUI { class CommonWidget::CommonWidgetPrivate { public: - CommonWidgetPrivate(CommonWidget *q) + explicit CommonWidgetPrivate(CommonWidget *q) : q_ptr(q) { titleButton = new QPushButton(qApp->windowIcon(), qAppName(), q_ptr); @@ -70,7 +70,7 @@ class CommonWidget::CommonWidgetPrivate titleWidget = new QWidget(q_ptr); titleWidget->setObjectName("TitleWidget"); auto *layout = new QHBoxLayout(titleWidget); - layout->setContentsMargins(5, 5, 5, 5); + layout->setContentsMargins(6, 6, 6, 6); layout->setSpacing(10); layout->addWidget(titleButton); layout->addStretch(); @@ -137,13 +137,14 @@ class CommonWidget::CommonWidgetPrivate ":/icon/icon/mactitle/titlebutton-close-active-alt@2.png"*/}, q_ptr); - const QSize size{16, 16}; + const QSize size{18, 18}; + titleButton->setIconSize(size); minButton->setFixedSize(size); maxButton->setFixedSize(size); restoreButton->setFixedSize(size); closeButton->setFixedSize(size); - const auto objectName("TitleButton"); + const auto *const objectName("TitleButton"); titleButton->setObjectName(objectName); minButton->setObjectName(objectName); maxButton->setObjectName(objectName); @@ -226,7 +227,7 @@ void CommonWidget::setShadowPadding(int shadowPadding) d_ptr->shadowPadding = shadowPadding; } -int CommonWidget::shadowPadding() +auto CommonWidget::shadowPadding() -> int { return d_ptr->shadowPadding; } diff --git a/src/gui/waitwidget.cpp b/src/gui/waitwidget.cpp index 7a58d1f..c927d2f 100644 --- a/src/gui/waitwidget.cpp +++ b/src/gui/waitwidget.cpp @@ -9,10 +9,11 @@ namespace GUI { class WaitWidget::WaitWidgetPrivate { public: - WaitWidgetPrivate(WaitWidget *q) + explicit WaitWidgetPrivate(WaitWidget *q) : q_ptr(q) { processBar = new QProgressBar(q_ptr); + processBar->setMaximumHeight(5); processBar->setTextVisible(false); processBar->setRange(0, 100); @@ -21,7 +22,7 @@ class WaitWidget::WaitWidgetPrivate void setupUI() { - auto layout = new QHBoxLayout(q_ptr); + auto *layout = new QHBoxLayout(q_ptr); layout->setContentsMargins(QMargins()); layout->setSpacing(0); layout->addWidget(processBar); diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 4ddec5f..6673044 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -1,4 +1,6 @@ add_subdirectory(coreplugin) -add_subdirectory(tcpplugin) -add_subdirectory(serialplugin) add_subdirectory(hashplugin) +add_subdirectory(systeminfoplugin) +add_subdirectory(helloplugin) +add_subdirectory(aboutplugin) +add_subdirectory(guiplugin) diff --git a/src/plugins/aboutplugin/CMakeLists.txt b/src/plugins/aboutplugin/CMakeLists.txt new file mode 100644 index 0000000..42113a1 --- /dev/null +++ b/src/plugins/aboutplugin/CMakeLists.txt @@ -0,0 +1,6 @@ +set(PROJECT_SOURCES aboutplugin.cc aboutplugin.hpp aboutwidget.cc + aboutwidget.hpp) + +add_custom_plugin(aboutplugin ${PROJECT_SOURCES}) +target_link_libraries(aboutplugin PRIVATE core extensionsystem utils + Qt6::Widgets) diff --git a/src/plugins/aboutplugin/aboutplugin.cc b/src/plugins/aboutplugin/aboutplugin.cc new file mode 100644 index 0000000..cc26a8b --- /dev/null +++ b/src/plugins/aboutplugin/aboutplugin.cc @@ -0,0 +1,22 @@ +#include "aboutplugin.hpp" +#include "aboutwidget.hpp" + +#include + +namespace Plugin { + +AboutPluginWidget::AboutPluginWidget(QObject *parent) +{ + auto *aboutWidget = new AboutWidget; + aboutWidget->setObjectName("AboutWidget"); + setWidget(aboutWidget); + setButton(new QPushButton(tr("About")), Core::CoreWidget::Help); +} + +bool AboutPlugin::initialize(const QStringList &arguments, QString *errorString) +{ + addObject(new AboutPluginWidget(this)); + return true; +} + +} // namespace Plugin diff --git a/src/plugins/aboutplugin/aboutplugin.hpp b/src/plugins/aboutplugin/aboutplugin.hpp new file mode 100644 index 0000000..3f923cc --- /dev/null +++ b/src/plugins/aboutplugin/aboutplugin.hpp @@ -0,0 +1,29 @@ +#ifndef ABOUTPLUGIN_HPP +#define ABOUTPLUGIN_HPP + +#include +#include + +namespace Plugin { + +class AboutPluginWidget : public Core::CoreWidget +{ + Q_OBJECT +public: + explicit AboutPluginWidget(QObject *parent = nullptr); +}; + +class AboutPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "Youth.Qt.plugin" FILE "aboutplugin.json") +public: + AboutPlugin() = default; + + auto initialize(const QStringList &arguments, QString *errorString) -> bool override; + void extensionsInitialized() override {} +}; + +} // namespace Plugin + +#endif // ABOUTPLUGIN_HPP diff --git a/src/plugins/aboutplugin/aboutplugin.json b/src/plugins/aboutplugin/aboutplugin.json new file mode 100644 index 0000000..117537c --- /dev/null +++ b/src/plugins/aboutplugin/aboutplugin.json @@ -0,0 +1,11 @@ +{ + "Name": "AboutPlugin", + "Version": "0.1.1", + "CompatVersion": "0.1.1", + "Required": false, + "Vendor": "Youth", + "Copyright": "(C) 2024 The Youth Ltd", + "License": "GNU General Public License v3.0", + "Category": "Help", + "Description": "About" +} \ No newline at end of file diff --git a/src/plugins/aboutplugin/aboutplugin.pro b/src/plugins/aboutplugin/aboutplugin.pro new file mode 100644 index 0000000..de9d5ce --- /dev/null +++ b/src/plugins/aboutplugin/aboutplugin.pro @@ -0,0 +1,22 @@ +include(../plugins.pri) + +QT += widgets + +DEFINES += ABOUTPLUGIN_LIBRARY +TARGET = $$replaceLibName(aboutplugin) + +LIBS += \ + -l$$replaceLibName(core) \ + -l$$replaceLibName(extensionsystem) \ + -l$$replaceLibName(utils) + +SOURCES += \ + aboutplugin.cc \ + aboutwidget.cc + +HEADERS += \ + aboutplugin.hpp \ + aboutwidget.hpp + +DISTFILES += \ + aboutplugin.json diff --git a/src/plugins/aboutplugin/aboutwidget.cc b/src/plugins/aboutplugin/aboutwidget.cc new file mode 100644 index 0000000..5366729 --- /dev/null +++ b/src/plugins/aboutplugin/aboutwidget.cc @@ -0,0 +1,53 @@ +#include "aboutwidget.hpp" + +#include +#include + +#include + +namespace Plugin { + +AboutWidget::AboutWidget(QWidget *parent) + : QWidget{parent} +{ + setupUI(); +} + +void AboutWidget::setupUI() +{ + auto text = QString("%1 %2
") + .arg(Utils::appName, Utils::version.toString()); + + auto *textLayout = new QVBoxLayout; + textLayout->setSpacing(20); + textLayout->addStretch(); + textLayout->addWidget(new QLabel{text, this}); + textLayout->addWidget(new QLabel{Utils::systemInfo(), this}); + textLayout->addWidget(new QLabel{Utils::copyright, this}); + textLayout->addStretch(); + + auto *iconButton = new QToolButton(this); + iconButton->setIconSize({64, 64}); + iconButton->setIcon(qApp->windowIcon()); + + auto *topLayout = new QHBoxLayout; + topLayout->setSpacing(20); + topLayout->addWidget(iconButton); + topLayout->addStretch(); + topLayout->addLayout(textLayout); + topLayout->addStretch(); + + auto *aboutQtButton = new QToolButton(this); + aboutQtButton->setText(tr("About Qt")); + connect(aboutQtButton, &QToolButton::clicked, qApp, &QApplication::aboutQt); + + auto *layout = new QVBoxLayout(this); + layout->setContentsMargins(30, 30, 30, 30); + layout->addStretch(); + layout->addLayout(topLayout); + layout->addStretch(); + layout->addWidget(aboutQtButton, 0, Qt::AlignCenter); +} + +} // namespace Plugin diff --git a/src/plugins/aboutplugin/aboutwidget.hpp b/src/plugins/aboutplugin/aboutwidget.hpp new file mode 100644 index 0000000..d0b4b85 --- /dev/null +++ b/src/plugins/aboutplugin/aboutwidget.hpp @@ -0,0 +1,20 @@ +#ifndef ABOUTWIDGET_HPP +#define ABOUTWIDGET_HPP + +#include + +namespace Plugin { + +class AboutWidget : public QWidget +{ + Q_OBJECT +public: + explicit AboutWidget(QWidget *parent = nullptr); + +private slots: + void setupUI(); +}; + +} // namespace Plugin + +#endif // ABOUTWIDGET_HPP diff --git a/src/plugins/coreplugin/coreplugin.json b/src/plugins/coreplugin/coreplugin.json index 3c74053..ffb194c 100644 --- a/src/plugins/coreplugin/coreplugin.json +++ b/src/plugins/coreplugin/coreplugin.json @@ -1,10 +1,10 @@ { "Name": "CorePlugin", - "Version": "0.0.1", - "CompatVersion": "0.0.1", + "Version": "0.1.1", + "CompatVersion": "0.1.1", "Required": true, "Vendor": "Youth", - "Copyright": "(C) 2023 The Youth Ltd", + "Copyright": "(C) 2024 The Youth Ltd", "License": "GNU General Public License v3.0", "Category": "Core", "Description": "MainWindow" diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp index 9250126..b76314d 100644 --- a/src/plugins/coreplugin/mainwindow.cpp +++ b/src/plugins/coreplugin/mainwindow.cpp @@ -36,7 +36,6 @@ class MainWindow::MainWindowPrivate void setupUI() { - initToolBar(); createSystemTray(); setInitWidget(tr("Hello World!")); @@ -62,25 +61,6 @@ class MainWindow::MainWindowPrivate QVBoxLayout *vLayoutGroup3; private: - void initToolBar() - { - auto *configButton = new QPushButton(q_ptr); - configButton->setText(QCoreApplication::translate("MainWindowPrivate", "Settings")); - configButton->setToolTip(QCoreApplication::translate("MainWindowPrivate", "Settings")); - auto *configWidget = new ConfigWidget(q_ptr); - stackedWidget->addWidget(configWidget); - QObject::connect(configButton, &QPushButton::clicked, q_ptr, [=] { - stackedWidget->setCurrentWidget(configWidget); - }); - - auto *titleBar = new QWidget(q_ptr); - auto *layout = new QHBoxLayout(titleBar); - layout->setContentsMargins(QMargins()); - layout->setSpacing(10); - layout->addWidget(configButton); - q_ptr->setTitleBar(titleBar); - } - void createSystemTray() { if (!QSystemTrayIcon::isSystemTrayAvailable()) { @@ -91,7 +71,8 @@ class MainWindow::MainWindowPrivate } auto *menu = new QMenu(q_ptr); - menu->addAction(tr("Quit"), q_ptr, &MainWindow::onQuit); + menu->addAction( + tr("Quit"), q_ptr, [] { Utils::quitApplication(); }, Qt::QueuedConnection); auto *trayIcon = new QSystemTrayIcon(q_ptr); trayIcon->setToolTip(tr("This is an Qt-App.")); @@ -120,7 +101,7 @@ class MainWindow::MainWindowPrivate }); } - void setInitWidget(const QString &text) + void setInitWidget(const QString &text) const { auto *label = new QLabel(text, q_ptr); label->setAlignment(Qt::AlignCenter); @@ -131,32 +112,34 @@ class MainWindow::MainWindowPrivate auto createSidebar() -> QWidget * { - auto *toolsButton = new QPushButton(tr("Common Tools"), q_ptr); - auto *aboutButton = new QPushButton(tr("About"), q_ptr); - auto *pluginButton = new QPushButton(tr("About Plugins"), q_ptr); - auto *qtButton = new QPushButton(tr("About Qt"), q_ptr); + auto *toolsButton = new QPushButton(tr("Main"), q_ptr); + auto *helpButton = new QPushButton(tr("Help"), q_ptr); + auto *settingsButton = new QPushButton(tr("Settings"), q_ptr); + auto *pluginButton = new QPushButton(tr("Plugins"), q_ptr); - QObject::connect(pluginButton, &QPushButton::clicked, q_ptr, &MainWindow::onAboutPlugins); - QObject::connect(qtButton, &QPushButton::clicked, q_ptr, [this] { - QMessageBox::aboutQt(q_ptr); + auto *configWidget = new ConfigWidget(q_ptr); + stackedWidget->addWidget(configWidget); + QObject::connect(settingsButton, &QPushButton::clicked, q_ptr, [=] { + stackedWidget->setCurrentWidget(configWidget); }); + QObject::connect(pluginButton, &QPushButton::clicked, q_ptr, &MainWindow::onAboutPlugins); - toolsButton->setProperty("Type", Core::CoreWidget::Tool); - aboutButton->setProperty("Type", Core::CoreWidget::About); - pluginButton->setProperty("Type", Core::CoreWidget::About); - qtButton->setProperty("Type", Core::CoreWidget::About); + toolsButton->setProperty("Type", Core::CoreWidget::Type::Main); + helpButton->setProperty("Type", Core::CoreWidget::Type::Help); + settingsButton->setProperty("Type", Core::CoreWidget::Type::Help); + pluginButton->setProperty("Type", Core::CoreWidget::Type::Help); switchBtnGroup->addButton(toolsButton, 0); - switchBtnGroup->addButton(aboutButton, 1); + switchBtnGroup->addButton(helpButton, 1); + menuBtnGroup->addButton(settingsButton); menuBtnGroup->addButton(pluginButton); - menuBtnGroup->addButton(qtButton); vLayoutGroup1->addWidget(toolsButton); - vLayoutGroup2->addWidget(aboutButton); + vLayoutGroup2->addWidget(helpButton); + vLayoutGroup2->addWidget(settingsButton); vLayoutGroup2->addWidget(pluginButton); - vLayoutGroup2->addWidget(qtButton); auto *widget = new QWidget(q_ptr); widget->setObjectName("MenuWidget"); @@ -188,9 +171,9 @@ void MainWindow::extensionsInitialized() if (page->widget() == nullptr) { continue; } - if (page->button()->property("Type") == Core::CoreWidget::Tool) { + if (page->button()->property("Type") == Core::CoreWidget::Type::Main) { d_ptr->vLayoutGroup1->addWidget(page->button()); - } else if (page->button()->property("Type") == Core::CoreWidget::About) { + } else if (page->button()->property("Type") == Core::CoreWidget::Type::Help) { d_ptr->vLayoutGroup2->addWidget(page->button()); } else { continue; @@ -224,14 +207,14 @@ void MainWindow::onAboutPlugins() dialog.exec(); } -void MainWindow::onQuit() -{ - QMetaObject::invokeMethod(qApp, &QApplication::quit, Qt::QueuedConnection); -} - void MainWindow::buildConnect() { - connect(this, &MainWindow::aboutToclose, this, &MainWindow::onQuit); + connect( + this, + &MainWindow::aboutToclose, + this, + [] { Utils::quitApplication(); }, + Qt::QueuedConnection); } void MainWindow::initMenu() diff --git a/src/plugins/coreplugin/mainwindow.h b/src/plugins/coreplugin/mainwindow.h index b66b47d..1be2083 100644 --- a/src/plugins/coreplugin/mainwindow.h +++ b/src/plugins/coreplugin/mainwindow.h @@ -17,7 +17,6 @@ class MainWindow : public GUI::CommonWidget private slots: void onShowGroupButton(int id); void onAboutPlugins(); - void onQuit(); private: void setupUI(); diff --git a/src/plugins/coreplugin/plugindialog.cpp b/src/plugins/coreplugin/plugindialog.cpp index 5376cc7..74f9b60 100644 --- a/src/plugins/coreplugin/plugindialog.cpp +++ b/src/plugins/coreplugin/plugindialog.cpp @@ -26,14 +26,14 @@ PluginDialog::PluginDialog(QWidget *parent) : GUI::Dialog(parent) , m_view(new ExtensionSystem::PluginView(this)) { - auto widget = new QWidget(this); + auto *widget = new QWidget(this); setCentralWidget(widget); - auto vl = new QVBoxLayout(widget); + auto *vl = new QVBoxLayout(widget); - auto filterLayout = new QHBoxLayout; + auto *filterLayout = new QHBoxLayout; vl->addLayout(filterLayout); - auto filterEdit = new GUI::FancyLineEdit(this); + auto *filterEdit = new GUI::FancyLineEdit(this); filterEdit->setFiltering(true); connect(filterEdit, &GUI::FancyLineEdit::filterChanged, @@ -49,7 +49,7 @@ PluginDialog::PluginDialog(QWidget *parent) m_detailsButton->setEnabled(false); m_errorDetailsButton->setEnabled(false); - auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); buttonBox->addButton(m_detailsButton, QDialogButtonBox::ActionRole); buttonBox->addButton(m_errorDetailsButton, QDialogButtonBox::ActionRole); buttonBox->addButton(m_installButton, QDialogButtonBox::ActionRole); @@ -97,7 +97,7 @@ void PluginDialog::showInstallWizard() void PluginDialog::updateButtons() { ExtensionSystem::PluginSpec *selectedSpec = m_view->currentPlugin(); - if (selectedSpec) { + if (selectedSpec != nullptr) { m_detailsButton->setEnabled(true); m_errorDetailsButton->setEnabled(selectedSpec->hasError()); } else { @@ -108,13 +108,14 @@ void PluginDialog::updateButtons() void PluginDialog::openDetails(ExtensionSystem::PluginSpec *spec) { - if (!spec) + if (spec == nullptr) { return; + } QDialog dialog(this); dialog.setWindowTitle(Tr::tr("Plugin Details of %1").arg(spec->name())); - auto layout = new QVBoxLayout; + auto *layout = new QVBoxLayout; dialog.setLayout(layout); - auto details = new ExtensionSystem::PluginDetailsView(&dialog); + auto *details = new ExtensionSystem::PluginDetailsView(&dialog); layout->addWidget(details); details->update(spec); QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close, @@ -130,13 +131,14 @@ void PluginDialog::openDetails(ExtensionSystem::PluginSpec *spec) void PluginDialog::openErrorDetails() { ExtensionSystem::PluginSpec *spec = m_view->currentPlugin(); - if (!spec) + if (spec == nullptr) { return; + } QDialog dialog(this); dialog.setWindowTitle(Tr::tr("Plugin Errors of %1").arg(spec->name())); - auto layout = new QVBoxLayout; + auto *layout = new QVBoxLayout; dialog.setLayout(layout); - auto errors = new ExtensionSystem::PluginErrorView(&dialog); + auto *errors = new ExtensionSystem::PluginErrorView(&dialog); layout->addWidget(errors); errors->update(spec); QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close, diff --git a/src/plugins/guiplugin/CMakeLists.txt b/src/plugins/guiplugin/CMakeLists.txt new file mode 100644 index 0000000..8652623 --- /dev/null +++ b/src/plugins/guiplugin/CMakeLists.txt @@ -0,0 +1,5 @@ +set(PROJECT_SOURCES guiplugin.cc guiplugin.hpp guiwidget.cc guiwidget.hpp) + +add_custom_plugin(guiplugin ${PROJECT_SOURCES}) +target_link_libraries(guiplugin PRIVATE core extensionsystem gui utils + Qt6::Widgets) diff --git a/src/plugins/guiplugin/guiplugin.cc b/src/plugins/guiplugin/guiplugin.cc new file mode 100644 index 0000000..d5c3eeb --- /dev/null +++ b/src/plugins/guiplugin/guiplugin.cc @@ -0,0 +1,20 @@ +#include "guiplugin.hpp" +#include "guiwidget.hpp" + +#include + +namespace Plugin { + +GuiPluginWidget::GuiPluginWidget(QObject *parent) +{ + setWidget(new GuiWidget); + setButton(new QPushButton(tr("Gui")), Core::CoreWidget::Main); +} + +bool GuiPlugin::initialize(const QStringList &arguments, QString *errorString) +{ + addObject(new GuiPluginWidget(this)); + return true; +} + +} // namespace Plugin diff --git a/src/plugins/guiplugin/guiplugin.hpp b/src/plugins/guiplugin/guiplugin.hpp new file mode 100644 index 0000000..ac372ad --- /dev/null +++ b/src/plugins/guiplugin/guiplugin.hpp @@ -0,0 +1,29 @@ +#ifndef GUIPLUGIN_HPP +#define GUIPLUGIN_HPP + +#include +#include + +namespace Plugin { + +class GuiPluginWidget : public Core::CoreWidget +{ + Q_OBJECT +public: + explicit GuiPluginWidget(QObject *parent = nullptr); +}; + +class GuiPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "Youth.Qt.plugin" FILE "guiplugin.json") +public: + GuiPlugin() = default; + + auto initialize(const QStringList &arguments, QString *errorString) -> bool override; + void extensionsInitialized() override {} +}; + +} // namespace Plugin + +#endif // GUIPLUGIN_HPP diff --git a/src/plugins/guiplugin/guiplugin.json b/src/plugins/guiplugin/guiplugin.json new file mode 100644 index 0000000..5bffefb --- /dev/null +++ b/src/plugins/guiplugin/guiplugin.json @@ -0,0 +1,11 @@ +{ + "Name": "GuiPlugin", + "Version": "0.1.1", + "CompatVersion": "0.1.1", + "Required": false, + "Vendor": "Youth", + "Copyright": "(C) 2024 The Youth Ltd", + "License": "GNU General Public License v3.0", + "Category": "Main", + "Description": "Gui" +} \ No newline at end of file diff --git a/src/plugins/guiplugin/guiplugin.pro b/src/plugins/guiplugin/guiplugin.pro new file mode 100644 index 0000000..2c42849 --- /dev/null +++ b/src/plugins/guiplugin/guiplugin.pro @@ -0,0 +1,23 @@ +include(../plugins.pri) + +QT += widgets + +DEFINES += GUIPLUGIN_LIBRARY +TARGET = $$replaceLibName(guiplugin) + +LIBS += \ + -l$$replaceLibName(core) \ + -l$$replaceLibName(extensionsystem) \ + -l$$replaceLibName(gui) \ + -l$$replaceLibName(utils) + +SOURCES += \ + guiplugin.cc \ + guiwidget.cc + +HEADERS += \ + guiplugin.hpp \ + guiwidget.hpp + +DISTFILES += \ + guiplugin.json diff --git a/src/plugins/guiplugin/guiwidget.cc b/src/plugins/guiplugin/guiwidget.cc new file mode 100644 index 0000000..7f3e9e8 --- /dev/null +++ b/src/plugins/guiplugin/guiwidget.cc @@ -0,0 +1,89 @@ +#include "guiwidget.hpp" + +#include + +namespace Plugin { + +GuiWidget::GuiWidget(QWidget *parent) + : QWidget{parent} +{ + setupUI(); +} + +void GuiWidget::setupUI() +{ + auto *layout = new QVBoxLayout(this); + layout->addWidget(createButtonGroup()); + layout->addWidget(createBoxGroup()); + layout->addWidget(createBarGroup()); +} + +auto GuiWidget::createButtonGroup() -> QGroupBox * +{ + auto *button = new QPushButton(tr("Default"), this); + auto *blueButton = new QPushButton(tr("Blue"), this); + blueButton->setObjectName("BlueButton"); + auto *grayButton = new QPushButton(tr("Gray"), this); + grayButton->setObjectName("GrayButton"); + auto *radioButton = new QRadioButton(tr("Radio"), this); + radioButton->setChecked(true); + auto *checkBox = new QCheckBox(tr("Check"), this); + checkBox->setChecked(true); + + auto *groupBox = new QGroupBox(tr("Buttons"), this); + auto *layout = new QHBoxLayout(groupBox); + layout->setSpacing(20); + layout->addWidget(button); + layout->addWidget(blueButton); + layout->addWidget(grayButton); + layout->addWidget(new QRadioButton(tr("Radio"), this)); + layout->addWidget(radioButton); + layout->addWidget(new QCheckBox(tr("Check"), this)); + layout->addWidget(checkBox); + + return groupBox; +} + +auto GuiWidget::createBoxGroup() -> QGroupBox * +{ + auto *spinBox = new QSpinBox(this); + spinBox->setRange(0, 100); + spinBox->setValue(50); + + auto *comboBox = new QComboBox(this); + for (int i = 0; i < 10; ++i) { + comboBox->addItem(QString::number(i)); + } + comboBox->setCurrentText("6"); + + auto *groupBox = new QGroupBox(tr("Boxes"), this); + auto *layout = new QHBoxLayout(groupBox); + layout->setSpacing(20); + layout->addWidget(spinBox); + layout->addWidget(comboBox); + + return groupBox; +} + +auto GuiWidget::createBarGroup() -> QGroupBox * +{ + auto *silder = new QSlider(Qt::Horizontal, this); + silder->setRange(0, 100); + silder->setValue(25); + + auto *progressBar = new QProgressBar(this); + progressBar->setRange(0, 100); + progressBar->setValue(25); + + connect(silder, &QSlider::valueChanged, progressBar, &QProgressBar::setValue); + + auto *groupBox = new QGroupBox(tr("Bars"), this); + auto *layout = new QHBoxLayout(groupBox); + layout->setSpacing(20); + layout->addWidget(silder); + layout->addWidget(progressBar); + + return groupBox; +} + +} // namespace Plugin diff --git a/src/plugins/guiplugin/guiwidget.hpp b/src/plugins/guiplugin/guiwidget.hpp new file mode 100644 index 0000000..32e6454 --- /dev/null +++ b/src/plugins/guiplugin/guiwidget.hpp @@ -0,0 +1,25 @@ +#ifndef GUIWIDGET_HPP +#define GUIWIDGET_HPP + +#include + +class QGroupBox; + +namespace Plugin { + +class GuiWidget : public QWidget +{ + Q_OBJECT +public: + explicit GuiWidget(QWidget *parent = nullptr); + +private: + void setupUI(); + auto createButtonGroup() -> QGroupBox *; + auto createBoxGroup() -> QGroupBox *; + auto createBarGroup() -> QGroupBox *; +}; + +} // namespace Plugin + +#endif // GUIWIDGET_HPP diff --git a/src/plugins/hashplugin/hashplugin.cc b/src/plugins/hashplugin/hashplugin.cc index e729e8a..1713245 100644 --- a/src/plugins/hashplugin/hashplugin.cc +++ b/src/plugins/hashplugin/hashplugin.cc @@ -8,7 +8,7 @@ namespace Plugin { HashPluginWidget::HashPluginWidget(QObject *parent) { setWidget(new HashWidget); - setButton(new QPushButton(tr("Hash Tool")), Core::CoreWidget::Tool); + setButton(new QPushButton(tr("Hash")), Core::CoreWidget::Main); } auto HashPlugin::initialize(const QStringList &arguments, QString *errorString) -> bool diff --git a/src/plugins/hashplugin/hashplugin.json b/src/plugins/hashplugin/hashplugin.json index 3dc7edb..98ef8b0 100644 --- a/src/plugins/hashplugin/hashplugin.json +++ b/src/plugins/hashplugin/hashplugin.json @@ -1,11 +1,11 @@ { "Name": "HashPlugin", - "Version": "0.0.1", - "CompatVersion": "0.0.1", + "Version": "0.1.1", + "CompatVersion": "0.1.1", "Required": false, "Vendor": "Youth", - "Copyright": "(C) 2023 The Youth Ltd", + "Copyright": "(C) 2024 The Youth Ltd", "License": "GNU General Public License v3.0", - "Category": "Tool", - "Description": "HashTool" + "Category": "Main", + "Description": "Hash" } \ No newline at end of file diff --git a/src/plugins/hashplugin/hashwidget.cc b/src/plugins/hashplugin/hashwidget.cc index 2d2c26f..c296406 100644 --- a/src/plugins/hashplugin/hashwidget.cc +++ b/src/plugins/hashplugin/hashwidget.cc @@ -21,6 +21,9 @@ class HashWidget::HashWidgetPrivate continue; } hashComboBox->addItem(metaEnums.key(i), value); + if (value == QCryptographicHash::Md5) { + hashComboBox->setCurrentText(metaEnums.key(i)); + } } selectFileButton = new QPushButton(QCoreApplication::translate("HashWidgetPrivate", @@ -41,26 +44,15 @@ class HashWidget::HashWidgetPrivate hashThread = new HashThread(q_ptr); } - void setupUI() + void setupUI() const { - auto buttonLayout = new QVBoxLayout; - buttonLayout->addWidget(hashComboBox); + auto *buttonLayout = new QHBoxLayout; + buttonLayout->setSpacing(20); buttonLayout->addWidget(selectFileButton); + buttonLayout->addWidget(hashComboBox); buttonLayout->addWidget(calculateButton); - auto leftLayout = new QVBoxLayout; - leftLayout->addWidget( - new QLabel(QCoreApplication::translate("HashWidgetPrivate", "Input:"), q_ptr)); - leftLayout->addWidget(inputEdit); - leftLayout->addWidget( - new QLabel(QCoreApplication::translate("HashWidgetPrivate", "Output:"), q_ptr)); - leftLayout->addWidget(outputEdit); - - auto bottomLayout = new QHBoxLayout; - bottomLayout->addLayout(leftLayout); - bottomLayout->addLayout(buttonLayout); - - auto descriptionLabel = new QLabel( + auto *descriptionLabel = new QLabel( QCoreApplication::translate( "HashWidgetPrivate", "If Input String is file path and file exists, calculate hash of file. " @@ -68,9 +60,15 @@ class HashWidget::HashWidgetPrivate q_ptr); descriptionLabel->setWordWrap(true); - auto layout = new QVBoxLayout(q_ptr); + auto *layout = new QVBoxLayout(q_ptr); layout->addWidget(descriptionLabel); - layout->addLayout(bottomLayout); + layout->addWidget( + new QLabel(QCoreApplication::translate("HashWidgetPrivate", "Input:"), q_ptr)); + layout->addWidget(inputEdit); + layout->addLayout(buttonLayout); + layout->addWidget( + new QLabel(QCoreApplication::translate("HashWidgetPrivate", "Output:"), q_ptr)); + layout->addWidget(outputEdit); } HashWidget *q_ptr; diff --git a/src/plugins/helloplugin/CMakeLists.txt b/src/plugins/helloplugin/CMakeLists.txt new file mode 100644 index 0000000..ea78982 --- /dev/null +++ b/src/plugins/helloplugin/CMakeLists.txt @@ -0,0 +1,4 @@ +set(PROJECT_SOURCES helloplugin.cc helloplugin.hpp hellowidget.cc hellowidget.hpp) + +add_custom_plugin(helloplugin ${PROJECT_SOURCES}) +target_link_libraries(helloplugin PRIVATE core extensionsystem Qt6::Widgets) diff --git a/src/plugins/helloplugin/helloplugin.cc b/src/plugins/helloplugin/helloplugin.cc new file mode 100644 index 0000000..c9cef91 --- /dev/null +++ b/src/plugins/helloplugin/helloplugin.cc @@ -0,0 +1,20 @@ +#include "helloplugin.hpp" +#include "hellowidget.hpp" + +#include + +namespace Plugin { + +HelloPluginWidget::HelloPluginWidget(QObject *parent) +{ + setWidget(new HelloWidget); + setButton(new QPushButton(tr("Hello")), Core::CoreWidget::Main); +} + +bool HelloPlugin::initialize(const QStringList &arguments, QString *errorString) +{ + addObject(new HelloPluginWidget(this)); + return true; +} + +} // namespace Plugin diff --git a/src/plugins/helloplugin/helloplugin.hpp b/src/plugins/helloplugin/helloplugin.hpp new file mode 100644 index 0000000..7f87612 --- /dev/null +++ b/src/plugins/helloplugin/helloplugin.hpp @@ -0,0 +1,29 @@ +#ifndef HELLOPLUGIN_HPP +#define HELLOPLUGIN_HPP + +#include +#include + +namespace Plugin { + +class HelloPluginWidget : public Core::CoreWidget +{ + Q_OBJECT +public: + explicit HelloPluginWidget(QObject *parent = nullptr); +}; + +class HelloPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "Youth.Qt.plugin" FILE "helloplugin.json") +public: + HelloPlugin() = default; + + auto initialize(const QStringList &arguments, QString *errorString) -> bool override; + void extensionsInitialized() override {} +}; + +} // namespace Plugin + +#endif // HELLOPLUGIN_HPP diff --git a/src/plugins/helloplugin/helloplugin.json b/src/plugins/helloplugin/helloplugin.json new file mode 100644 index 0000000..c8f819c --- /dev/null +++ b/src/plugins/helloplugin/helloplugin.json @@ -0,0 +1,11 @@ +{ + "Name": "HelloPlugin", + "Version": "0.1.1", + "CompatVersion": "0.1.1", + "Required": false, + "Vendor": "Youth", + "Copyright": "(C) 2024 The Youth Ltd", + "License": "GNU General Public License v3.0", + "Category": "Main", + "Description": "Hello" +} \ No newline at end of file diff --git a/src/plugins/helloplugin/helloplugin.pro b/src/plugins/helloplugin/helloplugin.pro new file mode 100644 index 0000000..dacbaa3 --- /dev/null +++ b/src/plugins/helloplugin/helloplugin.pro @@ -0,0 +1,21 @@ +include(../plugins.pri) + +QT += widgets + +DEFINES += HELLOPLUGIN_LIBRARY +TARGET = $$replaceLibName(helloplugin) + +LIBS += \ + -l$$replaceLibName(core) \ + -l$$replaceLibName(extensionsystem) + +SOURCES += \ + helloplugin.cc \ + hellowidget.cc + +HEADERS += \ + helloplugin.hpp \ + hellowidget.hpp + +DISTFILES += \ + helloplugin.json diff --git a/src/plugins/helloplugin/hellowidget.cc b/src/plugins/helloplugin/hellowidget.cc new file mode 100644 index 0000000..04f19ac --- /dev/null +++ b/src/plugins/helloplugin/hellowidget.cc @@ -0,0 +1,113 @@ +#include "hellowidget.hpp" + +#include + +namespace Plugin { + +class HelloWidget::HelloWidgetPrivate +{ +public: + explicit HelloWidgetPrivate(HelloWidget *q) + : q_ptr(q) + { + stackedWidget = new QStackedWidget(q_ptr); + while (labels.size() < labelsCount) { + auto *label = new QLabel(q_ptr); + label->setObjectName("HomeLabel"); + label->setAlignment(Qt::AlignCenter); + label->setWordWrap(true); + labels.append(label); + stackedWidget->addWidget(label); + } + + previousButton = new QToolButton(q_ptr); + nextButton = new QToolButton(q_ptr); + } + + void setupUI() + { + auto *buttonLayout = new QHBoxLayout; + buttonLayout->addWidget(previousButton); + buttonLayout->addStretch(); + buttonLayout->addWidget(nextButton); + + auto *layout = new QVBoxLayout(q_ptr); + layout->setContentsMargins(30, 30, 30, 30); + layout->addWidget(stackedWidget); + layout->addLayout(buttonLayout); + } + + void enableButtons() + { + previousButton->setEnabled(stackedWidget->currentIndex() > 0); + nextButton->setEnabled(stackedWidget->currentIndex() < labelsCount - 1); + } + + HelloWidget *q_ptr; + + QStackedWidget *stackedWidget; + QVector labels; + const int labelsCount = 4; + + QToolButton *previousButton; + QToolButton *nextButton; +}; + +HelloWidget::HelloWidget(QWidget *parent) + : QWidget{parent} + , d_ptr(new HelloWidgetPrivate(this)) +{ + d_ptr->setupUI(); + buildConnect(); + setupTr(); + + QMetaObject::invokeMethod(this, &HelloWidget::onNext, Qt::QueuedConnection); +} + +HelloWidget::~HelloWidget() = default; + +void HelloWidget::onPrevious() +{ + d_ptr->stackedWidget->setCurrentIndex((d_ptr->stackedWidget->currentIndex() - 1) + % d_ptr->labelsCount); + d_ptr->enableButtons(); +} + +void HelloWidget::onNext() +{ + d_ptr->stackedWidget->setCurrentIndex((d_ptr->stackedWidget->currentIndex() + 1) + % d_ptr->labelsCount); + d_ptr->enableButtons(); +} + +void HelloWidget::changeEvent(QEvent *event) +{ + QWidget::changeEvent(event); + switch (event->type()) { + case QEvent::LanguageChange: setupTr(); break; + default: break; + } +} + +void HelloWidget::setupTr() +{ + QStringList list{tr("Hello there! How's your day going so far?"), + tr("Hi, it's great to see you again!"), + tr("Good morning/afternoon/evening! How are you?"), + tr("Hey, hope you're having a wonderful day!")}; + + for (int i = 0; i < d_ptr->labelsCount; ++i) { + d_ptr->labels[i]->setText(list[i]); + } + + d_ptr->previousButton->setText(tr("Previous")); + d_ptr->nextButton->setText(tr("Next")); +} + +void HelloWidget::buildConnect() +{ + connect(d_ptr->previousButton, &QToolButton::clicked, this, &HelloWidget::onPrevious); + connect(d_ptr->nextButton, &QToolButton::clicked, this, &HelloWidget::onNext); +} + +} // namespace Plugin diff --git a/src/plugins/helloplugin/hellowidget.hpp b/src/plugins/helloplugin/hellowidget.hpp new file mode 100644 index 0000000..80c00ab --- /dev/null +++ b/src/plugins/helloplugin/hellowidget.hpp @@ -0,0 +1,32 @@ +#ifndef HELLOWIDGET_HPP +#define HELLOWIDGET_HPP + +#include + +namespace Plugin { + +class HelloWidget : public QWidget +{ + Q_OBJECT +public: + explicit HelloWidget(QWidget *parent = nullptr); + ~HelloWidget() override; + +private slots: + void onPrevious(); + void onNext(); + +protected: + void changeEvent(QEvent *event) override; + +private: + void setupTr(); + void buildConnect(); + + class HelloWidgetPrivate; + QScopedPointer d_ptr; +}; + +} // namespace Plugin + +#endif // HELLOWIDGET_HPP diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index e4e6961..9f563e0 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -2,7 +2,9 @@ TEMPLATE = subdirs CONFIG += ordered SUBDIRS += \ + aboutplugin \ coreplugin \ - serialplugin \ - tcpplugin \ - hashplugin + guiplugin \ + hashplugin \ + helloplugin \ + systeminfoplugin diff --git a/src/plugins/serialplugin/CMakeLists.txt b/src/plugins/serialplugin/CMakeLists.txt deleted file mode 100644 index 51ab1d3..0000000 --- a/src/plugins/serialplugin/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -set(PROJECT_SOURCES - serialplugin.cc - serialplugin.hpp - serialport.cpp - serialport.h - serialsettings.cc - serialsettings.hpp - serialwidget.cpp - serialwidget.h) - -add_custom_plugin(serialplugin ${PROJECT_SOURCES}) -target_link_libraries( - serialplugin - PRIVATE core - extensionsystem - gui - resource - utils - Qt6::SerialPort - Qt6::Core5Compat - Qt6::Widgets) diff --git a/src/plugins/serialplugin/serialplugin.cc b/src/plugins/serialplugin/serialplugin.cc deleted file mode 100644 index 62eeb84..0000000 --- a/src/plugins/serialplugin/serialplugin.cc +++ /dev/null @@ -1,20 +0,0 @@ -#include "serialplugin.hpp" -#include "serialwidget.h" - -#include - -namespace Plugin { - -SerialPluginWidget::SerialPluginWidget(QObject *parent) -{ - setWidget(new SerialWidget); - setButton(new QPushButton(tr("Serial Tool")), Core::CoreWidget::Tool); -} - -auto SerialPlugin::initialize(const QStringList &arguments, QString *errorString) -> bool -{ - addObject(new SerialPluginWidget(this)); - return true; -} - -} // namespace Plugin diff --git a/src/plugins/serialplugin/serialplugin.hpp b/src/plugins/serialplugin/serialplugin.hpp deleted file mode 100644 index 26f33e8..0000000 --- a/src/plugins/serialplugin/serialplugin.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef SERIALPLUGIN_HPP -#define SERIALPLUGIN_HPP - -#include -#include - -namespace Plugin { - -class SerialPluginWidget : public Core::CoreWidget -{ - Q_OBJECT -public: - explicit SerialPluginWidget(QObject *parent = nullptr); -}; - -class SerialPlugin : public ExtensionSystem::IPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "Youth.Qt.plugin" FILE "serialplugin.json") -public: - SerialPlugin() = default; - - auto initialize(const QStringList &arguments, QString *errorString) -> bool override; - void extensionsInitialized() override {} -}; - -} // namespace Plugin - -#endif // SERIALPLUGIN_HPP diff --git a/src/plugins/serialplugin/serialplugin.json b/src/plugins/serialplugin/serialplugin.json deleted file mode 100644 index 2163d7b..0000000 --- a/src/plugins/serialplugin/serialplugin.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "Name": "SerialPlugin", - "Version": "0.0.1", - "CompatVersion": "0.0.1", - "Required": false, - "Vendor": "Youth", - "Copyright": "(C) 2023 The Youth Ltd", - "License": "GNU General Public License v3.0", - "Category": "Tool", - "Description": "SerialTool" -} \ No newline at end of file diff --git a/src/plugins/serialplugin/serialplugin.pro b/src/plugins/serialplugin/serialplugin.pro deleted file mode 100644 index b43d25f..0000000 --- a/src/plugins/serialplugin/serialplugin.pro +++ /dev/null @@ -1,28 +0,0 @@ -include(../plugins.pri) - -QT += widgets serialport core5compat - -DEFINES += SERIALPLUGIN_LIBRARY -TARGET = $$replaceLibName(serialplugin) - -LIBS += \ - -l$$replaceLibName(core) \ - -l$$replaceLibName(extensionsystem) \ - -l$$replaceLibName(gui) \ - -l$$replaceLibName(resource) \ - -l$$replaceLibName(utils) - -SOURCES += \ - serialplugin.cc \ - serialport.cpp \ - serialsettings.cc \ - serialwidget.cpp - -HEADERS += \ - serialplugin.hpp \ - serialport.h \ - serialsettings.hpp \ - serialwidget.h - -DISTFILES += \ - serialplugin.json diff --git a/src/plugins/serialplugin/serialport.cpp b/src/plugins/serialplugin/serialport.cpp deleted file mode 100644 index aa2783d..0000000 --- a/src/plugins/serialplugin/serialport.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "serialport.h" - -#include - -namespace Plugin { - -SerialPort::SerialPort(QObject *parent) - : QSerialPort(parent) -{ - buildConnect(); -} - -SerialPort::~SerialPort() -{ - closeSerialPort(); -} - -bool SerialPort::openSerialPort(const SerialSettings ¶m) -{ - if (param.portName.isEmpty()) { - emit errorMessage(tr("Serial Error: PortName is Empty!")); - return false; - } - - closeSerialPort(); - - bool ok = true; - setPortName(param.portName); - ok &= open(QIODevice::ReadWrite); - flush(); - ok &= setBaudRate(static_cast(param.baudRate)); - ok &= setDataBits(static_cast(param.dataBits)); - ok &= setStopBits(static_cast(param.stopBits)); - ok &= setParity(static_cast(param.parity)); - ok &= setFlowControl(static_cast(param.flowControl)); - - if (!ok) { - onError(); - } - emit onLine(ok); - return ok; -} - -void SerialPort::closeSerialPort() -{ - if (isOpen()) { - close(); - emit onLine(false); - } -} - -void SerialPort::onError() -{ - if (error() == QSerialPort::NoError) { - return; - } - const auto err = tr("Serial Error[%1]: %2.").arg(QString::number(error()), errorString()); - emit errorMessage(err); -} - -void SerialPort::onReadyRead() -{ - if (bytesAvailable() <= 0) { - return; - } - - QByteArray bytes; - while (!atEnd()) { - bytes += readAll(); - } - emit message(bytes); -} - -void SerialPort::buildConnect() -{ - connect(this, &SerialPort::errorOccurred, this, &SerialPort::onError); - connect(this, &SerialPort::readyRead, this, &SerialPort::onReadyRead); -} - -} // namespace Plugin diff --git a/src/plugins/serialplugin/serialport.h b/src/plugins/serialplugin/serialport.h deleted file mode 100644 index 87106ff..0000000 --- a/src/plugins/serialplugin/serialport.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef SERIALPORT_H -#define SERIALPORT_H - -#include "serialsettings.hpp" - -namespace Plugin { - -class SerialPort : public QSerialPort -{ - Q_OBJECT -public: - explicit SerialPort(QObject *parent = nullptr); - ~SerialPort() override; - - auto openSerialPort(const SerialSettings ¶m) -> bool; - void closeSerialPort(); - -signals: - void onLine(bool); - void errorMessage(const QString &); - void message(const QByteArray &); - -private slots: - void onError(); - void onReadyRead(); - -private: - void buildConnect(); -}; - -} // namespace Plugin - -#endif // SERIALPORT_H diff --git a/src/plugins/serialplugin/serialsettings.cc b/src/plugins/serialplugin/serialsettings.cc deleted file mode 100644 index 88419e9..0000000 --- a/src/plugins/serialplugin/serialsettings.cc +++ /dev/null @@ -1 +0,0 @@ -#include "serialsettings.hpp" diff --git a/src/plugins/serialplugin/serialsettings.hpp b/src/plugins/serialplugin/serialsettings.hpp deleted file mode 100644 index 67084bf..0000000 --- a/src/plugins/serialplugin/serialsettings.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef SERIALSETTINGS_HPP -#define SERIALSETTINGS_HPP - -#include - -namespace Plugin { - -struct SerialSettings -{ - QString portName; - QSerialPort::BaudRate baudRate = QSerialPort::Baud9600; - QSerialPort::DataBits dataBits = QSerialPort::Data8; - QSerialPort::Parity parity = QSerialPort::NoParity; - QSerialPort::StopBits stopBits = QSerialPort::OneStop; - QSerialPort::FlowControl flowControl = QSerialPort::NoFlowControl; -}; - -} // namespace Plugin - -#endif // SERIALSETTINGS_HPP diff --git a/src/plugins/serialplugin/serialwidget.cpp b/src/plugins/serialplugin/serialwidget.cpp deleted file mode 100644 index 1851184..0000000 --- a/src/plugins/serialplugin/serialwidget.cpp +++ /dev/null @@ -1,562 +0,0 @@ -#include "serialwidget.h" -#include "serialport.h" -#include "serialsettings.hpp" - -#include -#include -#include - -#include -#include - -namespace Plugin { - -template -void initComboBox(QComboBox *comboBox) -{ - comboBox->clear(); - auto metaEnum = QMetaEnum::fromType(); - for (int i = 0; i < metaEnum.keyCount(); ++i) { - comboBox->addItem(metaEnum.key(i), metaEnum.value(i)); - } -} - -void setComboxCurrentText(QComboBox *box, const QVariant &value) -{ - box->setCurrentIndex(box->findData(value)); -} - -auto formatHex(const QByteArray &msg) -> QString -{ - QString temp; - auto hex = QString::fromLocal8Bit(msg.toHex().toUpper()); - for (int i = 0; i < hex.length(); i = i + 2) { - temp += hex.mid(i, 2) + " "; //两个字符+空格(例子:7e ) - } - return temp; -} - -struct WidgetSettings -{ - SerialSettings serialSettings; - bool hex = false; - int sendTime = 1000; - QString sendData; -}; - -class SerialWidget::SerialWidgetPrivate -{ -public: - explicit SerialWidgetPrivate(SerialWidget *q) - : q_ptr(q) - { - displayTextEdit = new QTextEdit(q_ptr); - displayTextEdit->document()->setMaximumBlockCount(1000); - displayTextEdit->setReadOnly(true); - - sendTextEdit = new QTextEdit(q_ptr); - sendButton = new QPushButton(QCoreApplication::translate("SerialWidgetPrivate", "Send"), - q_ptr); - sendButton->setObjectName("BlueButton"); - sendButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - - searchSerialButton = new QPushButton(QCoreApplication::translate("SerialWidgetPrivate", - "Search Available Serial"), - q_ptr); - searchSerialButton->setObjectName("BlueButton"); - portNameBox = new QComboBox(q_ptr); - baudRateBox = new QComboBox(q_ptr); - dataBitsBox = new QComboBox(q_ptr); - stopBitsBox = new QComboBox(q_ptr); - parityBox = new QComboBox(q_ptr); - flowControlBox = new QComboBox(q_ptr); - openOrCloseButton = new QPushButton(q_ptr); - openOrCloseButton->setObjectName("GrayButton"); - openOrCloseButton->setCheckable(true); - - hexBox = new QCheckBox(QCoreApplication::translate("SerialWidgetPrivate", "Hex"), q_ptr); - autoSendBox = new QCheckBox(QCoreApplication::translate("SerialWidgetPrivate", - "Auto Delivery"), - q_ptr); - autoSendTimeBox = new QSpinBox(q_ptr); - autoSendTimeBox->setSuffix(QCoreApplication::translate("SerialWidgetPrivate", " ms")); - autoSendTimeBox->setRange(0, 10000); - autoSendTimeBox->setValue(1000); - autoSendTimeBox->setSingleStep(50); - - sendConutButton = new QPushButton(QCoreApplication::translate("SerialWidgetPrivate", - "Send: 0 Bytes"), - q_ptr); - recvConutButton = new QPushButton(QCoreApplication::translate("SerialWidgetPrivate", - "Receive: 0 Bytes"), - q_ptr); - saveButton = new QPushButton(QCoreApplication::translate("SerialWidgetPrivate", "Save Data"), - q_ptr); - clearButton = new QPushButton(QCoreApplication::translate("SerialWidgetPrivate", - "Clear Screen"), - q_ptr); - - sendTimer = new QTimer(q_ptr); - } - - void setupUI() - { - auto *displayBox = createDisplayWidget(); - auto *sendBox = createSendWidget(); - - auto *splitter1 = new QSplitter(Qt::Vertical, q_ptr); - splitter1->addWidget(displayBox); - splitter1->addWidget(sendBox); - splitter1->setHandleWidth(0); - splitter1->setSizes(QList{400, 1}); - - auto *settingBox = createSettingsBox(); - - auto *splitter2 = new QSplitter(Qt::Horizontal, q_ptr); - splitter2->addWidget(splitter1); - splitter2->addWidget(settingBox); - splitter2->setHandleWidth(10); - splitter2->setSizes(QList{400, 1}); - - auto *layout = new QHBoxLayout(q_ptr); - layout->addWidget(splitter2); - } - - void initWindow() - { - searchPort(); - auto baudList = QSerialPortInfo::standardBaudRates(); - for (const auto baudrate : baudList) { - baudRateBox->addItem(QString::number(baudrate), baudrate); - } - - initComboBox(dataBitsBox); - initComboBox(parityBox); - initComboBox(flowControlBox); - - stopBitsBox->addItem("1", QSerialPort::OneStop); -#ifdef Q_OS_WIN - stopBitsBox->addItem("1.5", QSerialPort::OneAndHalfStop); -#endif - stopBitsBox->addItem("2", QSerialPort::TwoStop); - - QMetaObject::invokeMethod( - q_ptr, [this] { searchPort(); }, Qt::QueuedConnection); - } - - void searchPort() const - { - portNameBox->clear(); - auto availablePorts = QSerialPortInfo::availablePorts(); - for (const auto &info : std::as_const(availablePorts)) { - QSerialPort port; - port.setPort(info); - if (port.open(QIODevice::ReadWrite)) { - portNameBox->addItem(port.portName()); - port.close(); - } - } - } - - void setWindowSettings() - { - auto *serialSettings = &widgetSettings.serialSettings; - setComboxCurrentText(baudRateBox, serialSettings->baudRate); - setComboxCurrentText(dataBitsBox, serialSettings->dataBits); - setComboxCurrentText(stopBitsBox, serialSettings->stopBits); - setComboxCurrentText(parityBox, serialSettings->parity); - setComboxCurrentText(flowControlBox, serialSettings->flowControl); - - hexBox->setChecked(widgetSettings.hex); - autoSendTimeBox->setValue(widgetSettings.sendTime); - sendTextEdit->setText(widgetSettings.sendData); - } - - void setSerialSettings() - { - auto *serialParam = &widgetSettings.serialSettings; - serialParam->portName = portNameBox->currentText(); - serialParam->baudRate = static_cast( - baudRateBox->currentData().toInt()); - serialParam->dataBits = static_cast( - dataBitsBox->currentData().toInt()); - serialParam->stopBits = static_cast( - stopBitsBox->currentData().toInt()); - serialParam->parity = static_cast(parityBox->currentData().toInt()); - serialParam->flowControl = static_cast( - flowControlBox->currentData().toInt()); - } - - void appendDisplay(SerialWidget::MessageType type, const QString &message) const - { - if (message.isEmpty()) { - return; - } - QString display; - switch (type) { - case Send: - display = tr(" >> Serial Send: "); - displayTextEdit->setTextColor(Qt::black); - break; - case Recv: - display = tr(" >> Serial Recv: "); - displayTextEdit->setTextColor(QColor("dodgerblue")); - break; - case SuccessInfo: - display = tr(" >> Prompt Message: "); - displayTextEdit->setTextColor(Qt::green); - break; - case ErrorInfo: - display = tr(" >> Prompt Message: "); - displayTextEdit->setTextColor(Qt::red); - break; - default: return; - } - displayTextEdit->append( - tr("Time [%1] %2 %3") - .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz"), - display, - message)); - } - - void setSendCount() const { sendConutButton->setText(tr("Send: %1 Bytes").arg(sendCount)); } - - void setRecvCount() const { recvConutButton->setText(tr("Recv: %1 Bytes").arg(recvCount)); } - - void loadSetting() - { - auto *setting = ExtensionSystem::PluginManager::settings(); - if (setting == nullptr) { - return; - } - setting->beginGroup("serial_config"); - auto *serialParam = &widgetSettings.serialSettings; - serialParam->baudRate = QSerialPort::BaudRate( - setting->value("BaudRate", serialParam->baudRate).toInt()); - serialParam->dataBits = QSerialPort::DataBits( - setting->value("DataBits", serialParam->dataBits).toInt()); - serialParam->stopBits = QSerialPort::StopBits( - setting->value("StopBits", serialParam->stopBits).toInt()); - serialParam->parity = QSerialPort::Parity( - setting->value("Parity", serialParam->parity).toInt()); - serialParam->flowControl = QSerialPort::FlowControl( - setting->value("FlowControl", serialParam->flowControl).toInt()); - - widgetSettings.hex = setting->value("Hex", widgetSettings.hex).toBool(); - widgetSettings.sendTime = setting->value("SendTime", widgetSettings.sendTime).toInt(); - widgetSettings.sendData = setting->value("SendData", widgetSettings.sendData).toString(); - setting->endGroup(); - } - - void saveSetting() - { - auto *setting = ExtensionSystem::PluginManager::settings(); - if (setting == nullptr) { - return; - } - auto *serialParam = &widgetSettings.serialSettings; - setting->beginGroup("serial_config"); - setting->setValue("BaudRate", serialParam->baudRate); - setting->setValue("DataBits", serialParam->dataBits); - setting->setValue("StopBits", serialParam->stopBits); - setting->setValue("Parity", serialParam->parity); - setting->setValue("FlowControl", serialParam->flowControl); - - setting->setValue("Hex", hexBox->isChecked()); - setting->setValue("SendTime", autoSendTimeBox->value()); - setting->setValue("SendData", sendTextEdit->toPlainText().toUtf8()); - setting->endGroup(); - } - - SerialWidget *q_ptr; - - QTextEdit *displayTextEdit; - QTextEdit *sendTextEdit; - QPushButton *sendButton; - - QPushButton *searchSerialButton; - QComboBox *portNameBox; - QComboBox *baudRateBox; - QComboBox *dataBitsBox; - QComboBox *stopBitsBox; - QComboBox *parityBox; - QComboBox *flowControlBox; - QPushButton *openOrCloseButton; - - QCheckBox *hexBox; - QCheckBox *autoSendBox; - QSpinBox *autoSendTimeBox; - QPushButton *sendConutButton; - QPushButton *recvConutButton; - QPushButton *saveButton; - QPushButton *clearButton; - - QScopedPointer serialPortPtr; - WidgetSettings widgetSettings; - - QTimer *sendTimer; - int sendCount = 0; - int recvCount = 0; - -private: - [[nodiscard]] auto createDisplayWidget() const -> QWidget * - { - auto *box = new QGroupBox(QCoreApplication::translate("SerialWidgetPrivate", "Data Display"), - q_ptr); - auto *layout = new QHBoxLayout(box); - layout->addWidget(displayTextEdit); - return box; - } - - [[nodiscard]] auto createSendWidget() const -> QWidget * - { - auto *box = new QGroupBox(QCoreApplication::translate("SerialWidgetPrivate", "Data Send"), - q_ptr); - auto *layout = new QHBoxLayout(box); - layout->addWidget(sendTextEdit); - layout->addWidget(sendButton); - return box; - } - - [[nodiscard]] auto createSettingsBox() const -> QWidget * - { - auto *formLayout = new QFormLayout; - formLayout->setContentsMargins(0, 0, 0, 0); - formLayout->addRow(QCoreApplication::translate("SerialWidgetPrivate", "Port: "), - portNameBox); - formLayout->addRow(QCoreApplication::translate("SerialWidgetPrivate", "Baud Rate: "), - baudRateBox); - formLayout->addRow(QCoreApplication::translate("SerialWidgetPrivate", "Data Bits: "), - dataBitsBox); - formLayout->addRow(QCoreApplication::translate("SerialWidgetPrivate", "Stop Bits: "), - stopBitsBox); - formLayout->addRow(QCoreApplication::translate("SerialWidgetPrivate", "Parity: "), - parityBox); - formLayout->addRow(QCoreApplication::translate("SerialWidgetPrivate", "Flow Control: "), - flowControlBox); -#ifdef Q_OS_MACOS - formLayout->setRowWrapPolicy(QFormLayout::DontWrapRows); - formLayout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint); - formLayout->setFormAlignment(Qt::AlignHCenter | Qt::AlignTop); - formLayout->setLabelAlignment(Qt::AlignLeft); -#endif - - auto *box = new QGroupBox(tr("Settings"), q_ptr); - auto *layout = new QVBoxLayout(box); - layout->addWidget(searchSerialButton); - layout->addLayout(formLayout); - layout->addWidget(openOrCloseButton); - layout->addWidget(hexBox); - layout->addWidget(autoSendBox); - layout->addWidget(autoSendTimeBox); - layout->addStretch(); - layout->addWidget(sendConutButton); - layout->addWidget(recvConutButton); - layout->addWidget(saveButton); - layout->addWidget(clearButton); - return box; - } -}; - -SerialWidget::SerialWidget(QWidget *parent) - : QWidget(parent) - , d_ptr(new SerialWidgetPrivate(this)) -{ - d_ptr->setupUI(); - d_ptr->initWindow(); - d_ptr->loadSetting(); - d_ptr->setWindowSettings(); - buildConnect(); - Utils::setMacComboBoxStyle(this); - onLine(false); - d_ptr->displayTextEdit->clear(); -} - -SerialWidget::~SerialWidget() -{ - d_ptr->saveSetting(); -} - -void SerialWidget::onSendData() -{ - auto text = d_ptr->sendTextEdit->toPlainText(); - if (text.isEmpty()) { - return; - } - - QByteArray bytes; - if (d_ptr->hexBox->isChecked()) { - bytes = QByteArray::fromHex(text.toLocal8Bit()).toUpper(); - text = formatHex(bytes); - } else { - bytes = text.toLatin1(); - } - - if (d_ptr->serialPortPtr.isNull()) { - return; - } - d_ptr->serialPortPtr->write(bytes); - d_ptr->appendDisplay(Send, text); - d_ptr->sendCount += bytes.size(); - d_ptr->setSendCount(); -} - -void SerialWidget::onParamChanged(const QString & /*unused*/) -{ - if (d_ptr->serialPortPtr.isNull()) { - return; - } - d_ptr->serialPortPtr->close(); - d_ptr->serialPortPtr->openSerialPort(d_ptr->widgetSettings.serialSettings); -} - -void SerialWidget::onOpenOrCloseSerial(bool state) -{ - d_ptr->openOrCloseButton->setChecked(!state); - if (state) { - d_ptr->setSerialSettings(); - d_ptr->serialPortPtr.reset(new SerialPort); - connect(d_ptr->serialPortPtr.data(), - &SerialPort::onLine, - this, - &SerialWidget::onLine, - Qt::UniqueConnection); - connect(d_ptr->serialPortPtr.data(), - &SerialPort::errorMessage, - this, - &SerialWidget::onAppendError, - Qt::UniqueConnection); - connect(d_ptr->serialPortPtr.data(), - &SerialPort::message, - this, - &SerialWidget::onSerialRecvMessage, - Qt::UniqueConnection); - d_ptr->serialPortPtr->openSerialPort(d_ptr->widgetSettings.serialSettings); - return; - } - d_ptr->serialPortPtr.reset(); -} - -void SerialWidget::onLine(bool state) -{ - d_ptr->searchSerialButton->setEnabled(!state); - d_ptr->openOrCloseButton->setChecked(state); - d_ptr->openOrCloseButton->setText(state ? tr("Close") : tr("Open")); - - if (!state) { - d_ptr->autoSendBox->setChecked(state); - d_ptr->sendTimer->stop(); - } - d_ptr->autoSendBox->setEnabled(state); - d_ptr->sendButton->setEnabled(state); - - if (state) { - d_ptr->appendDisplay(SuccessInfo, tr("Serial Open!")); - } else { - d_ptr->appendDisplay(ErrorInfo, tr("Serial Close!")); - } -} - -void SerialWidget::onAppendError(const QString &error) -{ - d_ptr->appendDisplay(ErrorInfo, error); -} - -void SerialWidget::onSerialRecvMessage(const QByteArray &bytes) -{ - //qDebug() << "onSerialRecvMessage: " << bytes; - if (bytes.isEmpty()) { - return; - } - d_ptr->recvCount += bytes.size(); - d_ptr->setRecvCount(); - QString str; - if (d_ptr->hexBox->isChecked()) { - str = formatHex(bytes); - } else { - str = QString::fromLatin1(bytes); - } - d_ptr->appendDisplay(Recv, str); -} - -void SerialWidget::onAutoSend(bool state) -{ - d_ptr->autoSendTimeBox->setEnabled(!state); - if (state) { - d_ptr->sendTimer->start(d_ptr->autoSendTimeBox->value()); - } else { - d_ptr->sendTimer->stop(); - } -} - -void SerialWidget::onSave() -{ - const auto data = d_ptr->displayTextEdit->toPlainText(); - if (data.isEmpty()) { - return; - } - const auto openPath = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation) - .value(0, QDir::homePath()); - const auto path - = QFileDialog::getSaveFileName(this, - tr("Open File"), - QString("%1/%2").arg(openPath, - QDateTime::currentDateTime().toString( - "yyyy-MM-dd-HH-mm-ss")), - tr("Text Files(*.txt)")); - if (path.isEmpty()) { - d_ptr->appendDisplay(ErrorInfo, tr("No file saved.")); - return; - } - QFile file(path); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - GUI::MessBox::Warning(this, - tr("Write File: Can't open file:\n %1 !").arg(path), - GUI::MessBox::CloseButton); - return; - } - QTextStream stream(&file); - stream << data; - file.close(); - d_ptr->appendDisplay(SuccessInfo, tr("The file was saved successfully.")); -} - -void SerialWidget::buildConnect() -{ - connect(d_ptr->searchSerialButton, &QPushButton::clicked, this, [this] { d_ptr->searchPort(); }); - - connect(d_ptr->portNameBox, &QComboBox::currentTextChanged, this, &SerialWidget::onParamChanged); - connect(d_ptr->baudRateBox, &QComboBox::currentTextChanged, this, &SerialWidget::onParamChanged); - connect(d_ptr->dataBitsBox, &QComboBox::currentTextChanged, this, &SerialWidget::onParamChanged); - connect(d_ptr->stopBitsBox, &QComboBox::currentTextChanged, this, &SerialWidget::onParamChanged); - connect(d_ptr->parityBox, &QComboBox::currentTextChanged, this, &SerialWidget::onParamChanged); - connect(d_ptr->flowControlBox, - &QComboBox::currentTextChanged, - this, - &SerialWidget::onParamChanged); - - connect(d_ptr->openOrCloseButton, - &QPushButton::clicked, - this, - &SerialWidget::onOpenOrCloseSerial); - - auto *sendShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Return), this); - connect(sendShortcut, &QShortcut::activated, this, &SerialWidget::onSendData); - connect(d_ptr->sendButton, &QPushButton::clicked, this, &SerialWidget::onSendData); - - connect(d_ptr->autoSendBox, &QCheckBox::clicked, this, &SerialWidget::onAutoSend); - connect(d_ptr->sendTimer, &QTimer::timeout, this, &SerialWidget::onSendData); - - connect(d_ptr->sendConutButton, &QPushButton::clicked, this, [this] { - d_ptr->sendCount = 0; - d_ptr->setSendCount(); - }); - connect(d_ptr->recvConutButton, &QPushButton::clicked, this, [this] { - d_ptr->recvCount = 0; - d_ptr->setRecvCount(); - }); - connect(d_ptr->saveButton, &QPushButton::clicked, this, &SerialWidget::onSave); - connect(d_ptr->clearButton, &QPushButton::clicked, d_ptr->displayTextEdit, &QTextEdit::clear); -} - -} // namespace Plugin diff --git a/src/plugins/serialplugin/serialwidget.h b/src/plugins/serialplugin/serialwidget.h deleted file mode 100644 index 6b7daca..0000000 --- a/src/plugins/serialplugin/serialwidget.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef SERIALWIDGET_H -#define SERIALWIDGET_H - -#include - -namespace Plugin { - -class SerialWidget : public QWidget -{ - Q_OBJECT -public: - enum MessageType { Send, Recv, SuccessInfo, ErrorInfo }; - - explicit SerialWidget(QWidget *parent = nullptr); - ~SerialWidget() override; - -private slots: - void onSendData(); - void onParamChanged(const QString & /*unused*/); - - void onOpenOrCloseSerial(bool /*state*/); - - void onLine(bool /*state*/); - void onAppendError(const QString & /*error*/); - void onSerialRecvMessage(const QByteArray &bytes); - - void onAutoSend(bool /*state*/); - void onSave(); - -private: - void buildConnect(); - - class SerialWidgetPrivate; - QScopedPointer d_ptr; -}; - -} // namespace Plugin - -#endif // SERIALWIDGET_H diff --git a/src/plugins/systeminfoplugin/CMakeLists.txt b/src/plugins/systeminfoplugin/CMakeLists.txt new file mode 100644 index 0000000..e2c8152 --- /dev/null +++ b/src/plugins/systeminfoplugin/CMakeLists.txt @@ -0,0 +1,6 @@ +set(PROJECT_SOURCES systeminfoplugin.cc systeminfoplugin.hpp + systeminfowidget.cc systeminfowidget.hpp) + +add_custom_plugin(systeminfoplugin ${PROJECT_SOURCES}) +target_link_libraries(systeminfoplugin PRIVATE core extensionsystem utils + Qt6::Widgets) diff --git a/src/plugins/systeminfoplugin/systeminfoplugin.cc b/src/plugins/systeminfoplugin/systeminfoplugin.cc new file mode 100644 index 0000000..7382510 --- /dev/null +++ b/src/plugins/systeminfoplugin/systeminfoplugin.cc @@ -0,0 +1,20 @@ +#include "systeminfoplugin.hpp" +#include "systeminfowidget.hpp" + +#include + +namespace Plugin { + +SystemInfoPluginWidget::SystemInfoPluginWidget(QObject *parent) +{ + setWidget(new SystemInfoWidget); + setButton(new QPushButton(tr("System Info")), Core::CoreWidget::Help); +} + +bool SystemInfoPlugin::initialize(const QStringList &arguments, QString *errorString) +{ + addObject(new SystemInfoPluginWidget(this)); + return true; +} + +} // namespace Plugin diff --git a/src/plugins/systeminfoplugin/systeminfoplugin.hpp b/src/plugins/systeminfoplugin/systeminfoplugin.hpp new file mode 100644 index 0000000..a9f3cd0 --- /dev/null +++ b/src/plugins/systeminfoplugin/systeminfoplugin.hpp @@ -0,0 +1,29 @@ +#ifndef SYSTEMINFOPLUGIN_HPP +#define SYSTEMINFOPLUGIN_HPP + +#include +#include + +namespace Plugin { + +class SystemInfoPluginWidget : public Core::CoreWidget +{ + Q_OBJECT +public: + explicit SystemInfoPluginWidget(QObject *parent = nullptr); +}; + +class SystemInfoPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "Youth.Qt.plugin" FILE "systeminfoplugin.json") +public: + SystemInfoPlugin() = default; + + auto initialize(const QStringList &arguments, QString *errorString) -> bool override; + void extensionsInitialized() override {} +}; + +} // namespace Plugin + +#endif // SYSTEMINFOPLUGIN_HPP diff --git a/src/plugins/systeminfoplugin/systeminfoplugin.json b/src/plugins/systeminfoplugin/systeminfoplugin.json new file mode 100644 index 0000000..952313f --- /dev/null +++ b/src/plugins/systeminfoplugin/systeminfoplugin.json @@ -0,0 +1,11 @@ +{ + "Name": "SystemInfoPlugin", + "Version": "0.1.1", + "CompatVersion": "0.1.1", + "Required": false, + "Vendor": "Youth", + "Copyright": "(C) 2024 The Youth Ltd", + "License": "GNU General Public License v3.0", + "Category": "Help", + "Description": "SystemInfo" +} \ No newline at end of file diff --git a/src/plugins/systeminfoplugin/systeminfoplugin.pro b/src/plugins/systeminfoplugin/systeminfoplugin.pro new file mode 100644 index 0000000..196417e --- /dev/null +++ b/src/plugins/systeminfoplugin/systeminfoplugin.pro @@ -0,0 +1,22 @@ +include(../plugins.pri) + +QT += widgets + +DEFINES += SYSTEMINFOPLUGIN_LIBRARY +TARGET = $$replaceLibName(systeminfoplugin) + +LIBS += \ + -l$$replaceLibName(core) \ + -l$$replaceLibName(extensionsystem) \ + -l$$replaceLibName(utils) + +SOURCES += \ + systeminfoplugin.cc \ + systeminfowidget.cc + +HEADERS += \ + systeminfoplugin.hpp \ + systeminfowidget.hpp + +DISTFILES += \ + systeminfoplugin.json diff --git a/src/plugins/systeminfoplugin/systeminfowidget.cc b/src/plugins/systeminfoplugin/systeminfowidget.cc new file mode 100644 index 0000000..cce88e2 --- /dev/null +++ b/src/plugins/systeminfoplugin/systeminfowidget.cc @@ -0,0 +1,32 @@ +#include "systeminfowidget.hpp" + +#include + +namespace Plugin { + +SystemInfoWidget::SystemInfoWidget(QWidget *parent) + : QWidget{parent} +{ + setupUI(); +} + +void SystemInfoWidget::setupUI() +{ + auto *textBrowser = new QTextBrowser(this); + + auto *layout = new QVBoxLayout(this); + layout->setContentsMargins({}); + layout->addWidget(textBrowser); + + auto systemEnviroment = QProcess::systemEnvironment(); + for (const auto &info : std::as_const(systemEnviroment)) { + textBrowser->append(info); + textBrowser->append("\n"); + } + + auto cursor = textBrowser->textCursor(); + cursor.setPosition(0); + textBrowser->setTextCursor(cursor); +} + +} // namespace Plugin diff --git a/src/plugins/systeminfoplugin/systeminfowidget.hpp b/src/plugins/systeminfoplugin/systeminfowidget.hpp new file mode 100644 index 0000000..5437fc9 --- /dev/null +++ b/src/plugins/systeminfoplugin/systeminfowidget.hpp @@ -0,0 +1,20 @@ +#ifndef SYSTEMINFOWIDGET_HPP +#define SYSTEMINFOWIDGET_HPP + +#include + +namespace Plugin { + +class SystemInfoWidget : public QWidget +{ + Q_OBJECT +public: + explicit SystemInfoWidget(QWidget *parent = nullptr); + +private: + void setupUI(); +}; + +} // namespace Plugin + +#endif // SYSTEMINFOWIDGET_HPP diff --git a/src/plugins/tcpplugin/CMakeLists.txt b/src/plugins/tcpplugin/CMakeLists.txt deleted file mode 100644 index 9626dd1..0000000 --- a/src/plugins/tcpplugin/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -set(PROJECT_SOURCES - tcpclient.cpp - tcpclient.h - tcpplugin.cc - tcpplugin.hpp - tcpserver.cpp - tcpserver.h - tcpwidget.cpp - tcpwidget.h) - -add_custom_plugin(tcpplugin ${PROJECT_SOURCES}) -target_link_libraries( - tcpplugin - PRIVATE core - extensionsystem - gui - resource - utils - Qt6::Network - Qt6::Core5Compat - Qt6::Widgets) diff --git a/src/plugins/tcpplugin/tcpclient.cpp b/src/plugins/tcpplugin/tcpclient.cpp deleted file mode 100644 index deedb7a..0000000 --- a/src/plugins/tcpplugin/tcpclient.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "tcpclient.h" - -namespace Plugin { - -class TcpClient::TcpClientPrivate -{ -public: - explicit TcpClientPrivate(TcpClient *q) - : q_ptr(q) - {} - - TcpClient *q_ptr; - - QString ip; - quint16 port; -}; - -TcpClient::TcpClient(const QString &ip, quint16 port, QObject *parent) - : QTcpSocket(parent) - , d_ptr(new TcpClientPrivate(this)) -{ - buildConnect(); - setIpPort(ip, port); -} - -TcpClient::~TcpClient() -{ - closeSocket(); -} - -void TcpClient::setIpPort(const QString &ip, quint16 port) -{ - d_ptr->ip = ip; - d_ptr->port = port; -} - -void TcpClient::connectToServer() -{ - if (isConnected()) { - return; - } - connectToHost(d_ptr->ip, d_ptr->port); - //waitForConnected(1000); -} - -void TcpClient::connectToServer(const QString &ip, quint16 port) -{ - setIpPort(ip, port); - connectToServer(); -} - -void TcpClient::closeSocket() -{ - if (isOpen()) { - disconnectFromHost(); - close(); - } -} - -auto TcpClient::isConnected() -> bool -{ - return state() == QAbstractSocket::ConnectedState; -} - -void TcpClient::onError(SocketError socketError) -{ - const auto err = tr("[Client Error]: %1,%2").arg(QString::number(socketError), errorString()); - emit errorMessage(err); -} - -void TcpClient::onReadyRead() -{ - if (bytesAvailable() <= 0) { - return; - } - QByteArray bytes; - while (!atEnd()) { - bytes += readAll(); - } - emit serverMessage(bytes); -} - -void TcpClient::onStateChange(QAbstractSocket::SocketState socketState) -{ - switch (socketState) { - case QAbstractSocket::UnconnectedState: - emit socketStateChanged(tr("The socket is not connected."), false); - break; - case QAbstractSocket::HostLookupState: - emit socketStateChanged(tr("The socket is performing a host name lookup."), false); - break; - case QAbstractSocket::ConnectingState: - emit socketStateChanged(tr("The socket has started establishing a connection."), false); - break; - case QAbstractSocket::ConnectedState: - emit socketStateChanged(tr("A connection is established."), true); - break; - case QAbstractSocket::BoundState: - emit socketStateChanged(tr("The socket is bound to an address and port."), false); - break; - case QAbstractSocket::ClosingState: - emit socketStateChanged( - tr("The socket is about to close (data may still be waiting to be written)."), false); - break; - case QAbstractSocket::ListeningState: - emit socketStateChanged(tr("For internal use only."), false); - break; - default: break; - } -} - -void TcpClient::buildConnect() -{ - connect(this, &TcpClient::readyRead, this, &TcpClient::onReadyRead); - connect(this, &TcpClient::stateChanged, this, &TcpClient::onStateChange); - connect(this, &TcpClient::errorOccurred, this, &TcpClient::onError); -} - -} // namespace Plugin diff --git a/src/plugins/tcpplugin/tcpclient.h b/src/plugins/tcpplugin/tcpclient.h deleted file mode 100644 index 4728a57..0000000 --- a/src/plugins/tcpplugin/tcpclient.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef TCPCLIENT_H -#define TCPCLIENT_H - -#include - -namespace Plugin { - -class TcpClient : public QTcpSocket -{ - Q_OBJECT -public: - explicit TcpClient(const QString &ip, quint16 port, QObject *parent = nullptr); - ~TcpClient() override; - - void setIpPort(const QString &ip, quint16 port); - - void connectToServer(); - void connectToServer(const QString &ip, quint16 port); - void closeSocket(); - - auto isConnected() -> bool; - -signals: - void errorMessage(const QString &); - void serverMessage(const QByteArray &); - void socketStateChanged(const QString &text, bool onLine); - -private slots: - void onError(QAbstractSocket::SocketError socketError); - void onReadyRead(); - void onStateChange(QAbstractSocket::SocketState socketState); - -private: - void buildConnect(); - - class TcpClientPrivate; - QScopedPointer d_ptr; -}; - -} // namespace Plugin - -#endif // TCPCLIENT_H diff --git a/src/plugins/tcpplugin/tcpplugin.cc b/src/plugins/tcpplugin/tcpplugin.cc deleted file mode 100644 index 9ee2e2a..0000000 --- a/src/plugins/tcpplugin/tcpplugin.cc +++ /dev/null @@ -1,20 +0,0 @@ -#include "tcpplugin.hpp" -#include "tcpwidget.h" - -#include - -namespace Plugin { - -auto TcpPlugin::initialize(const QStringList &arguments, QString *errorString) -> bool -{ - addObject(new TcpPluginWidget(this)); - return true; -} - -TcpPluginWidget::TcpPluginWidget(QObject *parent) -{ - setWidget(new TcpWidget); - setButton(new QPushButton(tr("Tcp Tool")), Core::CoreWidget::Tool); -} - -} // namespace Plugin diff --git a/src/plugins/tcpplugin/tcpplugin.hpp b/src/plugins/tcpplugin/tcpplugin.hpp deleted file mode 100644 index c3d408c..0000000 --- a/src/plugins/tcpplugin/tcpplugin.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef TCPPLUGIN_HPP -#define TCPPLUGIN_HPP - -#include -#include - -namespace Plugin { - -class TcpPluginWidget : public Core::CoreWidget -{ - Q_OBJECT -public: - explicit TcpPluginWidget(QObject *parent = nullptr); -}; - -class TcpPlugin : public ExtensionSystem::IPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "Youth.Qt.plugin" FILE "tcpplugin.json") -public: - TcpPlugin() = default; - - auto initialize(const QStringList &arguments, QString *errorString) -> bool override; - void extensionsInitialized() override {} -}; - -} // namespace Plugin - -#endif // TCPPLUGIN_HPP diff --git a/src/plugins/tcpplugin/tcpplugin.json b/src/plugins/tcpplugin/tcpplugin.json deleted file mode 100644 index 40ce6e9..0000000 --- a/src/plugins/tcpplugin/tcpplugin.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "Name": "TcpPlugin", - "Version": "0.0.1", - "CompatVersion": "0.0.1", - "Required": false, - "Vendor": "Youth", - "Copyright": "(C) 2023 The Youth Ltd", - "License": "GNU General Public License v3.0", - "Category": "Tool", - "Description": "TcpTool" -} \ No newline at end of file diff --git a/src/plugins/tcpplugin/tcpplugin.pro b/src/plugins/tcpplugin/tcpplugin.pro deleted file mode 100644 index a5c79ab..0000000 --- a/src/plugins/tcpplugin/tcpplugin.pro +++ /dev/null @@ -1,28 +0,0 @@ -include(../plugins.pri) - -QT += widgets network core5compat - -DEFINES += TCPPLUGIN_LIBRARY -TARGET = $$replaceLibName(tcpplugin) - -LIBS += \ - -l$$replaceLibName(core) \ - -l$$replaceLibName(extensionsystem) \ - -l$$replaceLibName(gui) \ - -l$$replaceLibName(resource) \ - -l$$replaceLibName(utils) - -SOURCES += \ - tcpclient.cpp \ - tcpplugin.cc \ - tcpserver.cpp \ - tcpwidget.cpp - -HEADERS += \ - tcpclient.h \ - tcpplugin.hpp \ - tcpserver.h \ - tcpwidget.h - -DISTFILES += \ - tcpplugin.json diff --git a/src/plugins/tcpplugin/tcpserver.cpp b/src/plugins/tcpplugin/tcpserver.cpp deleted file mode 100644 index 405818c..0000000 --- a/src/plugins/tcpplugin/tcpserver.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include "tcpserver.h" - -#include - -namespace Plugin { - -class TcpServer::TcpServerPrivate -{ -public: - explicit TcpServerPrivate(TcpServer *q) - : q_ptr(q) - {} - - TcpServer *q_ptr; - - QVector tcpClients; -}; - -TcpServer::TcpServer(QObject *parent) - : QTcpServer(parent) - , d_ptr(new TcpServerPrivate(this)) -{ - buildConnect(); -} - -TcpServer::~TcpServer() -{ - if (isListening()) { - close(); - } -} - -void TcpServer::sendMessage(const QByteArray &bytes, const QString &clientInfo) -{ - if (bytes.isEmpty()) { - return; - } - - if (d_ptr->tcpClients.isEmpty()) { - auto error = tr( - "There is currently no client online, sending failed, please stop sending!"); - emit errorMessage(error); - return; - } - - if (clientInfo.isEmpty()) { - for (auto client : std::as_const(d_ptr->tcpClients)) { - client->write(bytes); - } - return; - } - - auto clientIP = clientInfo.split(":")[0].trimmed(); - auto clientPort = clientInfo.split(":")[1].toInt(); - for (auto client : std::as_const(d_ptr->tcpClients)) { - if (client->peerAddress().toString().split("::ffff:")[0] == clientIP - && client->peerPort() == clientPort) - client->write(bytes); - } -} - -void TcpServer::onError() -{ - auto err = tr("TCPServer accept Error: %1").arg(errorString()); - emit errorMessage(err); -} - -void TcpServer::onNewConnect() -{ - auto client = nextPendingConnection(); - client->setParent(this); //利用Qt的对象树进行析构 - d_ptr->tcpClients.append(client); - - auto clientInfo = tr("%1 : %2").arg(client->peerAddress().toString().split("::ffff:")[0], - QString::number(client->peerPort())); - emit newClientInfo(clientInfo); - - connect(client, &QTcpSocket::errorOccurred, this, &TcpServer::onClientError); - connect(client, &QTcpSocket::disconnected, this, &TcpServer::onClientDisconnect); - connect(client, &QTcpSocket::readyRead, this, &TcpServer::onClientReadyRead); -} - -void TcpServer::onClientError(QAbstractSocket::SocketError) -{ - auto client = qobject_cast(sender()); - if (nullptr == client) { - return; - } - - auto err = tr("Client [%1 : %2] Error: %3.") - .arg(client->peerAddress().toString().split("::ffff:")[0], - QString::number(client->peerPort()), - client->errorString()); - emit errorMessage(err); -} - -void TcpServer::onClientDisconnect() -{ - auto client = qobject_cast(sender()); - if (nullptr == client) { - return; - } - - if (client->state() == QAbstractSocket::UnconnectedState) { - auto clientInfo = tr("%1 : %2").arg(client->peerAddress().toString().split("::ffff:")[0], - QString::number(client->peerPort())); - emit disconnectClientInfo(clientInfo); - d_ptr->tcpClients.removeOne(client); - client->deleteLater(); - } -} - -void TcpServer::onClientReadyRead() -{ - auto client = qobject_cast(sender()); - if (nullptr == client) { - return; - } - if (client->bytesAvailable() <= 0) { - return; - } - - QByteArray bytes; - while (!client->atEnd()) { - bytes += client->readAll(); - } - - auto clientInfo = tr("Client [%1 : %2] : ") - .arg(client->peerAddress().toString().split("::ffff:")[0], - QString::number(client->peerPort())); - emit clientMessage(clientInfo, bytes); -} - -void TcpServer::buildConnect() -{ - connect(this, &TcpServer::acceptError, this, &TcpServer::onError); - connect(this, &TcpServer::newConnection, this, &TcpServer::onNewConnect); -} - -} // namespace Plugin diff --git a/src/plugins/tcpplugin/tcpserver.h b/src/plugins/tcpplugin/tcpserver.h deleted file mode 100644 index 5bc139e..0000000 --- a/src/plugins/tcpplugin/tcpserver.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef TCPSERVER_H -#define TCPSERVER_H - -#include - -namespace Plugin { - -class TcpServer : public QTcpServer -{ - Q_OBJECT -public: - explicit TcpServer(QObject *parent = nullptr); - ~TcpServer() override; - - void sendMessage(const QByteArray &bytes, const QString &clientInfo = ""); - -signals: - void errorMessage(const QString &); - void newClientInfo(const QString &); - void disconnectClientInfo(const QString &); - void clientMessage(const QString &, const QByteArray &); - -private slots: - void onError(); - void onNewConnect(); - void onClientError(QAbstractSocket::SocketError); - void onClientDisconnect(); - void onClientReadyRead(); - -private: - void buildConnect(); - - class TcpServerPrivate; - QScopedPointer d_ptr; -}; - -} // namespace Plugin - -#endif // TCPSERVER_H diff --git a/src/plugins/tcpplugin/tcpwidget.cpp b/src/plugins/tcpplugin/tcpwidget.cpp deleted file mode 100644 index d51e1ce..0000000 --- a/src/plugins/tcpplugin/tcpwidget.cpp +++ /dev/null @@ -1,671 +0,0 @@ -#include "tcpwidget.h" -#include "tcpclient.h" -#include "tcpserver.h" - -#include -#include -#include -#include - -#include -#include -#include - -namespace Plugin { - -auto formatHex(const QByteArray &msg) -> QString -{ - QString temp; - auto hex = QString::fromLocal8Bit(msg.toHex().toUpper()); - for (int i = 0; i < hex.length(); i = i + 2) { - temp += hex.mid(i, 2) + " "; //两个字符+空格(例子:7e ) - } - return temp; -} - -class TcpWidget::TcpWidgetPrivate -{ -public: - explicit TcpWidgetPrivate(TcpWidget *q) - : q_ptr(q) - { - displayTextEdit = new QTextEdit(q_ptr); - displayTextEdit->document()->setMaximumBlockCount(1000); - displayTextEdit->setReadOnly(true); - - sendTextEdit = new QTextEdit(q_ptr); - sendButton = new QPushButton(QCoreApplication::translate("TcpWidgetPrivate", "Send"), q_ptr); - sendButton->setObjectName("BlueButton"); - sendButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - - modelBox = new QComboBox(q_ptr); - modelBox->addItems({"TcpServer", "TcpClient"}); - ipLabel = new QLabel(QCoreApplication::translate("TcpWidgetPrivate", "Local IP List: "), - q_ptr); - localIPBox = new QComboBox(q_ptr); - serverIPEdit = new QLineEdit(q_ptr); - serverIPEdit->setPlaceholderText( - QCoreApplication::translate("TcpWidgetPrivate", "Please enter the server IP address.")); - QRegularExpression regExp( - "^((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$"); - auto *validator = new QRegularExpressionValidator(regExp, serverIPEdit); - serverIPEdit->setValidator(validator); - portLabel = new QLabel(QCoreApplication::translate("TcpWidgetPrivate", "Local Port: "), - q_ptr); - portEdit = new QLineEdit(q_ptr); - portEdit->setPlaceholderText( - QCoreApplication::translate("TcpWidgetPrivate", "Please enter the port number.")); - portEdit->setValidator(new Utils::IntValidator(0, 65536, portEdit)); - listenOrConnectButton = new QPushButton(q_ptr); - listenOrConnectButton->setCheckable(true); - listenOrConnectButton->setObjectName("GrayButton"); - - hexBox = new QCheckBox(QCoreApplication::translate("TcpWidgetPrivate", "Hex"), q_ptr); - autoSendBox = new QCheckBox(QCoreApplication::translate("TcpWidgetPrivate", "Auto Delivery"), - q_ptr); - autoSendTimeBox = new QSpinBox(q_ptr); - autoSendTimeBox->setSuffix(QCoreApplication::translate("TcpWidgetPrivate", " ms")); - autoSendTimeBox->setRange(0, 10000); - autoSendTimeBox->setValue(1000); - autoSendTimeBox->setSingleStep(50); - - allConnectBox = new QComboBox(q_ptr); - allConnectBox->addItem(QCoreApplication::translate("TcpWidgetPrivate", "Connect All")); - autoConnectBox = new QCheckBox(QCoreApplication::translate("TcpWidgetPrivate", - "Auto Reconnect"), - q_ptr); - autoConnectTimeBox = new QSpinBox(q_ptr); - autoConnectTimeBox->setSuffix(QCoreApplication::translate("TcpWidgetPrivate", " ms")); - autoConnectTimeBox->setRange(1000, 100000); - autoConnectTimeBox->setValue(1000); - autoConnectTimeBox->setSingleStep(50); - - sendConutButton = new QPushButton(q_ptr); - recvConutButton = new QPushButton(q_ptr); - saveButton = new QPushButton(QCoreApplication::translate("TcpWidgetPrivate", "Save Data"), - q_ptr); - clearButton = new QPushButton(QCoreApplication::translate("TcpWidgetPrivate", - "Clear Screen"), - q_ptr); - - setWidget = new QWidget(q_ptr); - } - - void setupUI() - { - auto *displayBox = createDisplayWidget(); - auto *sendBox = createSendWidget(); - - auto *splitter1 = new QSplitter(Qt::Vertical, q_ptr); - splitter1->addWidget(displayBox); - splitter1->addWidget(sendBox); - splitter1->setHandleWidth(0); - splitter1->setSizes(QList{400, 1}); - - auto *settingBox = createSettingsBox(); - - auto *splitter2 = new QSplitter(Qt::Horizontal, q_ptr); - splitter2->addWidget(splitter1); - splitter2->addWidget(settingBox); - splitter2->setHandleWidth(10); - splitter2->setSizes(QList{400, 1}); - - auto *layout = new QHBoxLayout(q_ptr); - layout->addWidget(splitter2); - } - - void initWindow() const - { - localIPBox->clear(); - auto ipList = QNetworkInterface::allAddresses(); - for (const auto &address : std::as_const(ipList)) { - if (address.protocol() == QAbstractSocket::IPv4Protocol) { - localIPBox->addItem(address.toString()); - } - } - localIPBox->setCurrentIndex(localIPBox->count() - 1); - } - - void setSendCount() const - { - sendConutButton->setText( - QCoreApplication::translate("TcpWidgetPrivate", "Send: %1 Bytes").arg(sendCount)); - } - - void setRecvCount() const - { - recvConutButton->setText( - QCoreApplication::translate("TcpWidgetPrivate", "Recv: %1 Bytes").arg(recvCount)); - } - - void clearCount() - { - sendCount = 0; - recvCount = 0; - setSendCount(); - setRecvCount(); - } - - void appendDisplay(TcpWidget::MessageType type, const QString &message) const - { - if (message.isEmpty()) { - return; - } - QString display; - switch (type) { - case Send: - display = QCoreApplication::translate("TcpWidgetPrivate", " >> Network Send: "); - displayTextEdit->setTextColor(Qt::black); - break; - case Recv: - display = QCoreApplication::translate("TcpWidgetPrivate", " >> Network Recv: "); - displayTextEdit->setTextColor(QColor("dodgerblue")); - break; - case SuccessInfo: - display = QCoreApplication::translate("TcpWidgetPrivate", " >> Prompt Message: "); - displayTextEdit->setTextColor(Qt::green); - break; - case ErrorInfo: - display = QCoreApplication::translate("TcpWidgetPrivate", " >> Prompt Message: "); - displayTextEdit->setTextColor(Qt::red); - break; - default: return; - } - displayTextEdit->append( - QCoreApplication::translate("TcpWidgetPrivate", "Time [%1] %2 %3") - .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz"), - display, - message)); - } - - void loadSetting() const - { - auto *setting = ExtensionSystem::PluginManager::settings(); - if (setting == nullptr) { - return; - } - setting->beginGroup("tcp_config"); - modelBox->setCurrentIndex(setting->value("CommunicationMode").toInt()); - serverIPEdit->setText(setting->value("ClientIP", "127.0.0.1").toString()); - portEdit->setText(setting->value("Port", "80").toString()); - - hexBox->setChecked(setting->value("Hex").toBool()); - autoSendTimeBox->setValue(setting->value("SendTime", 1000).toInt()); - autoConnectTimeBox->setValue(setting->value("ConnectTime", 3000).toInt()); - sendTextEdit->setText(setting->value("SendData").toString()); - setting->endGroup(); - } - - void saveSetting() const - { - auto *setting = ExtensionSystem::PluginManager::settings(); - if (setting == nullptr) { - return; - } - setting->beginGroup("tcp_config"); - setting->setValue("CommunicationMode", modelBox->currentIndex()); - setting->setValue("ClientIP", serverIPEdit->text()); - setting->setValue("Port", portEdit->text()); - - setting->setValue("Hex", hexBox->isChecked()); - setting->setValue("SendTime", autoSendTimeBox->value()); - setting->setValue("ConnectTime", autoConnectTimeBox->value()); - setting->setValue("SendData", sendTextEdit->toPlainText().toUtf8()); - setting->endGroup(); - } - - TcpWidget *q_ptr; - - QTextEdit *displayTextEdit; - QTextEdit *sendTextEdit; - QPushButton *sendButton; - - QComboBox *modelBox; - QLabel *ipLabel; - QComboBox *localIPBox; - QLineEdit *serverIPEdit; - QLabel *portLabel; - QLineEdit *portEdit; - QPushButton *listenOrConnectButton; - - QCheckBox *hexBox; - QCheckBox *autoSendBox; - QSpinBox *autoSendTimeBox; - QComboBox *allConnectBox; - QCheckBox *autoConnectBox; - QSpinBox *autoConnectTimeBox; - - QPushButton *sendConutButton; - QPushButton *recvConutButton; - QPushButton *saveButton; - QPushButton *clearButton; - - QWidget *setWidget; - - QScopedPointer tcpClientPtr; - QScopedPointer tcpServerPtr; - - QTimer sendTime; - QTimer autoConnectTime; - int sendCount = 0; - int recvCount = 0; - -private: - [[nodiscard]] auto createDisplayWidget() const -> QWidget * - { - auto *box = new QGroupBox(QCoreApplication::translate("TcpWidgetPrivate", "Data Display"), - q_ptr); - auto *layout = new QHBoxLayout(box); - layout->addWidget(displayTextEdit); - return box; - } - - [[nodiscard]] auto createSendWidget() const -> QWidget * - { - auto *box = new QGroupBox(QCoreApplication::translate("TcpWidgetPrivate", "Data Send"), - q_ptr); - auto *layout = new QHBoxLayout(box); - layout->addWidget(sendTextEdit); - layout->addWidget(sendButton); - return box; - } - - [[nodiscard]] auto createSettingsBox() const -> QWidget * - { - auto *setLayout = new QVBoxLayout(setWidget); - setLayout->setContentsMargins(0, 0, 0, 0); - setLayout->addWidget( - new QLabel(QCoreApplication::translate("TcpWidgetPrivate", "Mode: "), q_ptr)); - setLayout->addWidget(modelBox); - setLayout->addWidget(ipLabel); - setLayout->addWidget(localIPBox); - setLayout->addWidget(serverIPEdit); - setLayout->addWidget(portLabel); - setLayout->addWidget(portEdit); - setLayout->addWidget(listenOrConnectButton); - - auto *box = new QGroupBox(QCoreApplication::translate("TcpWidgetPrivate", "Settings"), - q_ptr); - auto *layout = new QVBoxLayout(box); - layout->addWidget(setWidget); - layout->addWidget(hexBox); - layout->addWidget(autoSendBox); - layout->addWidget(autoSendTimeBox); - layout->addWidget(allConnectBox); - layout->addWidget(autoConnectBox); - layout->addWidget(autoConnectTimeBox); - layout->addStretch(); - layout->addWidget(sendConutButton); - layout->addWidget(recvConutButton); - layout->addWidget(saveButton); - layout->addWidget(clearButton); - return box; - } -}; - -TcpWidget::TcpWidget(QWidget *parent) - : QWidget(parent) - , d_ptr(new TcpWidgetPrivate(this)) -{ - d_ptr->setupUI(); - d_ptr->initWindow(); - buildConnect(); - Utils::setMacComboBoxStyle(this); - d_ptr->loadSetting(); - QMetaObject::invokeMethod( - this, - [this] { - onModelChange(d_ptr->modelBox->currentText()); - d_ptr->displayTextEdit->clear(); - }, - Qt::QueuedConnection); -} - -TcpWidget::~TcpWidget() -{ - d_ptr->saveSetting(); -} - -void TcpWidget::onModelChange(const QString &text) -{ - if (text == tr("TcpServer")) { - d_ptr->ipLabel->setText(tr("Local IP List: ")); - d_ptr->localIPBox->show(); - d_ptr->serverIPEdit->hide(); - d_ptr->portLabel->setText(tr("Local Port: ")); - d_ptr->listenOrConnectButton->setText(tr("Listen")); - d_ptr->allConnectBox->show(); - d_ptr->autoConnectBox->hide(); - d_ptr->autoConnectTimeBox->hide(); - onServerOnline(false); - } else if (text == tr("TcpClient")) { - d_ptr->ipLabel->setText(tr("Server IP: ")); - d_ptr->localIPBox->hide(); - d_ptr->serverIPEdit->show(); - d_ptr->listenOrConnectButton->setText(tr("Connect")); - d_ptr->portLabel->setText(tr("Server Port: ")); - d_ptr->allConnectBox->hide(); - d_ptr->autoConnectBox->show(); - d_ptr->autoConnectTimeBox->show(); - onClientStateChanged(tr("The socket is not connected."), false); - } - d_ptr->clearCount(); - d_ptr->displayTextEdit->textCursor().removeSelectedText(); -} - -void TcpWidget::onListenOrConnect(bool state) -{ - d_ptr->listenOrConnectButton->setChecked(!state); - - onServerOnline(false); - d_ptr->tcpServerPtr.reset(); - d_ptr->tcpClientPtr.reset(); - if (d_ptr->modelBox->currentText() == tr("TcpServer")) { - if (state) { - resetTcpServer(); - } else { - for (int i = 1; i < d_ptr->allConnectBox->count(); i++) { - onServerDisconnectClient(d_ptr->allConnectBox->itemText(i)); - d_ptr->allConnectBox->removeItem(i); - } - } - } else if (d_ptr->modelBox->currentText() == tr("TcpClient")) { - if (state) { - resetTcpClient(); - } - } -} - -void TcpWidget::onSendData() -{ - auto text = d_ptr->sendTextEdit->toPlainText(); - if (text.isEmpty()) { - return; - } - - QByteArray bytes; - if (d_ptr->hexBox->isChecked()) { - bytes = QByteArray::fromHex(text.toLocal8Bit()).toUpper(); - text = formatHex(bytes); - } else { - bytes = text.toUtf8(); - } - - if (d_ptr->tcpServerPtr.isNull()) { - d_ptr->tcpClientPtr->write(bytes); - d_ptr->appendDisplay(Send, text); - d_ptr->sendCount += bytes.size(); - d_ptr->setSendCount(); - return; - } - if (d_ptr->allConnectBox->count() == 1) { - auto error = tr("No client is currently online, please stop sending invalid!"); - d_ptr->appendDisplay(ErrorInfo, error); - return; - } - auto clientInfo = d_ptr->allConnectBox->currentText(); - if (clientInfo == tr("Connect All")) { - d_ptr->appendDisplay(Send, tr("Send To All Online Clients: %1.").arg(text)); - clientInfo.clear(); - } else { - d_ptr->appendDisplay(Send, tr("Send To Clients [%1] : %2.").arg(clientInfo, text)); - } - d_ptr->tcpServerPtr->sendMessage(bytes, clientInfo); - d_ptr->sendCount += text.size(); - d_ptr->setSendCount(); -} - -void TcpWidget::onServerOnline(bool state) -{ - d_ptr->modelBox->setEnabled(!state); - d_ptr->localIPBox->setEnabled(!state); - d_ptr->portEdit->setEnabled(!state); - d_ptr->listenOrConnectButton->setChecked(state); - d_ptr->listenOrConnectButton->setText(state ? tr("Stop Listen") : tr("Listen")); - if (!state) { - d_ptr->autoSendBox->setChecked(state); - d_ptr->sendTime.stop(); - } - d_ptr->autoSendBox->setEnabled(state); - d_ptr->sendButton->setEnabled(state); - - if (state) { - d_ptr->appendDisplay(SuccessInfo, tr("Server Online!")); - } else { - d_ptr->appendDisplay(ErrorInfo, tr("Server Offline!")); - } -} - -void TcpWidget::onServerNewClient(const QString &clientInfo) -{ - d_ptr->allConnectBox->addItem(clientInfo); - auto str = clientInfo + tr(" Online."); - d_ptr->appendDisplay(SuccessInfo, str); -} - -void TcpWidget::onServerDisconnectClient(const QString &clientInfo) -{ - d_ptr->allConnectBox->removeItem(d_ptr->allConnectBox->findText(clientInfo)); - auto str = clientInfo + tr(" Offline."); - d_ptr->appendDisplay(ErrorInfo, str); -} - -void TcpWidget::onServerRecvMessage(const QString &clientInfo, const QByteArray &bytes) -{ - if (bytes.isEmpty()) { - return; - } - d_ptr->recvCount += bytes.size(); - d_ptr->setRecvCount(); - auto str = clientInfo; - if (d_ptr->hexBox->isChecked()) { - str += formatHex(bytes); - } else { - str += QString::fromUtf8(bytes); - } - d_ptr->appendDisplay(Recv, str); -} - -void TcpWidget::onClientStateChanged(const QString &text, bool onLine) -{ - d_ptr->modelBox->setEnabled(!onLine); - d_ptr->serverIPEdit->setEnabled(!onLine); - d_ptr->portEdit->setEnabled(!onLine); - d_ptr->listenOrConnectButton->setChecked(onLine); - d_ptr->listenOrConnectButton->setText(onLine ? tr("Disconnect") : tr("Connect")); - if (!onLine) { - d_ptr->autoSendBox->setChecked(onLine); - d_ptr->sendTime.stop(); - } - d_ptr->autoSendBox->setEnabled(onLine); - d_ptr->sendButton->setEnabled(onLine); - - // if (!onLine && !d_ptr->autoConnectBox->isChecked()) { - // d_ptr->tcpClientPtr.reset(); - // } - if (onLine) { - d_ptr->appendDisplay(SuccessInfo, text); - } else { - d_ptr->appendDisplay(ErrorInfo, text); - } -} - -void TcpWidget::onClientRecvMessage(const QByteArray &bytes) -{ - if (bytes.isEmpty()) { - return; - } - d_ptr->recvCount += bytes.size(); - d_ptr->setRecvCount(); - QString str; - if (d_ptr->hexBox->isChecked()) { - str = formatHex(bytes); - } else { - str = bytes; - } - d_ptr->appendDisplay(Recv, str); -} - -void TcpWidget::onAutoReconnectStartOrStop(bool state) -{ - d_ptr->setWidget->setEnabled(!state); - if (state) { - resetTcpClient(); - d_ptr->autoConnectTime.start(d_ptr->autoConnectTimeBox->value()); - } else { - d_ptr->autoConnectTime.stop(); - if (!d_ptr->listenOrConnectButton->isChecked()) { - d_ptr->tcpServerPtr.reset(); - d_ptr->tcpClientPtr.reset(); - } - } -} - -void TcpWidget::onAutoConnect() -{ - if (!d_ptr->tcpClientPtr.isNull() && d_ptr->tcpClientPtr->isConnected()) { - return; - } - - resetTcpClient(); -} - -void TcpWidget::onAutoSend(bool state) -{ - d_ptr->autoSendTimeBox->setEnabled(!state); - if (state) { - d_ptr->sendTime.start(d_ptr->autoSendTimeBox->value()); - } else { - d_ptr->sendTime.stop(); - } -} - -void TcpWidget::onSave() -{ - auto text = d_ptr->displayTextEdit->toPlainText(); - if (text.isEmpty()) { - return; - } - const auto openPath = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation) - .value(0, QDir::homePath()); - const auto path - = QFileDialog::getSaveFileName(this, - tr("Open File"), - QString("%1/%2").arg(openPath, - QDateTime::currentDateTime().toString( - "yyyy-MM-dd-HH-mm-ss")), - tr("Text Files(*.txt)")); - if (path.isEmpty()) { - d_ptr->appendDisplay(ErrorInfo, tr("No file saved.")); - return; - } - QFile file(path); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - GUI::MessBox::Warning(this, - tr("Write File: Can't open file:\n %1 !").arg(path), - GUI::MessBox::CloseButton); - return; - } - file.write(text.toUtf8()); - file.flush(); - file.close(); - d_ptr->appendDisplay(SuccessInfo, tr("The file was saved successfully.")); -} - -void TcpWidget::onAppendError(const QString &error) -{ - d_ptr->appendDisplay(ErrorInfo, error); -} - -void TcpWidget::buildConnect() -{ - connect(d_ptr->modelBox, &QComboBox::currentTextChanged, this, &TcpWidget::onModelChange); - connect(d_ptr->listenOrConnectButton, - &QPushButton::clicked, - this, - &TcpWidget::onListenOrConnect); - - auto *sendShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Return), this); - connect(sendShortcut, &QShortcut::activated, this, &TcpWidget::onSendData); - connect(d_ptr->sendButton, &QPushButton::clicked, this, &TcpWidget::onSendData); - - connect(d_ptr->autoSendBox, &QCheckBox::clicked, this, &TcpWidget::onAutoSend); - connect(&d_ptr->sendTime, &QTimer::timeout, this, &TcpWidget::onSendData); - - connect(d_ptr->autoConnectBox, - &QCheckBox::clicked, - this, - &TcpWidget::onAutoReconnectStartOrStop); - connect(&d_ptr->autoConnectTime, &QTimer::timeout, this, &TcpWidget::onAutoConnect); - - connect(d_ptr->sendConutButton, &QPushButton::clicked, this, [this] { - d_ptr->sendCount = 0; - d_ptr->setSendCount(); - }); - connect(d_ptr->recvConutButton, &QPushButton::clicked, this, [this] { - d_ptr->recvCount = 0; - d_ptr->setRecvCount(); - }); - connect(d_ptr->saveButton, &QPushButton::clicked, this, &TcpWidget::onSave); - connect(d_ptr->clearButton, &QPushButton::clicked, d_ptr->displayTextEdit, &QTextEdit::clear); -} - -void TcpWidget::resetTcpServer() -{ - auto port = d_ptr->portEdit->text(); - if (port.isEmpty()) { - GUI::MessBox::Warning(this, tr("Please enter the port number!"), GUI::MessBox::CloseButton); - d_ptr->portEdit->setFocus(); - return; - } - d_ptr->tcpServerPtr.reset(new TcpServer); - connect(d_ptr->tcpServerPtr.data(), &TcpServer::errorMessage, this, &TcpWidget::onAppendError); - connect(d_ptr->tcpServerPtr.data(), - &TcpServer::newClientInfo, - this, - &TcpWidget::onServerNewClient); - connect(d_ptr->tcpServerPtr.data(), - &TcpServer::disconnectClientInfo, - this, - &TcpWidget::onServerDisconnectClient); - connect(d_ptr->tcpServerPtr.data(), - &TcpServer::clientMessage, - this, - &TcpWidget::onServerRecvMessage); - auto ok = d_ptr->tcpServerPtr->listen(QHostAddress(d_ptr->localIPBox->currentText()), - static_cast(port.toUInt())); - onServerOnline(ok); -} - -void TcpWidget::resetTcpClient() -{ - auto port = d_ptr->portEdit->text(); - if (port.isEmpty()) { - GUI::MessBox::Warning(this, tr("Please enter the port number!"), GUI::MessBox::CloseButton); - d_ptr->portEdit->setFocus(); - d_ptr->autoConnectBox->setChecked(false); - onAutoReconnectStartOrStop(false); - return; - } - auto ip = d_ptr->serverIPEdit->text().trimmed(); - if (ip.isEmpty()) { - GUI::MessBox::Warning(this, tr("Please enter the ip address!"), GUI::MessBox::CloseButton); - d_ptr->serverIPEdit->setFocus(); - d_ptr->autoConnectBox->setChecked(false); - onAutoReconnectStartOrStop(false); - return; - } - d_ptr->tcpClientPtr.reset(new TcpClient(ip, static_cast(port.toUInt()))); - connect(d_ptr->tcpClientPtr.data(), &TcpClient::errorMessage, this, &TcpWidget::onAppendError); - connect(d_ptr->tcpClientPtr.data(), - &TcpClient::serverMessage, - this, - &TcpWidget::onClientRecvMessage); - connect(d_ptr->tcpClientPtr.data(), - &TcpClient::socketStateChanged, - this, - &TcpWidget::onClientStateChanged); - d_ptr->tcpClientPtr->connectToServer(); -} - -} // namespace Plugin diff --git a/src/plugins/tcpplugin/tcpwidget.h b/src/plugins/tcpplugin/tcpwidget.h deleted file mode 100644 index 6e4c23b..0000000 --- a/src/plugins/tcpplugin/tcpwidget.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef TCPWIDGET_H -#define TCPWIDGET_H - -#include - -namespace Plugin { - -class TcpWidget : public QWidget -{ - Q_OBJECT -public: - enum MessageType { Send, Recv, SuccessInfo, ErrorInfo }; - - explicit TcpWidget(QWidget *parent = nullptr); - ~TcpWidget() override; - -private slots: - void onModelChange(const QString & /*text*/); - void onListenOrConnect(bool /*state*/); - void onSendData(); - void onAppendError(const QString & /*error*/); - - void onServerOnline(bool /*state*/); - void onServerNewClient(const QString & /*clientInfo*/); - void onServerDisconnectClient(const QString & /*clientInfo*/); - void onServerRecvMessage(const QString & /*clientInfo*/, const QByteArray & /*bytes*/); - - void onClientStateChanged(const QString &text, bool onLine); - void onClientRecvMessage(const QByteArray & /*bytes*/); - void onAutoReconnectStartOrStop(bool /*state*/); - void onAutoConnect(); - - void onAutoSend(bool /*state*/); - void onSave(); - -private: - void buildConnect(); - - void resetTcpServer(); - void resetTcpClient(); - - class TcpWidgetPrivate; - QScopedPointer d_ptr; -}; - -} // namespace Plugin - -#endif // TCPWIDGET_H diff --git a/src/resource/qss/carshdialog.css b/src/resource/qss/carshdialog.css deleted file mode 100644 index 4c31c8d..0000000 --- a/src/resource/qss/carshdialog.css +++ /dev/null @@ -1,3 +0,0 @@ -#CrashLabel{ -font-size: 18px; -} diff --git a/src/resource/qss/common.css b/src/resource/qss/common.css index ab251bd..a403c58 100644 --- a/src/resource/qss/common.css +++ b/src/resource/qss/common.css @@ -3,109 +3,96 @@ *****************************************************************************/ QWidget { -color: #495060; -font-family: "PingFang SC"; -font-size: 12px; + color: #495060; + font-family: "PingFang SC"; + font-size: 12px; } QWidget:focus { -outline: none; /* 去掉得到焦点时的虚线框 */ + outline: none; + /* 去掉得到焦点时的虚线框 */ } /****************************************************************************** -* QPushButton +* QToolButton,QPushButton *****************************************************************************/ +QToolButton, QPushButton { -color: #495060; -background-color: white; -border: 1px solid #dddee1; -border-radius: 4px; -min-width: 70px; -min-height: 24px; -padding-left: 10px; -padding-right: 10px; -} - -QPushButton:!enabled { -background-color: rgb(215,215,215); + color: #495060; + background: white; + border: 1px solid #dddee1; + border-radius: 4px; + min-width: 70px; + min-height: 24px; + padding-left: 10px; + padding-right: 10px; } +QToolButton:hover, QPushButton:hover { -color: #57a3f3; -border-color: #57a3f3; -/* background-color: transparent; */ + color: #57a3f3; + border-color: #57a3f3; + /* background: transparent; */ } +QToolButton:pressed, QPushButton:pressed { -color: #2b85e4; -border-color: #2b85e4; -} - -QPushButton:default { -color: #fff; -background-color: #2d8cf0; -border-color: #2d8cf0; + color: #2b85e4; + border-color: #2b85e4; } -QPushButton:default:!enabled { -background-color: rgb(215,215,215); -} - -QPushButton:default:hover { -background-color: #57a3f3; -border-color: #57a3f3; -} - -QPushButton:default:pressed { -background-color: #2b85e4; -border-color: #2b85e4; +QToolButton:!enabled, +QPushButton:!enabled { + background: rgb(215,215,215); + color: white; } /****************************************************************************** * QAbstractItemView *****************************************************************************/ + QAbstractItemView { -border: 1px solid #dddee1; -alternate-background-color: rgb(250, 250, 250); + border: 1px solid #dddee1; + alternate-background-color: rgb(250, 250, 250); } QAbstractItemView::item { -border: 0px; -color: #495060; -border-bottom: 1px solid #e9eaec; -height: 24px; + border: 1px solid transparent; + color: #495060; + border-bottom: 1px solid #e9eaec; + height: 24px; } QAbstractItemView::item:hover { -background:#39a3ff; -color:white; + background: #39a3ff; + color: white; } QAbstractItemView::item:selected { -background:#39a3ff; -color:white; + background: #39a3ff; + color: white; } /* 可选单元格前的 checkbox */ QAbstractItemView::indicator { -width: 20px; -height: 20px; -border-width: 0 0 0 0; -margin-right: -4px; + width: 20px; + height: 20px; + border-width: 0 0 0 0; + margin-right: -4px; } QAbstractItemView::indicator { -border-image: url(:/common/icon/common/checkbox-unchecked-insensitive.png) 0 0 0 0 stretch stretch; + border-image: url(:/common/icon/common/checkbox-unchecked-insensitive.png) 0 0 0 0 stretch stretch; } QAbstractItemView::indicator:checked { -border-image: url(:/common/icon/common/checkbox-checked-selectionmode.png) 0 0 0 0 stretch stretch; + border-image: url(:/common/icon/common/checkbox-checked-selectionmode.png) 0 0 0 0 stretch stretch; } QAbstractItemView::icon { -width: 20px; -height: 20px; + width: 20px; + height: 20px; } /****************************************************************************** @@ -120,7 +107,8 @@ margin-top: 1ex; /* leave space at the top for the title QGroupBox::title { subcontrol-origin: margin; -/*subcontrol-position: top center; *//* position at the top center +/*subcontrol-position: top center; */ +/* position at the top center padding: 0 3px; } */ @@ -128,275 +116,287 @@ padding: 0 3px; /****************************************************************************** * QStackedWidget *****************************************************************************/ -QStackedWidget{ -background-color:#eeeeee; -padding:0; -border-bottom-right-radius: 5px; + +QStackedWidget { + background: #eeeeee; + padding: 0; + border-bottom-right-radius: 5px; } /****************************************************************************** * QHeaderView *****************************************************************************/ + QHeaderView::section { -background-color: rgb(45,45,45); -color: white; -border: 1px soild white; + background: rgb(45, 45, 45); + color: white; + border: 1px soild white; } /****************************************************************************** * QTabWidget *****************************************************************************/ -QTabWidget::pane{ -border-top: 2px solid #00C2C7CB; -position: absolute; -top: 10px; -background: #002d2f33; + +QTabWidget::pane { + border-top: 2px solid #00C2C7CB; + position: absolute; + top: 10px; + background: #002d2f33; } QTabWidget::tab-bar { -alignment: center; + alignment: center; } QTabBar::tab { -background: #00000000; -border: none; -border-bottom: 2px solid #3c3e42; -min-width: 10px;margin-right: 20px; -padding-left: 20px; -padding-right: 20px; -padding-top: 5px; -padding-bottom: 5px; -color: #686a6e; + background: #00000000; + border: none; + border-bottom: 2px solid #3c3e42; + min-width: 10px; + margin-right: 20px; + padding-left: 20px; + padding-right: 20px; + padding-top: 5px; + padding-bottom: 5px; + color: #686a6e; } QTabBar::tab:hover { -background: #3a3a3f; + background: #3a3a3f; } QTabBar::tab:selected { -border-color: #3a3a3f; -color:#dcdde4; -border-bottom-color: #dcdde4; + border-color: #3a3a3f; + color: #dcdde4; + border-bottom-color: #dcdde4; } /****************************************************************************** * QTreeWidget *****************************************************************************/ -QTreeWidget{ -max-width:250px; -border-width:0px; -font-size:18px; -color:white; -padding:0; -background-color:#818181; -border-bottom-left-radius: 5px; + +QTreeWidget { + max-width: 250px; + border-width: 0px; + font-size: 18px; + color: white; + padding: 0; + background: #818181; + border-bottom-left-radius: 5px; } -QTreeWidget::Item{ -border-width:0px; -min-height:45px; -padding:0; +QTreeWidget::Item { + border-width: 0px; + min-height: 45px; + padding: 0; } -QTreeWidget::Item:hover{ -background: #0066ff; -padding:0; +QTreeWidget::Item:hover { + background: #0066ff; + padding: 0; } -QTreeWidget::item:selected{ -background:#444444; -color:#0084ff; +QTreeWidget::item:selected { + background: #444444; + color: #0084ff; } -QTreeWidget::item:selected:active{ -border-width:0px; -background:#444444; -padding:0; +QTreeWidget::item:selected:active { + border-width: 0px; + background: #444444; + padding: 0; } /****************************************************************************** * QListWidget *****************************************************************************/ -QListWidget{ -border-width:0px; -padding:0; -background:white; -border-bottom-left-radius: 5px; +QListWidget { + border: 1px solid #dddee1; + padding: 0; + background: white; + border-bottom-left-radius: 5px; } -QListWidget::Item{ -border-width:0px; -padding:0; +QListWidget::Item { + border: 1px solid transparent; + padding: 0; } -QListWidget::Item:hover{ -background: #39a3ff; -padding:0; -color:white; +QListWidget::Item:hover { + background: #39a3ff; + padding: 0; + color: white; } -QListWidget::item:selected{ -background:#39a3ff; -color:white; +QListWidget::item:selected { + background: #39a3ff; + color: white; } -QListWidget::item:selected:active{ -border-width:0px; -background:#0066ff; -padding:0; -color:white; +QListWidget::item:selected:active { + border-width: 0px; + background: #0066ff; + padding: 0; + color: white; } /****************************************************************************** * QLineEdit *****************************************************************************/ + QLineEdit { -height: 24px; -font-size: 12px; -border: 1px solid #dddee1; -border-radius: 4px; -color: #495060; -background-color: #fff; -padding: 0 4px; + height: 24px; + font-size: 12px; + border: 1px solid #dddee1; + border-radius: 4px; + color: #495060; + background: #fff; + padding: 0 4px; } QLineEdit:focus { -border-color: #57a3f3; + border-color: #57a3f3; } QLineEdit:read-only { -background: rgb(215,215,215); -color: #495060; -border-color: #dddee1; + background: rgb(215, 215, 215); + color: #495060; + border-color: #dddee1; } QLineEdit:read-only:focus { -border-color: #dddee1; + border-color: #dddee1; } QLineEdit[echoMode="2"] { -lineedit-password-character: 9679; + lineedit-password-character: 9679; } /****************************************************************************** * QTextBrowser *****************************************************************************/ -QTextBrowser{ -font-size: 12px; -border: 1px solid #dddee1; -border-radius: 4px; -color: #495060; -background-color: #fff; -padding: 0 4px; + +QTextBrowser { + font-size: 12px; + border: 1px solid #dddee1; + border-radius: 4px; + color: #495060; + background: #fff; + padding: 0 4px; } + QTextBrowser:focus { -border-color: #57a3f3; + border-color: #57a3f3; } /****************************************************************************** * QTextEdit *****************************************************************************/ -QTextEdit{ -font-size: 12px; -border: 1px solid #dddee1; -border-radius: 4px; -color: #495060; -background-color: #fff; -padding: 0 4px; + +QTextEdit { + font-size: 12px; + border: 1px solid #dddee1; + border-radius: 4px; + color: #495060; + background: #fff; + padding: 0 4px; } QTextEdit:focus { -border-color: #57a3f3; -} + border-color: #57a3f3; +} QTextEdit:read-only { -color: #495060; -border-color: #dddee1; + color: #495060; + border-color: #dddee1; } /****************************************************************************** * QCheckBox&QRadioButton *****************************************************************************/ -QCheckBox::indicator, QRadioButton::indicator { -subcontrol-origin: border; -subcontrol-position: left center; -width: 24px; -height: 24px; -border-width: 0 0 0 0; +QCheckBox::indicator, +QRadioButton::indicator { + subcontrol-origin: border; + subcontrol-position: left center; + + width: 20px; + height: 20px; + border-width: 0 0 0 0; } QCheckBox::indicator { -border-image: url(:/common/icon/common/checkbox-unchecked-insensitive.png); + border-image: url(":/common/icon/common/checkbox-unchecked-insensitive@2.png"); } QCheckBox::indicator:checked { -border-image: url(:/common/icon/common/checkbox-checked-selectionmode.png); + border-image: url(":/common/icon/common/checkbox-checked-selectionmode@2.png"); } QRadioButton::indicator { -border-image: url(:/common/icon/common/radio-unchecked-insensitive.png); + border-image: url(":/common/icon/common/radio-unchecked-insensitive@2.png"); } QRadioButton::indicator:checked { -border-image: url(:/common/icon/common/radio-checked.png); + border-image: url(":/common/icon/common/radio-checked@2.png"); } /****************************************************************************** * QComboBox *****************************************************************************/ + QComboBox { -height: 24px; -min-width: 90px; -font-size: 12px; -border: 1px solid #dddee1; -border-radius: 4px; -color: #495060; -background-color: #fff; -padding: 0 4px; + height: 24px; + min-width: 90px; + font-size: 12px; + border: 1px solid #dddee1; + border-radius: 4px; + color: #495060; + background: #fff; + padding: 0 4px; } -QComboBox:focus { -border-color: #57a3f3; +QComboBox:!editable{ + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #E1E1E1, stop: 0.4 #DDDDDD, stop: 0.5 #D8D8D8, stop: 1.0 #D3D3D3); } -QComboBox:editable { -background: white; +QComboBox:!editable:on{ + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #D3D3D3, stop: 0.4 #D8D8D8, stop: 0.5 #DDDDDD, stop: 1.0 #E1E1E1); } -QComboBox::drop-down:editable { -background: white; +QComboBox:focus { + border-color: #57a3f3; } -QComboBox::drop-down:editable:on { -background: white; +QComboBox:editable { + background: white; } -QComboBox:!editable{ -background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, -stop: 0 #E1E1E1, stop: 0.4 #DDDDDD, stop: 0.5 #D8D8D8, stop: 1.0 #D3D3D3); +QComboBox::drop-down:editable { + background: white; } -QComboBox:!editable:on{ -background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, -stop: 0 #D3D3D3, stop: 0.4 #D8D8D8, stop: 0.5 #DDDDDD, stop: 1.0 #E1E1E1); +QComboBox::drop-down:editable:on { + background: white; } QComboBox:on { -padding-top: 3px; -padding-left: 4px; + padding-top: 3px; + padding-left: 4px; } QComboBox::drop-down { -width: 12px; -height: 14px; -margin-right: 2px; -margin-top: 2px; -subcontrol-origin: border; -subcontrol-position: right center; -border-width: 0 0 0 0; -border-image: url(:/common/icon/common/combobox-arrow.png); + width: 12px; + height: 14px; + margin-right: 2px; + margin-top: 2px; + subcontrol-origin: border; + subcontrol-position: right center; + border-width: 0 0 0 0; + border-image: url(:/common/icon/common/combobox-arrow.png); } /*QComboBox::drop-down:hover { @@ -406,78 +406,83 @@ border-image: url(:/common/icon/common/arrow-down-prelight.png); /****************************************************************************** * QSpinBox, QDateEdit, QTimeEdit, QDateTimeEdit *****************************************************************************/ + QAbstractSpinBox { -height: 24px; -min-width: 60px; -font-size: 12px; -border: 1px solid #dddee1; -border-radius: 4px; -color: #495060; -background-color: #fff; -padding: 0 1px; -qproperty-alignment: AlignCenter; + height: 24px; + min-width: 60px; + font-size: 12px; + border: 1px solid #dddee1; + border-radius: 4px; + color: #495060; + background: #fff; + padding: 0 1px; + qproperty-alignment: AlignCenter; } QAbstractSpinBox:focus { -border-color: #57a3f3; + border-color: #57a3f3; } -QAbstractSpinBox::down-button, QAbstractSpinBox::up-button { -subcontrol-origin: border; -width: 16px; -height: 16px; -border-width: 0 0 0 0; +QAbstractSpinBox::down-button, +QAbstractSpinBox::up-button { + subcontrol-origin: border; + width: 16px; + height: 16px; + border-width: 0 0 0 0; } QAbstractSpinBox::down-button { -subcontrol-position: center left; -margin-left: 5px; -border-image: url(:/common/icon/common/minus.png); + subcontrol-position: center left; + margin-left: 5px; + border-image: url(:/common/icon/common/minus.png); } QAbstractSpinBox::up-button { -subcontrol-position: center right; -margin-right: 5px; -border-image: url(:/common/icon/common/plus.png); + subcontrol-position: center right; + margin-right: 5px; + border-image: url(:/common/icon/common/plus.png); } /****************************************************************************** * QScrollBar *****************************************************************************/ + QScrollBar:vertical { -width: 6px; -background: #e9eaec; -padding-bottom: 0px; + width: 6px; + background: #e9eaec; + padding-bottom: 0px; } QScrollBar:horizontal { -height: 6px; -background: #e9eaec; -padding-left: 0px; + height: 6px; + background: #e9eaec; + padding-left: 0px; } QScrollBar::handle:vertical, QScrollBar::handle:horizontal { -background: #AAA; + background: #AAA; } QScrollBar::handle:vertical { -min-height: 50px; + min-height: 50px; } QScrollBar::handle:horizontal { -min-width: 50px; + min-width: 50px; } QScrollBar::handle:vertical:hover, QScrollBar::handle:horizontal:hover { -background: #888; + background: #888; } -QScrollBar::sub-line:vertical, QScrollBar::add-line:vertical, -QScrollBar::sub-line:horizontal, QScrollBar::add-line:horizontal { -width: 0; -height: 0; +QScrollBar::sub-line:vertical, +QScrollBar::add-line:vertical, +QScrollBar::sub-line:horizontal, +QScrollBar::add-line:horizontal { + width: 0; + height: 0; } /****************************************************************************** @@ -485,82 +490,169 @@ height: 0; *****************************************************************************/ QMenu { -background-color:white; -font-size: 13px; + background: white; + font-size: 13px; } QMenu::item { -background-color:transparent; + background: transparent; } QMenu::item:selected { -background-color: #009dd1; -color: white; + background: #009dd1; + color: white; } QMenu::icon::checked { -background:red; -border:3px inset red; -position:absolute; + background: red; + border: 3px inset red; + position: absolute; -top:2px; -right:2px; -bottom:4px; -left:4px; + top: 2px; + right: 2px; + bottom: 4px; + left: 4px; } QMenu::separator { -height:2px; -background:lightblue; -margin-left:10px; -margin-right:5px; + height: 2px; + background: lightblue; + margin-left: 10px; + margin-right: 5px; } QMenu::indicator { -width:20px; -height:20px; + width: 20px; + height: 20px; } /* non-exclusive indicator = check box style indicator (see QActionGroup::setExclusive) */ QMenu::indicator:non-exclusive:unchecked { -image: url(:/common/icon/common/checkbox-unchecked-insensitive.png); + image: url(:/common/icon/common/checkbox-unchecked-insensitive.png); } QMenu::indicator:non-exclusive:unchecked:selected { -image: url(:/common/icon/common/checkbox-unchecked-insensitive.png); + image: url(:/common/icon/common/checkbox-unchecked-insensitive.png); } QMenu::indicator:non-exclusive:checked { -image: url(:/common/icon/common/checkbox-checked-selectionmode.png); + image: url(:/common/icon/common/checkbox-checked-selectionmode.png); } QMenu::indicator:non-exclusive:checked:selected { -image: url(:/common/icon/common/checkbox-checked-selectionmode.png); + image: url(:/common/icon/common/checkbox-checked-selectionmode.png); } QMenuBar { -background-color:transparent; -font-size: 14px; + background: transparent; + font-size: 14px; } QMenuBar::item { -background:transparent; -border-width: 1px; -border-style: none none solid none; -border-color: black; + background: transparent; + border-width: 1px; + border-style: none none solid none; + border-color: black; } /****************************************************************************** * QProgressBar *****************************************************************************/ -QProgressBar{ -max-height: 5px; -border:2px solid lightblue; -border-radius: 1px; -background-color:white; +QProgressBar { + text-align: center; + border: 1px solid lightblue; + border-radius: 5px; + background: white; +} + +QProgressBar::chunk { + background: rgb(56, 156, 255); + border-radius: 5px; +} + +/****************************************************************************** +* QSlider +*****************************************************************************/ + +QSlider:horizontal { + height: 24px; +} + +QSlider::groove:horizontal { + subcontrol-origin: content; + + /* the groove expands to the size of the slider by default. by giving it a height, it has a fixed size */ + height: 6px; + border-radius: 3px; +} + +QSlider::handle:horizontal { + background-color: white; + width: 12px; + border-radius: 9px; + border: 3px solid #57a3f3; + + /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ + margin: -6px 0; +} + +QSlider::sub-page:horizontal { + background: #57a3f3; + border-radius: 3px; +} + +QSlider::add-page:horizontal { + background-color: white; + border-radius: 3px; +} + +QSlider:vertical { + width: 24px; +} + +QSlider::groove:vertical { + subcontrol-origin: content; + + /* the groove expands to the size of the slider by default. by giving it a height, it has a fixed size */ + width: 6px; + border-radius: 3px; +} + +QSlider::handle:vertical { + background-color: white; + height: 12px; + border-radius: 9px; + border: 3px solid #57a3f3; + + /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ + margin: 0 -6px; +} + +QSlider::sub-page:vertical { + background: #e9eaec; + border-radius: 3px; +} + +QSlider::add-page:vertical { + background-color: #57a3f3; + border-radius: 3px; +} + +/****************************************************************************** +* QToolBox +*****************************************************************************/ + +QToolBox::tab { + background: white; + border-radius: 4px; + color: #495060; +} + +QToolBox::tab:selected { + color: #2b85e4; } -QProgressBar::chunk{ -background-color:rgb(56,156,255); +QToolBoxButton { + min-height: 35px; } diff --git a/src/resource/qss/commonwidget.css b/src/resource/qss/commonwidget.css index c46bc2c..532347b 100644 --- a/src/resource/qss/commonwidget.css +++ b/src/resource/qss/commonwidget.css @@ -1,164 +1,167 @@ -#BlueButton{ -color: white; -background-color: #2d8cf0; -border-color: #2d8cf0; +#BlueButton { + color: white; + background-color: #2d8cf0; + border-color: #2d8cf0; } -#BlueButton:hover{ -background-color: #57a3f3; -border-color: #57a3f3; +#BlueButton:hover { + background-color: #57a3f3; + border-color: #57a3f3; } -#BlueButton:pressed{ -background-color: #2b85e4; -border-color: #2b85e4; +#BlueButton:pressed { + background-color: #2b85e4; + border-color: #2b85e4; } #BlueButton:!enabled { -color: #495060; -background-color: rgb(215,215,215); -border-color: rgb(215,215,215); + color: #495060; + background-color: rgb(215,215,215); + border-color: rgb(215,215,215); } -#GrayButton{ -color: white; -background-color:rgb(192,192,192); -border-color: rgb(192,192,192); +#GrayButton { + color: white; + background-color: rgb(192, 192, 192); + border-color: rgb(192, 192, 192); } -#GrayButton:hover{ -background-color:#878787; -border-color:#878787; +#GrayButton:hover { + background-color: #878787; + border-color: #878787; } -#GrayButton:pressed{ -background-color:#878787; -border-color:#878787; +#GrayButton:pressed { + background-color: #878787; + border-color: #878787; } -#GrayButton:checked{ -background-color: #2b85e4; -border-color: #2b85e4; +#GrayButton:checked { + background-color: #2b85e4; + border-color: #2b85e4; } /****************************************************************************** * CommonWidget *****************************************************************************/ + #CommonCentralWidget { -background: #f7f7f7; -border-bottom-left-radius: 5px; -border-bottom-right-radius: 5px; + background: #f7f7f7; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; } /****************************************************************************** * MesaaBox *****************************************************************************/ + #InfoLabel { -min-width: 48px; -max-width: 48px; -min-height: 48px; -max-height: 48px; -border-width: 0; -border-image: url(resource/image/base/controls/info.png); + min-width: 48px; + max-width: 48px; + min-height: 48px; + max-height: 48px; + border-width: 0; + border-image: url(resource/image/base/controls/info.png); } #WarningLabel { -min-width: 48px; -max-width: 48px; -min-height: 48px; -max-height: 48px; -border-width: 0; -border-image: url(resource/image/base/controls/warn.png); + min-width: 48px; + max-width: 48px; + min-height: 48px; + max-height: 48px; + border-width: 0; + border-image: url(resource/image/base/controls/warn.png); } #MessageLabel { -font-size: 20px; -qproperty-alignment: AlignCenter; + font-size: 20px; + qproperty-alignment: AlignCenter; } #MessBtnWidget { -background: white; -height: 35px; -max-height: 40px; -border-bottom-left-radius: 5px; -border-bottom-right-radius: 5px; -border-width: 1px; -border-style: solid none none none; -border-color: rgb(231, 231, 231); + background: white; + height: 35px; + max-height: 40px; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + border-width: 1px; + border-style: solid none none none; + border-color: rgb(231, 231, 231); } /****************************************************************************** * EditComboBox *****************************************************************************/ -#deleteButton{ -width: 20px; -height: 20px; -min-width: 20px; -max-width: 20px; -min-height: 20px; -max-height: 20px; -border-width: 0; -border-image: url(resource/image/base/controls/delete.png); +#deleteButton { + width: 20px; + height: 20px; + min-width: 20px; + max-width: 20px; + min-height: 20px; + max-height: 20px; + border-width: 0; + border-image: url(resource/image/base/controls/delete.png); } -.EditComboBox{ -height: 40px; -font-size: 14px; -border-style: none none solid none; -border-radius: 0px; +.EditComboBox { + height: 40px; + font-size: 14px; + border-style: none none solid none; + border-radius: 0px; } -.EditComboBox QAbstractItemView::item{ -min-height: 40px; +.EditComboBox QAbstractItemView::item { + min-height: 40px; } -#UsernameLabel{ -font-size:14px; +#UsernameLabel { + font-size: 14px; } -#UsernameLabel:hover{ -color: white; +#UsernameLabel:hover { + color: white; } /****************************************************************************** * PasswordLineEdit *****************************************************************************/ -#ShowPasswordButton{ -border-width: 0; -background-color: transparent; -border-image: url(resource/image/base/controls/hidePassword.png); -/*qproperty-icon: url(image/hidePassword.png)off, url(image/showPassword.png)on;*/ + +#ShowPasswordButton { + border-width: 0; + background-color: transparent; + border-image: url(resource/image/base/controls/hidePassword.png); + /*qproperty-icon: url(image/hidePassword.png)off, url(image/showPassword.png)on;*/ } -#ShowPasswordButton:checked{ -border-image: url(resource/image/base/controls/showPassword.png); +#ShowPasswordButton:checked { + border-image: url(resource/image/base/controls/showPassword.png); } -.PasswordLineEdit{ -height: 40px; -font-size: 14px; -border-style: none none solid none; -border-radius: 0px; +.PasswordLineEdit { + height: 40px; + font-size: 14px; + border-style: none none solid none; + border-radius: 0px; } /****************************************************************************** * CommonWidget *****************************************************************************/ -#TitleWidget{ -background: white; -border-top-left-radius: 5px; -border-top-right-radius: 5px; -max-height: 40px; -border-width: 1px; -border-style: none none solid none; -border-color: rgb(231, 231, 231); -} - -#TitleButton{ -border: none; -font-size:16px; -min-width: 0px; -min-height: 0px; -} +#TitleWidget { + background: white; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + max-height: 40px; + border-width: 1px; + border-style: none none solid none; + border-color: rgb(231, 231, 231); +} + +#TitleButton { + border: none; + font-size: 16px; + min-width: 0px; + min-height: 0px; +} \ No newline at end of file diff --git a/src/resource/qss/corewidget.css b/src/resource/qss/corewidget.css deleted file mode 100644 index 9a431ce..0000000 --- a/src/resource/qss/corewidget.css +++ /dev/null @@ -1,6 +0,0 @@ -#HomeLabel { -font-weight: bold; -font-size: 30px; -font-weight: bold; -} - diff --git a/src/resource/qss/sidebarbutton.css b/src/resource/qss/sidebarbutton.css index a31b234..62bd40a 100644 --- a/src/resource/qss/sidebarbutton.css +++ b/src/resource/qss/sidebarbutton.css @@ -1,44 +1,46 @@ -#MenuWidget{ -min-width: 200px; -max-width: 200px; -padding: 0px; -background: rgb(54, 62, 78); -border-bottom-left-radius: 5px; +#MenuWidget { + min-width: 200px; + max-width: 200px; + padding: 0px; + background: rgb(54, 62, 78); + border-bottom-left-radius: 5px; } -.GroupButton, .GroupItemButton { -border: 0; -color: #EEE; -border-radius: 0; -background: rgb(54, 62, 78); +.GroupButton, +.GroupItemButton { + border: 0; + color: #EEE; + border-radius: 0; + background: rgb(54, 62, 78); } -.GroupButton:hover, .GroupItemButton:hover { -border-color: #389cff; -background: #389cff; -color: white; +.GroupButton:hover, +.GroupItemButton:hover { + border-color: #389cff; + background: #389cff; + color: white; } .GroupButton { -height: 40px; -font-size: 16px; -text-align: left; -padding-left: 6px; + height: 40px; + font-size: 16px; + text-align: left; + padding-left: 6px; } .GroupItemButton { -height: 38px; -font-size: 14px; -text-align: left; + height: 38px; + font-size: 14px; + text-align: left; -padding-left: 35px; -border-width: 3px; -border-style: none none none solid; -border-color: rgb(54, 62, 78); + padding-left: 35px; + border-width: 3px; + border-style: none none none solid; + border-color: rgb(54, 62, 78); } .GroupItemButton:checked { -color: #39a3ff; -background: rgb(85,79,73); -border-color: #39a3ff; -} + color: #39a3ff; + background: rgb(85, 79, 73); + border-color: #39a3ff; +} \ No newline at end of file diff --git a/src/resource/qss/specific.css b/src/resource/qss/specific.css new file mode 100644 index 0000000..d329685 --- /dev/null +++ b/src/resource/qss/specific.css @@ -0,0 +1,13 @@ +#CrashLabel { + font-size: 18px; +} + +#HomeLabel { + font-weight: bold; + font-size: 30px; + font-weight: bold; +} + +#AboutWidget QLabel { + font-size: 14px; +} \ No newline at end of file diff --git a/src/resource/resource.qrc b/src/resource/resource.qrc index c1e3eb1..dc1dc94 100644 --- a/src/resource/resource.qrc +++ b/src/resource/resource.qrc @@ -90,8 +90,7 @@ qss/common.css qss/commonwidget.css qss/sidebarbutton.css - qss/carshdialog.css - qss/corewidget.css + qss/specific.css icon/common/checkbox-checked.png diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index b63aa6d..ea934b9 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -17,6 +17,8 @@ set(PROJECT_SOURCES mimetypes/mimetypeparser.cpp mimetypes/mimeutils.cpp algorithm.h + appinfo.cc + appinfo.hpp benchmarker.cpp benchmarker.h countdownlatch.cc diff --git a/src/utils/appinfo.cc b/src/utils/appinfo.cc new file mode 100644 index 0000000..46d5e5e --- /dev/null +++ b/src/utils/appinfo.cc @@ -0,0 +1,3 @@ +#include "appinfo.hpp" + +namespace Utils {} diff --git a/src/utils/appinfo.hpp b/src/utils/appinfo.hpp new file mode 100644 index 0000000..c54d162 --- /dev/null +++ b/src/utils/appinfo.hpp @@ -0,0 +1,17 @@ +#ifndef APPINFO_HPP +#define APPINFO_HPP + +#include + +namespace Utils { + +static const QVersionNumber version = QVersionNumber(0, 1, 1); +static const QString appName = "Qt-App"; +static const QString crashName = "CrashReport"; +static const QString organzationName = "Youth"; +static const QString organizationDomain = "Youth"; +static const QString copyright = "Copyright 2017-2024 Youth. All rights reserved."; + +} // namespace Utils + +#endif // APPINFO_HPP diff --git a/src/utils/logasync.cpp b/src/utils/logasync.cpp index 6fe3af1..9b4fe16 100644 --- a/src/utils/logasync.cpp +++ b/src/utils/logasync.cpp @@ -177,8 +177,8 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt #ifndef QT_NO_DEBUG contexInfo = QString("File:(%1) Line:(%2)").arg(context.file).arg(context.line); #endif - const QString message = QString("%1 %2 [%3] %4 - %5\n") - .arg(dataTimeString, threadId, level, msg, contexInfo); + const auto message = QString("%1 %2 [%3] %4 - %5\n") + .arg(dataTimeString, threadId, level, msg, contexInfo); switch (instance->orientation()) { case LogAsync::Orientation::Std: @@ -198,8 +198,15 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt } } -struct LogAsyncPrivate +class LogAsync::LogAsyncPrivate { +public: + explicit LogAsyncPrivate(LogAsync *q) + : q_ptr(q) + {} + + LogAsync *q_ptr; + QtMsgType msgType = QtWarningMsg; LogAsync::Orientation orientation = LogAsync::Orientation::Std; QWaitCondition waitCondition; @@ -252,7 +259,7 @@ void LogAsync::run() LogAsync::LogAsync(QObject *parent) : QThread(parent) - , d_ptr(new LogAsyncPrivate) + , d_ptr(new LogAsyncPrivate(this)) { qInstallMessageHandler(messageHandler); } diff --git a/src/utils/logasync.h b/src/utils/logasync.h index 5f13886..222a859 100644 --- a/src/utils/logasync.h +++ b/src/utils/logasync.h @@ -30,7 +30,6 @@ private slots: QScopedPointer d_ptr; }; -struct LogAsyncPrivate; class UTILS_EXPORT LogAsync : public QThread { Q_OBJECT @@ -56,6 +55,7 @@ class UTILS_EXPORT LogAsync : public QThread explicit LogAsync(QObject *parent = nullptr); ~LogAsync() override; + class LogAsyncPrivate; QScopedPointer d_ptr; SINGLETON(LogAsync) diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index 2552738..b7e74fc 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -86,13 +86,17 @@ auto compilerString() -> QString return QLatin1String(""); } -void Utils::printBuildInfo() +auto Utils::systemInfo() -> QString { - // qInfo() << QSysInfo::buildAbi() << QSysInfo::machineUniqueId() - // << QOperatingSystemVersion::current(); - const QString info = QString("Qt %1 (%2, %3 bit)") - .arg(qVersion(), compilerString(), QString::number(QSysInfo::WordSize)); - qInfo() << QCoreApplication::translate("Utils", "Build with: ") << info; + auto text = QString("%1 (%2) on %3 (%4)") + .arg(QSysInfo::prettyProductName(), + QSysInfo::kernelVersion(), + QSysInfo::currentCpuArchitecture(), + QSysInfo::machineHostName()) + + "\n" + + QString("Build with: Qt %1 (%2, %3)") + .arg(qVersion(), compilerString(), QSysInfo::buildAbi()); + return text; } void Utils::setHighDpiEnvironmentVariable() @@ -398,3 +402,8 @@ void Utils::setMacComboBoxStyle(QWidget *parent) comboBox->setStyle(QStyleFactory::create("Fusion")); } } + +void Utils::quitApplication() +{ + QMetaObject::invokeMethod(qApp, &QApplication::quit, Qt::QueuedConnection); +} diff --git a/src/utils/utils.h b/src/utils/utils.h index 2582997..e5ab0a7 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -12,8 +12,9 @@ class QMenu; namespace Utils { -UTILS_EXPORT void printBuildInfo(); +UTILS_EXPORT auto systemInfo() -> QString; UTILS_EXPORT void setHighDpiEnvironmentVariable(); +UTILS_EXPORT void quitApplication(); UTILS_EXPORT void setUTF8Code(); UTILS_EXPORT void setQSS(const QStringList &qssFilePaths); UTILS_EXPORT void loadFonts(const QString &fontPath); diff --git a/src/utils/utils.pro b/src/utils/utils.pro index 4e8917b..88c5be7 100644 --- a/src/utils/utils.pro +++ b/src/utils/utils.pro @@ -11,6 +11,7 @@ LIBS += -ldbghelp } SOURCES += \ + appinfo.cc \ benchmarker.cpp \ countdownlatch.cc \ hostosinfo.cpp \ @@ -24,6 +25,7 @@ SOURCES += \ HEADERS += \ algorithm.h \ + appinfo.hpp \ benchmarker.h \ countdownlatch.hpp \ executeondestruction.h \ diff --git a/translations/qt-app_zh_CN.ts b/translations/qt-app_zh_CN.ts index d0250d5..a9df373 100644 --- a/translations/qt-app_zh_CN.ts +++ b/translations/qt-app_zh_CN.ts @@ -24,17 +24,17 @@ Crash::CrashWidgets - + Path of Crash File - + Restart - + Close @@ -53,27 +53,27 @@ Contact Me - Email: GUI::CommonWidget - + Minimize - + Maximize - + Restore - + Close - + TitleBar @@ -94,47 +94,38 @@ Contact Me - Email: HashWidgetPrivate - + Select File - + Select file to calculate hash. - + Calculate - + Calculate hash. - - - Input: - - - Output: + If Input String is file path and file exists, calculate hash of file. Otherwise calculate hash of Input String. - - If Input String is file path and file exists, calculate hash of file. Otherwise calculate hash of Input String. + + Input: - - - MainWindowPrivate - - - Settings + + Output: @@ -157,406 +148,199 @@ Contact Me - Email: - Plugin::HashPluginWidget + Plugin::AboutPluginWidget - - Hash Tool + + About - Plugin::HashWidget - - - Select File - - - - - Input is empty! - - - - - Hash thread is running! - - - - - Calculating... - - + Plugin::AboutWidget - - Calculate + + About Qt - Plugin::MainWindow + Plugin::GuiPluginWidget - - Hello World! + + Gui + + + Plugin::GuiWidget - - Systray, I couldn't detect any system tray on this system. + + Default - - Quit + + Blue - - This is an Qt-App. + + Gray - - Common Tools + + + Radio - - About + + + Check - - About Plugins + + Buttons - - About Qt + + Boxes - - - Plugin::SerialPluginWidget - - Serial Tool + + Bars - Plugin::SerialPort - - - Serial Error: PortName is Empty! - - + Plugin::HashPluginWidget - - Serial Error[%1]: %2. + + Hash - Plugin::SerialWidget - - - >> Serial Send: - - - - - >> Serial Recv: - - - - - - >> Prompt Message: - - - - - Time [%1] %2 %3 - - - - - Send: %1 Bytes - - - - - Recv: %1 Bytes - - - - - Settings - - - - - Close - - - - - Open - - - - - Serial Open! - - - - - Serial Close! - - + Plugin::HashWidget - - Open File + + Select File - - Text Files(*.txt) + + Input is empty! - - No file saved. + + Hash thread is running! - - Write File: Can't open file: - %1 ! + + Calculating... - - The file was saved successfully. + + Calculate - Plugin::TcpClient - - - [Client Error]: %1,%2 - - - - - The socket is not connected. - - - - - The socket is performing a host name lookup. - - - - - The socket has started establishing a connection. - - - - - A connection is established. - - - - - The socket is bound to an address and port. - - + Plugin::HelloPluginWidget - - The socket is about to close (data may still be waiting to be written). - - - - - For internal use only. + + Hello - Plugin::TcpPluginWidget + Plugin::HelloWidget - - Tcp Tool + + Hello there! How's your day going so far? - - - Plugin::TcpServer - - There is currently no client online, sending failed, please stop sending! + + Hi, it's great to see you again! - - TCPServer accept Error: %1 + + Good morning/afternoon/evening! How are you? - - - %1 : %2 + + Hey, hope you're having a wonderful day! - - Client [%1 : %2] Error: %3. + + Previous - - Client [%1 : %2] : + + Next - Plugin::TcpWidget - - - - TcpServer - - - - - Local IP List: - - - - - Local Port: - - - - - - Listen - - - - - - TcpClient - - - - - Server IP: - - - - - - Connect - - - - - Server Port: - - - - - The socket is not connected. - - - - - No client is currently online, please stop sending invalid! - - - - - Connect All - - - - - Send To All Online Clients: %1. - - - - - Send To Clients [%1] : %2. - - - - - Stop Listen - - - - - Server Online! - - - - - Server Offline! - - - - - Online. - - + Plugin::MainWindow - - Offline. + + Hello World! - - Disconnect + + Systray, I couldn't detect any system tray on this system. - - Open File + + Quit - - Text Files(*.txt) + + This is an Qt-App. - - No file saved. + + Main - - Write File: Can't open file: - %1 ! + + Help - - The file was saved successfully. + + Settings - - - Please enter the port number! + + Plugins + + + Plugin::SystemInfoPluginWidget - - Please enter the ip address! + + System Info @@ -583,12 +367,12 @@ Contact Me - Email: - + Plugin Details of %1 - + Plugin Errors of %1 @@ -1106,209 +890,6 @@ will also disable the following plugins: - - SerialWidgetPrivate - - - Send - - - - - Search Available Serial - - - - - Hex - - - - - Auto Delivery - - - - - ms - - - - - Send: 0 Bytes - - - - - Receive: 0 Bytes - - - - - Save Data - - - - - Clear Screen - - - - - Data Display - - - - - Data Send - - - - - Port: - - - - - Baud Rate: - - - - - Data Bits: - - - - - Stop Bits: - - - - - Parity: - - - - - Flow Control: - - - - - TcpWidgetPrivate - - - Send - - - - - Local IP List: - - - - - Please enter the server IP address. - - - - - Local Port: - - - - - Please enter the port number. - - - - - Hex - - - - - Auto Delivery - - - - - - ms - - - - - Connect All - - - - - Auto Reconnect - - - - - Save Data - - - - - Clear Screen - - - - - Send: %1 Bytes - - - - - Recv: %1 Bytes - - - - - >> Network Send: - - - - - >> Network Recv: - - - - - - >> Prompt Message: - - - - - Time [%1] %2 %3 - - - - - Data Display - - - - - Data Send - - - - - Mode: - - - - - Settings - - - Utils @@ -1322,27 +903,22 @@ will also disable the following plugins: - - Build with: - - - - + Cannot remove file "%1": %2 - + Cannot remove directory "%1": %2 - + Cannot open the file: %1 - + %1 Offset: %2 diff --git a/vcpkg.json b/vcpkg.json index 0ca4a79..90aa24e 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", "name": "qt-app", - "version": "0.0.1", + "version": "0.1.1", "description": "manifest", "dependencies": [ "breakpad"