当前位置: 首页 > article >正文

【QT常用技术讲解】任务栏图标+socket网络服务+开机自启动

前言

        首先看网络编程的定义:两个不同主机设备之间的进程通信。C/S(Client-Server)是早期非常典型的软件架构,C/S架构虽然简单,但却非常适用于桌面图形化的QT项目。

        本篇的QT项目是从真实的项目中简化出来,满足很多相似的场景:创建一个TCP服务,接收到消息后,通过多线程执行后台CMD命令行,并且自动把程序放到系统自启动目录中。

        覆盖到QT的知识点:任务栏托盘、右键菜单、TCP服务、多线程。

功能讲解

1、创建系统托盘

// 创建托盘图标
    QSystemTrayIcon *trayIcon = new QSystemTrayIcon();
    trayIcon->setIcon(QIcon(":/index/img/default.png")); // 设置托盘图标
// 创建上下文菜单
    QMenu *menu = new QMenu();
// 添加菜单项
    menu->addAction(exitAction);
    trayIcon->setContextMenu(menu);

    // 显示托盘图标
    trayIcon->show();

2、托盘右键菜单

// 创建上下文菜单
    QMenu *menu = new QMenu();
    QAction *showAction = new QAction("执行后台命令", &app);//测试用
    // 连接信号与槽
    QObject::connect(showAction, &QAction::triggered, []() {
        //测试:打开谷歌浏览器
        QString cmd = "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe";
        cmdrun act;
        act.Run(cmd);
    });

    QAction *exitAction = new QAction("退出", &app);
    QObject::connect(exitAction, &QAction::triggered, &app, &QApplication::quit);

    // 添加菜单项
    menu->addAction(showAction);
    menu->addAction(exitAction);
    trayIcon->setContextMenu(menu);

showAction事件只是用来测试点击右键之后,执行本地的CMD命令是否正常,适合用来调用第三方软件达到指定的目的(比如通过window的powershell脚本获取指定EXE执行程序的图标),调通之后就可以注释掉,用TCP创建的服务执行本地的CMD命令。

3、TCP服务

首先,需要在.pro文件中加上network,编译时才不会报错

QT       += core gui network

tcp服务头文件:

#ifndef MYTCPSERVER_H
#define MYTCPSERVER_H
#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>
#include <QObject>

//请求包结构
typedef struct  netPackQ
{
    char opCode[2];//暗号校验头--校验不对的直接丢弃
    char keyword[254];//交互的核心内容
    netPackQ(){
        memset(opCode,0,2);
        memset(keyword,0,254);
    }
} netPackQ;

class Mytcpserver: public QObject {
    Q_OBJECT

public:
    Mytcpserver(QObject *parent = nullptr);

    private slots:
        void onNewConnection();

    private:
        QTcpServer *server;
};

#endif // MYTCPSERVER_H

TCP主文件:

#include "mytcpserver.h"
#include "cmdrun.h"

Mytcpserver::Mytcpserver(QObject *parent) : QObject(parent) {
    // 创建 TCP 服务器
    server = new QTcpServer(this);

    // 连接新连接信号到槽函数
    connect(server, &QTcpServer::newConnection, this, &Mytcpserver::onNewConnection);

    // 绑定到 10086 端口
    if (!server->listen(QHostAddress::Any, 10086)) {
        qDebug() << "Server could not start!";
    } else {
        qDebug() << "Server started on port 10086.";
    }
}


void Mytcpserver::onNewConnection() {
    QTcpSocket *socket = server->nextPendingConnection();

    // 连接读取数据信号
    connect(socket, &QTcpSocket::readyRead, [socket]() {
        netPackQ packet;
        qint64 bytesReceived = socket->read(reinterpret_cast<char*>(&packet), sizeof(netPackQ));
        if (bytesReceived == sizeof(netPackQ)) {
            // 处理接收到的数据
            qDebug() << "Received date";
            if(packet.opCode[0] == 0x01 && packet.opCode[1]==9){//执行后台命令,打开指定应用。比如:C:\Windows\notepad.exe
                qDebug() << "Received keyword:" << packet.keyword;
                cmdrun act;
                act.Run(packet.keyword);//====处理核心内容=====
            }
            socket->write("Received sucess\n"); // 回复客户端
        } else {
            qDebug() << "Received incomplete data";
        }
    });

    // 连接断开信号
    connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater);
}

4、多线程执行后台CMD命令

直接用进程执行CMD命令会导致托盘卡顿,所以得用多线程来执行,cmdrun类很简单,如下:

//头文件:cmdrun.h
#ifndef CMDRUN_H
#define CMDRUN_H
#include<QString>
#include "threadCmd.h"
class cmdrun
{
public:
    cmdrun();
    ~cmdrun();
    int Run(QString cmd);
};

#endif // CMDRUN_H


//cpp文件:cmdrun.cpp
#include "cmdrun.h"
#include <QDebug>

cmdrun::cmdrun()
{

}

cmdrun::~cmdrun()
{

}

int cmdrun::Run(QString cmd){
    //qDebug() << __LINE__ << cmd;
    ThreadCmd *thread = new ThreadCmd(cmd);
    thread->start();

    return 1;
}

5、多进程执行CMD命令

//头文件:threadCmd.h
#ifndef THREADCMD_H
#define THREADCMD_H

#include <QThread>
#include <QString>

class ThreadCmd : public QThread
{
    Q_OBJECT
public:
    explicit ThreadCmd(const QString &param) ;

protected:
    void run() override;

private:
    QString m_param;
};

#endif // THREADCMD_H


//cpp文件:threadCmd.cpp
#ifndef THREADCMD_CPP
#define THREADCMD_CPP

#include "threadCmd.h"
#include <QDebug>
#include <QProcess>
#include <QTextCodec>

ThreadCmd::ThreadCmd(const QString &param) : m_param(param) {

}

void ThreadCmd::run(){
    QProcess process;
    // 执行命令
    QString cmd = "D:\\cmdexec.cmd \"" + m_param +"\"";
    //QString cmd = m_param;
    //qDebug() << __LINE__ << cmd;
    // 启动快捷方式
    if (QProcess::startDetached(cmd)) {
        qDebug() << "Application launched successfully.";
    } else {
        qDebug() << "Failed to launch application.";
    }
    // 进程使用完毕后,可以手动删除
    process.deleteLater();
}


#endif // THREADCMD_CPP

6、开机自启动

系统托盘通常都有开机自启动的需求,可以手动拷贝到启动目录,或者在程序中编写把应用程序的.lnk文件放到自启动目录中:

void addToStartup(const QString appPath) {
    QString startupPath = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + "/Startup/";

    //qDebug()<< __LINE__ << startupPath;
    // 创建启动文件夹(如果不存在)
    QDir dir(startupPath);
    if (!dir.exists()) {
        dir.mkpath(".");
    }

    // 创建快捷方式(Windows 特有)
    QString shortcutPath = startupPath + "cstsvr.lnk";

    if (!QFile::exists(shortcutPath)) {
        // 使用 PowerShell 创建快捷方式 (请根据实际情况调整)
        QString command = QString("powershell -command \"$ws = New-Object -ComObject WScript.Shell; $s = $ws.CreateShortcut('%1'); $s.TargetPath='%2'; $s.Save()\"").arg(shortcutPath).arg(appPath);
        QProcess::execute(command);
    }
}


int main(int argc, char *argv[]) {
    QApplication  app(argc, argv);
    // 获取当前应用程序的完整路径
    QString appPath = QCoreApplication::applicationFilePath();
    //qDebug()<< __LINE__ << appPath;
    addToStartup(appPath);// 添加自启动到启动文件夹
    .......
}

篇尾

        不同主机之间的进程间通信,通常需要设定一端为服务端进行侦听,当然也可以两端都设置为服务端,服务端≠服务器,服务端只代表某项服务的侦听端,比如本篇涉及的项目需求是,国产Linux系统需要获取window系统的所有应用程序的图标,并且取回到国产Linux系统的QT界面中展示,此时就需要在window系统上挂着【根据exe应用路径获取图片】的服务端,并把文件回传给Linux。


http://www.kler.cn/a/390258.html

相关文章:

  • 读数据质量管理:数据可靠性与数据质量问题解决之道03数据目录
  • FatLab:我的编程课程系列
  • Prometheus面试内容整理-Prometheus 的架构和工作原理
  • scrapy爬取中信证券销售金融产品信息
  • Autosar CP DDS规范导读
  • 【p2p、分布式,区块链笔记 DAM】GUN/SEA(Security, Encryption, Authorization) 模块genkey
  • mysql数据库(四)单表查询
  • 【idea】idea2024版本创建项目时没有java 8的版本选择
  • TOEIC 词汇专题:科技硬件篇
  • 【AI新领域应用】AlphaFold 2,原子级别精度的蛋白质3D结构预测,李沐论文精读(2021Nature封面,2024诺贝尔奖)
  • Python——飞机大战
  • 如何在 FastReport VCL 中创建报告时使用样式
  • Springboot 使用EasyExcel导出含图片并设置样式的Excel文件
  • 第四十二章 Vue中使用mutations修改Vuex仓库数据
  • 【JAVA】-Springboot核心机制
  • 智能量化模型在大数据下的中阳策略发展
  • 基于Python的高校成绩分析管理系统
  • 计算机新手练级攻略——如何搜索问题
  • 软考知识备忘
  • 【Linux进程篇3】说白了,Linux创建进程(fork父子进程)也就那样!!!
  • MySQL基础篇总结
  • vue/react前端项目自定义js脚本实现自定义部署等操作
  • 高级java每日一道面试题-2024年11月01日-Redis篇-Redis支持的数据类型有哪些?
  • Android 编译系统
  • Selenium+Pytest自动化测试框架 ------ 禅道实战
  • 青训5_1112_01 小S的倒排索引(内置方法 set(a) set(b) 及sorted 排序)