一个QT实现的按键精灵(只写了鼠标部分,键盘用不到就懒得写了)。这里分享一下思路和一些碰到的问题。
功能思路
(1)重复鼠标点击过程:
运行方式:
连点:以固定的时间频率按顺序连续点击设置的点位,可以用来当连点器用。
顺时:完全重复录制下来的鼠标点击过程,按设录制时的点击顺序和时间间隔循环。
实现方式:
录制:录制鼠标点击的轨迹(包括时间间隔)
保存:以txt格式保存轨迹到制定文件夹下
打开:打开已保存的鼠标轨迹
运行:
移动:根据当前轨迹,按顺序移动到轨迹点位
点击:在当前点位上,模拟鼠标的点击
(2)显示模式:
考虑到某些游戏需要全屏,所以需要开发额外的显示模式:
置顶:始终保持软件置于最顶层
图标模式:将完整界面变为小图标,以免遮住游戏界面
界面变换:保留图标,隐藏其他全部空间,背景变为透明
图标移动:按住拖动图标
隐藏模式: 隐藏整个界面,只显示游戏界面,通过键盘操作实现功能。
键盘操作:绑定键盘键位,实现对软件的案件操作。
(3)附加功能:
速度设置:设置连点模式下的点击速度
时间显示:显示录制时间和运行时间
点位显示:显示录制或打开的全部点位
轨迹显示:用QChart同比显示运行轨迹(实时)
部分模块分享
主要是分享一些碰到的问题和一些QT以外的内容(Windwos.h),还有一些QChart的内容。其他东西,基本都是软件本身的架构、逻辑还有一些常用的东西,全放出来太多了,这里就不帖了。
鼠标拖动及图标化显示切换
因为是写在PC端给模拟器用的,所以直接用了C++的 Windows.h 这个类。
而使用Windows.h类的时候,可能会碰到库引用的问题,就是明明你可以在QT安装目录的对应文件夹里找到Windows.h,也可以正常#include它,但是,就是无法编译通过,这种情况下,所有基于Windows.h的变量和函数都会 报错。
解决方式也很简单,在调用该类的.cpp文件下,加入下面这行代码即可(加载#include之下,构造函数之上):
#pragma comment(lib, "user32.lib")
下面是一些虚函数的重载:
重载三个鼠标函数,实现界面拖动,变量的类型,都在后面注释了。
void Wizard::mousePressEvent(QMouseEvent *event)
{
//当鼠标左键点击时.
if (event->button() == Qt::LeftButton)
{
m_move = true; //bool
//记录鼠标的世界坐标.
m_startPoint = event->globalPos(); //QPoint
//记录窗体的世界坐标.
m_windowPoint = this->frameGeometry().topLeft(); //QPoint
}
}
void Wizard::mouseMoveEvent(QMouseEvent *event)
{
if (m_move == true)
{
//移动中的鼠标位置相对于初始位置的相对位置.
QPoint relativePos = event->globalPos() - m_startPoint;
//然后移动窗体即可.
this->move(m_windowPoint + relativePos );
}
}
void Wizard::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
//改变移动状态.
m_move = false;
}
}
图标化显示的时候,绘制透明遮罩,将widget的背景变为透明。
void Wizard::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.fillRect(this->rect(), QColor(255, 255, 255, clarity));
}
本来用 button 来实现界面的点击变换是最方便的,但是拖动图标移动的功能始终无法在 button上实现,每次拖动,鼠标都会自动从按钮上锚定到widget的空白部分拖动,效果就很僵硬,qml里可以直接用mouseArea来实现,但widget我用得不多,所以最后只能用本身可以拖动的label代替button,然后给label加上事件过滤器实现界面变换的效果。
ui->moveIcon->installEventFilter(this); //给选定的label添加事件过滤器,写在构造函数里
bool Wizard::eventFilter(QObject *watched, QEvent *event) //label事件过滤器,最小(最大化)
{
if(watched == ui->moveIcon)
{
//判断事件
if(event->type() == QEvent::MouseButtonDblClick) //双击图标,进行窗口变换
{
if (this->height()>120) //如果为窗口模式
{
clarity = 0; //通明度设为0
ui->moveIcon->setPixmap(QPixmap(":/Res/image/最大化.png"));
ui->exitbtn->hide();
ui->runtime->hide();
//…………
//这边不全放出来了,隐藏所有控件
this->setFixedSize(ui->moveIcon->width()+20,ui->moveIcon->height()+20);
}
else //如果为图标藐视
{
clarity = 255;
ui->moveIcon->setPixmap(QPixmap(":/Res/image/最小化.png"));
ui->hideall->show();
ui->runtime->show();
//…………
//显示所有控件
this->setFixedSize(1040,802);
}
return true; //该事件已处理
}
else
return false; //如果是其它事件,可以进行进一步的处理
}
else
return QWidget::eventFilter(watched, event);
}
鼠标点击及键盘设置
模拟鼠标点击,mouse_event 为Windwos.h自带库函数。
//写在执行函数里
SetCursorPos(pos_x,pos_y);//更改鼠标坐标,Window.h库函数,直接调即可,不用声明
mouseaction(1,1, m_Pos); //左键按下
mouseaction(1,0, m_Pos); //左键松开
void TimerRun::mouseaction(int key, int flag, const QPoint &point)
{
//key: 1左键,0右键; flag:1按下,0松开
if (key == 1)
{
if (flag == 1)
mouse_event(MOUSEEVENTF_LEFTDOWN, point.x(), point.y(), 0, 0);
else
mouse_event(MOUSEEVENTF_LEFTUP, point.x(), point.y(), 0, 0);
}
else
{
if (flag == 1)
mouse_event(MOUSEEVENTF_RIGHTDOWN, point.x(), point.y(), 0, 0);
else
mouse_event(MOUSEEVENTF_RIGHTUP, point.x(), point.y(), 0, 0);
}
}
键盘键位功能的设置。
void Wizard::keyPressEvent(QKeyEvent *event)
{
int keyValue = event->key();
if (keyValue == Qt::Key_F1) //开始录制
ui->beginrecord->click();
if (keyValue == Qt::Key_F2) //开始运行
ui->startrun->click();
if (keyValue == Qt::Key_F3) //保存轨迹
ui->savdfile->click();
if (keyValue == Qt::Key_F4) //打开文件
ui->openfile->click();
if (keyValue == Qt::Key_F5) //隐藏界面
ui->hideall->click();
if (keyValue == Qt::Key_F9) //退出软件
ui->exitbtn->click();
//按住Ctrl,然后点击鼠标左键,实现点位录入
if (keyValue == Qt::Key_Control && ui->beginrecord->text().compare("结束录制") == 0)
{
//获取鼠标位置
m_pos = QCursor::pos();//获取鼠标的绝对位置 QPoint
int currenttime = rec_h*3600*1000+rec_min*60*1000+rec_s*1000;
pos.insert(QString::number(poskey), tr("%1,%2,%3").arg(m_pos.x()).arg(m_pos.y()).arg(currenttime)); //QJsonObject
QString str = tr("Point%1:(%2,%3)-%4").arg(poskey).arg(m_pos.x()).arg(m_pos.y()).arg(currenttime);
ui->pointrecord->appendPlainText(str); //打印显示点位
poskey++; //int,点位标志名
chart->GenerateData_slot(m_pos.x(), 1080-m_pos.y()); //QChart图标实时更新
}
}
QChart轨迹同步显示
这里直接把我自己设置的QChart全放出来吧,因为我也是第一次用这个,所以注释还是比较详细的
.h
#ifndef DYNAMICCHART_H
#define DYNAMICCHART_H
#include <QtCharts/QChart>
#include <QtCharts/QSplineSeries>
#include <QtCharts/QValueAxis>
#include <QTimer>
QT_CHARTS_USE_NAMESPACE
class DynamicChart : public QChart
{
Q_OBJECT
public:
DynamicChart(QChart *parent = nullptr);
~DynamicChart();
public slots:
void GenerateData_slot(const int &x, const int &y);
void ClearData_slot();
private:
QLineSeries *m_series;//存放所有图表中点的坐标,如果想要显示2条或者以上的图表,则需要声明多个对象
QValueAxis *m_axisX;
QValueAxis *m_axisY;
};
#endif // DYNAMICCHART_H
.cpp
DynamicChart::DynamicChart(QChart *parent):
QChart(parent),
m_series(nullptr),
m_axisX(new QValueAxis()),
m_axisY(new QValueAxis())
{
m_series = new QLineSeries(this);
QPen DisplayStyle;
DisplayStyle.setColor(QColor(11,80,240));//设置图表显示颜色
DisplayStyle.setWidth(3);//设置图表显示宽度
m_series->setPen(DisplayStyle);//将设置好的样式应用到series中,如果想要在一张图上显示多种样式
//,那就要对多个series分别设置样式
m_series->setPointsVisible(true);
addSeries(m_series);
addAxis(m_axisX,Qt::AlignBottom);//设置x轴以及对齐样式
addAxis(m_axisY,Qt::AlignLeft);//设置y轴以及对齐样式
m_axisX->setLabelFormat("%d"); //设置坐标轴数据格式
m_axisX->setTickCount(10);//设置x轴之间的间隔
m_axisX->setRange(0, 1920);//设置X轴的范围
m_axisX->setGridLineColor(Qt::black);
m_axisX->setMinorTickCount(1);
m_axisX->setMinorGridLineColor(Qt::lightGray);
m_axisY->setLabelFormat("%d");
m_axisY->setTickCount(6);//设置Y轴之间的间隔
m_axisY->setRange(0, 1080);//设置Y轴的范围
m_axisY->setGridLineColor(Qt::black);
m_axisY->setMinorTickCount(1);
m_axisY->setMinorGridLineColor(Qt::lightGray);
m_series->attachAxis(m_axisX);//将设置好的X轴放入series中
m_series->attachAxis(m_axisY);//将设置好的Y轴放入series中
}
DynamicChart::~DynamicChart()
{
delete m_axisY;
delete m_axisX;
delete m_series;
}
void DynamicChart::GenerateData_slot(const int &x, const int &y)
{
m_series->append(x, y);//将数据放入series中,每加入一组数据,图表就会自动重画一次
}
void DynamicChart::ClearData_slot()
{
m_series->clear();
}
效果演示
成品在下面,不保证没有bug,要用的自己下吧,发现bug了可以到下面留言,虽然我也大概率懒得改。
完整源码就不放了,像我这样的菜鸟写得代码,没什么借鉴意义,就不骗你们积分了。
链接:https://pan.baidu.com/s/1TtbntYaDi5P-hIR_euLFGw
提取码:15m4