目录
本文均是本人的一些总结,可能有不严谨的地方还请指出
(大概会持续更新吧)
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
{
}
}