Qt常用却容易忘的功能记录

目录

1.exe图标设置

2.托盘设置

3.当关闭最后窗口时程序仍然后台运行 

4.多次点击exe只执行一次 

 5.消息重定向到textEdit

6.读写ini文件

7.exe所在路径

8.当前时刻获取

9.设置窗口图标背景

10.状态栏

11.并发tcp服务器

12.tcp客户端

13.开机自启

14.获取桌面背景窗口

15.64位32位自动切换库文件

本文均是本人的一些总结,可能有不严谨的地方还请指出

(大概会持续更新吧)

1.exe图标设置

将ico图标与logo.rc文件放进pro目录中(3文件同目录)

logo.rc中写IDI_ICON1 ICON DISCARDABLE "icon.ico"

pro中加RC_FILE += logo.rc即可

2.托盘设置

头文件

#include <QSystemTrayIcon>
#include <QMenu>
QSystemTrayIcon *m_systemTray;//托盘
QMenu *m_trayMenu;//托盘菜单

 托盘初始化示例,放入MainWindow构造函数调用

void MainWindow::InitTray()//初始化托盘
{
    m_systemTray = new QSystemTrayIcon(this);//托盘
    m_systemTray->setIcon(QIcon(":/res/image/icon.ico"));//托盘图标
    m_systemTray->setToolTip("xxxxx");//托盘提示
    m_trayMenu = new QMenu(this);//托盘菜单
    m_trayMenu->addAction("显示控件窗口", this, &MainWindow::show);
    m_trayMenu->addAction("退出", this, [=]{
        QApplication::quit();//退出
    });
    m_systemTray->setContextMenu(m_trayMenu);
    connect(m_systemTray, &QSystemTrayIcon::activated, this, [=](QSystemTrayIcon::ActivationReason s){
        if(s == QSystemTrayIcon::DoubleClick)   this->show();//鼠标双击显示
    });
    m_systemTray->show();
}

3.当关闭最后窗口时程序仍然后台运行 

main函数中设置

//关闭窗口但不退出
QApplication::setQuitOnLastWindowClosed(false);

4.多次点击exe只执行一次 

#include "mainwindow.h"

#include <QApplication>
#include <QSharedMemory>

int main(int argc, char *argv[])
{
    QSharedMemory shared("xxxxx");//创建共享内存,保证程序单实例运行
    if(shared.attach()) return -1;
    shared.create(1);
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

 5.消息重定向到textEdit

由于textEdit在子线程调用会导致崩溃,所以以全局的队列实现,qt的队列是线程安全的

首先写个全局调用的头文件Globel.h吧,通常我喜欢把多个线程访问的东西写在Globel

#ifndef GLOBEL_H
#define GLOBEL_H
#include <QCoreApplication>
#include <QTime>
#include <QQueue>

#define LOCAL_TIME QTime::currentTime().toString()
#define WORK_PATH QCoreApplication::applicationDirPath()
#define MSG_QUEUE_MAX_LEN 20 //消息队列最大容量

extern QQueue<QString> g_msgQueue;//消息队列
/**********************************************/
//注意把g_msgQueue定义一遍,比如在对应cpp定义
//#include "Globel.h"
//QQueue<QString> g_msgQueue;//消息队列
/**********************************************/

//全局类
class Globel
{
public:
    //输出消息回调
    static void LogoutputToTextedit(QtMsgType, const QMessageLogContext&, const QString &msg)
    {
        QString text;
        text.append(LOCAL_TIME);
        text.append(msg);
        if(g_msgQueue.size() >= MSG_QUEUE_MAX_LEN) return;
        g_msgQueue.enqueue(text);
    }
    //设置消息重定向
    static void SetMsgToQueue()
    {
        qInstallMessageHandler(LogoutputToTextedit);
    }
    static QString GetMsg()//获取消息
    {
        return g_msgQueue.empty() ? QString() : g_msgQueue.dequeue();
    }

};

#endif // GLOBEL_H

mainwindow构造中

ui->setupUi(this);
Globel::SetMsgToQueue();//设置消息重定向
//timer
m_timer = new QTimer;
connect(m_timer, &QTimer::timeout,this, [=]{
    QString msg = Globel::GetMsg();//获取消息
    if(!msg.isNull())
        ui->textEdit->append(msg);
});
m_timer->start(10);//10ms

6.读写ini文件

头文件#include <QSettings>

void MainWindow::SaveUsersData()//保存用户数据
{
    QSettings *userdata_iniwrite = new QSettings("userdata.ini", QSettings::IniFormat);
    userdata_iniwrite->setValue("tcp/ip", m_serverIp);
    userdata_iniwrite->setValue("tcp/port", QString::number(m_serverPort));
    userdata_iniwrite->deleteLater();
}

void MainWindow::ReadUsersData()//读取用户数据
{
    QSettings *userdata_iniread = new QSettings("userdata.ini", QSettings::IniFormat);
    m_serverIp = userdata_iniread->value("tcp/ip").toString();
    m_serverPort = userdata_iniread->value("tcp/port").toUInt();
    userdata_iniread->deleteLater();
}

特别地,需要读取中文则需要添加

userdata_iniread->setIniCodec(QTextCodec::codecForName("UTF-8")); //重点

7.exe所在路径

当windows程序开机自启后,./就不是exe所在路径了,可通过下面方法获得WORK_PATH

#include <QCoreApplication>

#define WORK_PATH QCoreApplication::applicationDirPath()

8.当前时刻获取

#define LOCAL_TIME QTime::currentTime().toString()

9.设置窗口图标背景

this->setWindowIcon(QIcon(":/res/image/icon.ico"));//设置窗口图标

//只设置当前对象的背景
this->setStyleSheet("#MainWindow{border-image:url(:/res/image/bg.jpeg)}");
//设置当前对象及其子对象的背景
this->setStyleSheet("border-image:url(:/res/image/bg.jpeg)");

10.状态栏

头文件#include <QStatusBar>,附带个#include <QLabel>

m_statusLabel = new QLabel;
m_statusBar = new QStatusBar;
m_statusBar->addWidget(m_statusLabel);
//接下来操作m_statusLabel即可

11.并发tcp服务器

记得pro文件配置QT += network

首先继承一个QTcpServer,重写virtual void incomingConnection(qintptr handle),这里用数组实现的,当然用链表挺好。

"tcpsockettask.h"就是连接后的处理了,下面说

#ifndef MYTCPSEVER_H
#define MYTCPSEVER_H

#include <QObject>
#include <QTcpServer>
#include <QQueue>

#include "tcpsockettask.h"

#define MAX_CONNECT_NUM 50

//管理tcp任务对象结构体
struct TcpSocketTaskControl
{
    TcpSocketTaskControl() : tcp_task(nullptr), exist(false){}
    TcpSocketTask *tcp_task;//tcp任务对象
    bool exist;//对象存在性
};

//自定义QTcpServer类
class MyTcpSever : public QTcpServer
{
    Q_OBJECT
public:
    explicit MyTcpSever(QObject *parent = nullptr) : QTcpServer(parent), m_count(0)
    {
        qDebug() << "构造Tcp服务器,本服务器最大承载数" << MAX_CONNECT_NUM;
    }
    void ClearTcpTask()//清理tcp任务对象
    {
        qDebug() << "清理tcp任务对象";
        for(int i = 0; i < MAX_CONNECT_NUM; i++)
        {
            if(m_tcpSocketTaskControl[i].exist == true)
            {
                m_tcpSocketTaskControl[i].tcp_task->quit();
                m_tcpSocketTaskControl[i].tcp_task->wait();
                delete m_tcpSocketTaskControl[i].tcp_task;
                m_tcpSocketTaskControl[i].exist = false;
                m_count--;//tcp任务对象计数
                qDebug() << "清理" << i << "号tcp任务对象";
            }
        }
        qDebug() << "清理tcp任务对象完毕,tcp任务对象计数: " << m_count;
    }
    inline int GetTaskNum() const {return m_count;}//获取tcp任务对象计数

protected:
    virtual void incomingConnection(qintptr handle)//新连接来临处理函数重写
    {
        for(int i = 0; i < MAX_CONNECT_NUM; i++)
        {
            if(m_tcpSocketTaskControl[i].exist == true)
            {
                if(m_tcpSocketTaskControl[i].tcp_task->isFinished())//线程运行结束了
                {
                    m_tcpSocketTaskControl[i].tcp_task->quit();
                    m_tcpSocketTaskControl[i].tcp_task->wait();
                    delete m_tcpSocketTaskControl[i].tcp_task;
                    m_tcpSocketTaskControl[i].exist = false;
                    m_count--;//tcp任务对象计数
                    qDebug() << "清理" << i << "号tcp任务对象";
                }
            }
        }
        for(int i = 0; i < MAX_CONNECT_NUM; i++)
        {
            if(m_tcpSocketTaskControl[i].exist == false)
            {
                m_tcpSocketTaskControl[i].exist = true;
                m_tcpSocketTaskControl[i].tcp_task = new TcpSocketTask(handle);//新的连接建立时触发槽函数
                m_tcpSocketTaskControl[i].tcp_task->start();//开始线程
                m_count++;//tcp任务对象计数
                qDebug() << "分配到" << i << "号tcp任务对象";
                break;
            }
            if(i == MAX_CONNECT_NUM - 1)
                qDebug() << "[错误] tcp任务对象已经分配满!";
        }
        qDebug() << "tcp任务对象计数: " << m_count;
    }

private:
    TcpSocketTaskControl m_tcpSocketTaskControl[MAX_CONNECT_NUM];//管理tcp任务对象结构体
    int m_count;//tcp任务对象计数
};

#endif // MYTCPSEVER_H

"tcpsockettask.h"连接后的处理

#ifndef TCPSOCKETTASK_H
#define TCPSOCKETTASK_H

#include <QObject>
#include <QThread>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTimer>
#include <QByteArray>

#define TCP_BUFFER_HEAD 0xffff //tcp传输头
#define FACE_SEND_INV 500 //间隔

//TcpSocket任务类
class TcpSocketTask : public QThread
{
    Q_OBJECT
public:
    explicit TcpSocketTask(qintptr handle) : m_handle(handle){}

protected:
    virtual void run()//线程执行函数重写
    {
        //init
        m_head = TCP_BUFFER_HEAD;
      head_buffer.append(m_head>>24).append(m_head>>16).append(m_head>>8).append(m_head);
        m_timer_send_text = new QTimer;
        //tcp
        m_tcpSocket = new QTcpSocket;
        if(m_tcpSocket->setSocketDescriptor(m_handle) == false) return;//根据描述符得到tcpsocket对象
        qDebug() << "与客户端建立连接: " + m_tcpSocket->peerAddress().toString() << m_tcpSocket;
        connect(m_tcpSocket, &QTcpSocket::readyRead, this, [=](){//收到tcp包时触发槽函数
            m_tcpSocket->readAll();//哪怕不用也得读出来
        }, Qt::DirectConnection);
        connect(m_tcpSocket, &QTcpSocket::disconnected, this, [=](){//连接断开时触发槽函数
            m_timer_send_text->stop();
            m_tcpSocket->close();
            qDebug() << "断开连接: " + m_tcpSocket->peerAddress().toString() << m_tcpSocket;
            this->quit();//退出事件循环
        }, Qt::DirectConnection);
        //timer
        connect(m_timer_send_text, &QTimer::timeout, this, [=]{
            QByteArray data_buffer;
            data_buffer.append("xxx");
            SendBuffer(data_buffer, 0, m_tcpSocket);//发送数据,type:1视频、0文本
        }, Qt::DirectConnection);
        m_timer_send_text->start(SEND_INV);
        exec();//事件循环
        m_timer_send_text->stop();
        m_timer_send_text->deleteLater();
        m_tcpSocket->deleteLater();
    }

private:
    //发送数据,type:1视频、0文本
    void SendBuffer(const QByteArray &buffer, uint type, QTcpSocket *tcpSocket) const
    {
        QByteArray data = head_buffer;
        uint data_size = buffer.size();
        data.append(data_size).append(data_size>>8).append(data_size>>16).append(data_size>>24);
        data.append(type).append(type>>8).append(type>>16).append(type>>24);
        data.append(buffer);
        tcpSocket->write(data);
    }

    qintptr m_handle;//描述符
    QTcpSocket *m_tcpSocket;
    uint m_head;
    QByteArray head_buffer;
    QTimer *m_timer_send_text;
};

#endif // TCPSOCKETTASK_H

回到gui线程,包含#include "mytcpsever.h"

构造

//tcp
    m_tcpSever = new MyTcpSever;
    bool ok = m_tcpSever->listen(QHostAddress::AnyIPv4, m_port);//建立监听
    if(!ok)
    {
        QMessageBox::warning(nullptr, "致命错误", QString::number(m_port) + "端口被占用");
        QApplication::quit();//退出
    }
    qDebug() << "服务器开启\n监听端口: " << m_port;

 析构

//tcp
    m_tcpSever->close();
    m_tcpSever->ClearTcpTask();
    delete m_tcpSever;

12.tcp客户端

头文件至少#include <QTcpSocket>

初始化

 //init
    m_online = false;
    m_head = TCP_BUFFER_HEAD;
    m_head_buffer.append(m_head>>24).append(m_head>>16).append(m_head>>8).append(m_head);    
//tcp
    m_tcp_socket = new QTcpSocket;
    connect(m_tcp_socket, &QTcpSocket::connected, [=](){
        m_online = true;
        qDebug() << LOCAL_TIME + "连接成功!";
    });
    connect(m_tcp_socket, &QTcpSocket::readyRead, this, [=](){
        QByteArray recv_data = m_tcp_socket->readAll();
        if(recv_data.isNull()) return;
    });
    connect(m_tcp_socket, &QTcpSocket::disconnected, this, [=](){
        m_online = false;
        m_temp_buffer.clear();
        m_tcp_socket->close();
        qDebug() << LOCAL_TIME + "连接断开!";
    });

 连接与断连,用按键写

void MainWindow::on_ConnectButton_clicked()//连接按键
{
    if(m_online)
    {
        qDebug() << LOCAL_TIME + "忽略本次点击!";
        return;
    }
    m_serverIp = ui->ipEdit->text();
    m_serverPort = ui->portEdit->text().toUInt();
    m_tcp_socket->abort();
    m_tcp_socket->connectToHost(m_serverIp, m_serverPort);
    qDebug() << LOCAL_TIME + "尝试连接到" + m_serverIp + ":" + QString::number(m_serverPort);
}
void MainWindow::on_DisonnectButton_clicked()//断开按键
{
    if(!m_online)
    {
        qDebug() << LOCAL_TIME + "忽略本次点击!";
        return;
    }
    m_tcp_socket->close();
}

13.开机自启

#define HEKY_AUTORUN "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"
void mainWidget::AutoRun(bool flag)//设置开机自启,flag表示是否开机自启
{
    QString appName = QApplication::applicationName();
    QString appPath = QApplication::applicationFilePath();
    appPath = appPath.replace("/", "\\");
    //打开注册表
    QSettings *reg = new QSettings(
                HEKY_AUTORUN,//宏定义,自启动的注册表位置
                QSettings::NativeFormat);
    QString val = reg->value(appName).toString();//根据appName获取键值
    if (flag) {//打开开机自启
        if (val != appPath) {//键值不是本程序地址或者不存在,则写入键值
            reg->setValue(appName, appPath);
        }
        QString v = reg->value(appName).toString();//读取刚刚写入的键值
        if (v != appPath) {//写入失败
            ui->AutoStartcheckBox->setChecked(false);
            QMessageBox::warning(this, "拒绝访问", "添加注册表失败.", QMessageBox::Ok, QMessageBox::Ok);
        }
    } else {//关闭开机自启
        reg->remove(appName);//移除键值
        QString v = reg->value(appName).toString();
        if (v != "") {//键值仍然存在,移除失败
            ui->AutoStartcheckBox->setChecked(true);
            QMessageBox::warning(this, "拒绝访问", "移除注册表失败.", QMessageBox::Ok, QMessageBox::Ok);
        }
    }
    reg->deleteLater();//延迟删除
}

bool mainWidget::isAutoRun()//判断是否打开开机自启
{
    QString appName = QApplication::applicationName();
    QString appPath = QApplication::applicationFilePath();
    appPath = appPath.replace("/", "\\");
    QSettings *reg = new QSettings(
                HEKY_AUTORUN,//宏定义,在头文件support.h里面
                QSettings::Registry64Format);
    //如果注册表键值就是本程序的地址,表示打开了开机自启
    QString val = reg->value(appName).toString();
    return val == appPath;
}

14.获取桌面背景窗口

HWND mainWidget::GetBackground()//获取背景窗体句柄
{
    //背景窗体没有窗体名,但是知道它的类名是workerW,且有父窗体Program Maneger,所以只要
    //遍历所有workW类型的窗体,逐一比较它的父窗体是不是Program Manager就可以找到背景窗体
    HWND hwnd = FindWindowA("progman", "Program Manager");
    HWND worker = NULL;
    do
    {
        worker = FindWindowExA(NULL, worker, "workerW", NULL);
        if (worker != NULL)
        {
            char buff[200] = {0};
            int ret = GetClassNameA(worker, (PCHAR) buff, sizeof(buff) * 2);
            if (ret == 0)
                return NULL;
        }
        if (GetParent(worker) == hwnd)
        {
            return worker;//返回结果
        }
    }
    while (worker != NULL);
    //没有找到
    //发送消息生成一个WorkerW窗体
    SendMessage(hwnd,0x052C,0,0);
    //重复上面步骤
    do
    {
        worker = FindWindowExA(NULL, worker, "workerW", NULL);
        if (worker != NULL)
        {
            char buff[200] = {0};
            int ret = GetClassNameA(worker, (PCHAR) buff, sizeof(buff) * 2);
            if (ret == 0)
                return NULL;
        }
        if (GetParent(worker) == hwnd)
        {
            return worker;//返回结果
        }
    }
    while (worker != NULL);
    return NULL;
}

要在qt下改壁纸可以这样

_BKWidget = new BKWidget;//壁纸显示widget
HWND hwnd = (HWND)_BKWidget->winId();//获取背景窗体的句柄
if(!SetBackground(hwnd))//把视频窗体设置成背景窗体的子窗体
     QMessageBox::information(this, "错误", "无法获取背景窗体的句柄");

其中

bool mainWidget::SetBackground(HWND child)//将child窗体设置成背景窗体的子窗体
{
    HWND background = GetBackground();
    if(background != 0)
    {
        SetParent(child, GetBackground());
        return true;
    }
    return false;
}

15.64位32位自动切换库文件

contains(QT_ARCH, i386){
win32: LIBS += -L$$PWD/lib32/ -lquc
}else{
win32: LIBS += -L$$PWD/lib64/ -lquc
}

同理区分arm

unix{
contains(QT_ARCH, arm){

}
else
{

}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值