书接上回:框架有了,功能好了,界面很垃(这里看上回)
...
正如标题所说的,界面美化需要用到Easy-X库,没有的、不会配置的,可以默默离开了。
---------------->>这里请,去下载Easy-X
目录
主要框架:
是的,正如你所看到的,上一期的框架并不适用,由于图形化的代码量过大,需要一个新的框架。
这一次,将采用1 + 2 的方式,即一个源文件,两个头文件。
源文件:MouseClicker.cpp
void SetObjectsStyle();//包含界面文字颜色(部分)、文字背景、文字样式、背景颜色
void SetWindowStyle();//包含窗口风格、样式
void SetInterval();//点击间隔设置
void KeyChoose();//点击按键设置
void Setting();//包含功能设置,包括点击间隔、点击按键设置
void Home();//主界面,包含连点功能头文件:
1. Button.h(按钮坐标,按钮绘制)(前方大量结构体聚集地,接好)
关键宏定义(很多宏定义源文件中也需要):
#define WIDTH 350 //窗口宽度
#define HIGH 200 //窗口高度
#define INTERVAL 20 //间隔(很杂,按钮间隔,按钮中文字间隔......)
#define BUTTONW 100 //按钮宽度
#define BUTTONH 40 //按钮高度
#define ELLIPSEW 30 //圆角宽度
#define ELLIPSEH 30 //圆角高度仅此文件要用到的变量:
COLORREF COLOR = BLACK;
//第一部分,各个按钮坐标(每一个包含:(int类型)topX,topY,bottomX,bottomY)
struct Button_Setting
struct Button_SetClickInterval
struct Button_SetKey
struct Button_Left
struct Button_Middle
struct Button_Right
struct Button_Return
//第二部分,各个按钮的绘制
struct DrawButton
包含:
void Setting(); //绘制:设置按钮
void SetClickInterval(); //绘制:设置点击间隔按钮
void SetKey(); //绘制:设置连点按键按钮
void SetLeftDown(); //绘制:设置连点按键为鼠标左键按钮
void SetMiddleDown(); //绘制:设置连点按键为鼠标中键按钮
void SetRightDown(); //绘制:设置连点按键为鼠标右键按钮
void Return(); //绘制:返回按钮2.showUI.h(界面绘制)(比较轻松)
注意:他需要包含上一个头文件
void showHome()
void showKeyChoose()
void showSetting()
关系复杂,直接放图:
先看效果:
代码来了:
Button.h头文件
#pragma once
#ifndef BUTTON_H_
#define BUTTON_H_
#define WIDTH 350 //窗口宽度
#define HIGH 200 //窗口高度
#define INTERVAL 20 //按钮间隔
#define BUTTONW 100 //按钮宽度
#define BUTTONH 40 //按钮高度
#define ELLIPSEW 30 //圆角宽度
#define ELLIPSEH 30 //圆角高度
COLORREF COLOR = BLACK;
//设置按钮
struct Button_Setting
{
int topX = INTERVAL;
int topY = HIGH - BUTTONH - INTERVAL;
int bottomX = WIDTH - INTERVAL;
int bottomY = HIGH - INTERVAL;
}B_Setting;
//功能设置按钮//
struct Button_SetClickInterval
{
int topX = INTERVAL;
int topY = INTERVAL;
int bottomX = INTERVAL + BUTTONW;
int bottomY = INTERVAL + BUTTONH;
}B_SetInterval;
struct Button_SetKey
{
int topX = 2 * INTERVAL + BUTTONW;
int topY = INTERVAL;
int bottomX = 2 * BUTTONW + 2 * INTERVAL;
int bottomY = BUTTONH + INTERVAL;
}B_SetKey;
//按键切换按钮//
struct Button_Left
{
int topX = INTERVAL;
int topY = INTERVAL;
int bottomX = INTERVAL + BUTTONW;
int bottomY = INTERVAL + BUTTONH;
}B_MouseL;
struct Button_Middle
{
int topX = INTERVAL;
int topY = 2 * INTERVAL + BUTTONH;
int bottomX = INTERVAL + BUTTONW;
int bottomY = 2 * INTERVAL + 2 * BUTTONH;
}B_MouseM;
struct Button_Right
{
int topX = INTERVAL;
int topY = 3 * INTERVAL + 2 * BUTTONH;
int bottomX = INTERVAL + BUTTONW;
int bottomY = 3 * INTERVAL + 3 * BUTTONH;
}B_MouseR;
//返回按钮//
struct Button_Return
{
int topX = WIDTH - BUTTONW - INTERVAL;
int topY = HIGH - BUTTONH - INTERVAL;
int bottomX = WIDTH - INTERVAL;
int bottomY = HIGH - INTERVAL;
}B_return;
//按钮绘制类
struct DrawButton
{
void Setting();
void SetClickInterval(); //绘制:设置点击间隔按钮
void SetKey(); //绘制:设置连点按键按钮
void SetLeftDown(); //绘制:设置连点按键为鼠标左键按钮
void SetMiddleDown(); //绘制:设置连点按键为鼠标中键按钮
void SetRightDown(); //绘制:设置连点按键为鼠标右键按钮
void Return(); //绘制:返回按钮
}drawButton;
//绘制:设置按钮
void DrawButton::Setting()
{
solidroundrect(B_Setting.topX, B_Setting.topY, B_Setting.bottomX, B_Setting.bottomY, ELLIPSEW, ELLIPSEH);
settextcolor(COLOR);
outtextxy((WIDTH - 2 * INTERVAL) / 2, B_Setting.topY + BUTTONH / 2 - 5, L"设置");
}
//绘制:设置点击间隔按钮
void DrawButton::SetClickInterval()
{
solidroundrect(B_SetInterval.topX, B_SetInterval.topY, B_SetInterval.bottomX, B_SetInterval.bottomY, ELLIPSEW, ELLIPSEH);
settextcolor(COLOR);
outtextxy(B_SetInterval.topX, B_SetInterval.topY + BUTTONH / 2 - 5, L"设置点击间隔");
}
//绘制:设置连点按键按钮
void DrawButton::SetKey()
{
solidroundrect(B_SetKey.topX, B_SetKey.topY, B_SetKey.bottomX, B_SetKey.bottomY, ELLIPSEW, ELLIPSEH);
settextcolor(COLOR);
outtextxy(B_SetKey.topX, B_SetKey.topY + BUTTONH / 2 - 5, L"切换点击按键");
}
//绘制:设置连点按键为鼠标左键按钮
void DrawButton::SetLeftDown()
{
solidroundrect(B_MouseL.topX, B_MouseL.topY, B_MouseL.bottomX, B_MouseL.bottomY, ELLIPSEW, ELLIPSEH);
settextcolor(COLOR);
outtextxy(B_MouseL.topX + 15, B_MouseL.topY + BUTTONH / 2 - 5, L"鼠标左键");
}
//绘制:设置连点按键为鼠标中键按钮
void DrawButton::SetMiddleDown()
{
solidroundrect(B_MouseM.topX, B_MouseM.topY, B_MouseM.bottomX, B_MouseM.bottomY, ELLIPSEW, ELLIPSEH);
settextcolor(COLOR);
outtextxy(B_MouseM.topX + 15, B_MouseM.topY + BUTTONH / 2 - 5, L"鼠标中键");
}
//绘制:设置连点按键为鼠标右键按钮
void DrawButton::SetRightDown()
{
solidroundrect(B_MouseR.topX, B_MouseR.topY, B_MouseR.bottomX, B_MouseR.bottomY, ELLIPSEW, ELLIPSEH);
settextcolor(COLOR);
outtextxy(B_MouseR.topX + 15, B_MouseR.topY + BUTTONH / 2 - 5, L"鼠标右键");
}
//绘制:返回按钮
void DrawButton::Return()
{
solidroundrect(B_return.topX, B_return.topY, B_return.bottomX, B_return.bottomY, ELLIPSEW, ELLIPSEH);
settextcolor(COLOR);
outtextxy(B_return.topX + BUTTONW / 2 - 15, B_return.topY + BUTTONH / 2 - 5, L"返回");
}
#endif // !BUTTON_H_
showUI.h头文件
#pragma once
#ifndef SHOWUI_H_
#define SHOWUI_H_
#include "Button.h"
#define TEXTINTERVAL (4 * INTERVAL)
extern int KeyDownNum;
void showHome()
{
settextcolor(BLACK);
outtextxy(TEXTINTERVAL, 40, L"按下[Alt键]开始连点");
outtextxy(TEXTINTERVAL, 60, L"松开[Alt键]结束连点");
if (KeyDownNum == 1)
{
outtextxy(TEXTINTERVAL, 80, L"当前连点按键:鼠标左键");
}
else if (KeyDownNum == 2)
{
outtextxy(TEXTINTERVAL, 80, L"当前连点按键:鼠标中键");
}
else outtextxy(TEXTINTERVAL, 80, L"当前连点按键:鼠标右键");
drawButton.Setting();
}
void showKeyChoose()
{
drawButton.SetLeftDown();
drawButton.SetMiddleDown();
drawButton.SetRightDown();
drawButton.Return();
}
void showSetting()
{
drawButton.SetClickInterval();
drawButton.SetKey();
drawButton.Return();
}
#endif // !SHOWUI_H_
MouseClicker.h源文件
#include <iostream>
#include <Windows.h>
#include <graphics.h>
#include "showUI.h"
#include "Button.h"
#define KEY_DOWN(VK_Key) ((GetAsyncKeyState(VK_Key) & 0x8000) ? 1:0)
using namespace std;
extern int KeyDownNum = 1;
DWORD KeyDownValue[3] = { MOUSEEVENTF_LEFTDOWN ,MOUSEEVENTF_MIDDLEDOWN,MOUSEEVENTF_RIGHTDOWN };
DWORD KeyUpValue[3] = { MOUSEEVENTF_LEFTUP,MOUSEEVENTF_MIDDLEUP,MOUSEEVENTF_RIGHTUP };
int Window = 1;
ExMessage msg;
wchar_t ClickInterval[9999] = { 51 };
void SetObjectsStyle();
void SetWindowStyle();
void SetInterval();
void KeyChoose();
void Setting();
void Home();
int main() {
initgraph(WIDTH, HIGH);
SetWindowStyle();
SetObjectsStyle();
cleardevice();
Home();
return 0;
}
void SetObjectsStyle()
{
LOGFONT f;
gettextstyle(&f);
_tcscpy_s(f.lfFaceName, _T("宋体"));
f.lfQuality = ANTIALIASED_QUALITY;
settextstyle(&f);
setbkcolor(WHITE);
setfillcolor(GREEN);
setbkmode(TRANSPARENT);
}
void SetWindowStyle()
{
SetWindowText(GetHWnd(), L" ");
int GWStyle = (int)GetWindowLong(GetHWnd(), GWL_STYLE);
SetWindowLong(GetHWnd(), GWL_STYLE, GWStyle & ~WS_SIZEBOX & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX);
SetWindowPos(GetHWnd(), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE);
}
void SetInterval()
{
InputBox(ClickInterval, 9999, L"输入点击间隔(单位:毫秒)(默认数值为50)", L"", L"在这里输入点击间隔(无需单位)", WIDTH, HIGH / 2, false);
if (_wtoi(ClickInterval) < 1)
{
MessageBox(GetHWnd(), L"点击间隔必须大于1", L"数值警告", MB_OK | MB_SETFOREGROUND | MB_TOPMOST);
return SetInterval();
}
}
void KeyChoose()
{
Window = 2;
cleardevice();
showKeyChoose();
while (1)
{
if (peekmessage(&msg, EM_MOUSE))
{
if (Window == 2)
{
if (msg.message == WM_LBUTTONUP && msg.x > B_MouseL.topX && msg.y > B_MouseL.topY && msg.x < B_MouseL.bottomX && msg.y < B_MouseL.bottomY)
{
KeyDownNum = 1;
MessageBox(GetHWnd(), L"已将连点按键改为鼠标左键", L"", MB_OK | MB_TOPMOST | MB_SETFOREGROUND);
}
else if (msg.message == WM_LBUTTONUP && msg.x > B_MouseM.topX && msg.y > B_MouseM.topY && msg.x < B_MouseM.bottomX && msg.y < B_MouseM.bottomY)
{
KeyDownNum = 2;
MessageBox(GetHWnd(), L"已将连点按键改为鼠标中键", L"", MB_OK | MB_TOPMOST | MB_SETFOREGROUND);
}
else if (msg.message == WM_LBUTTONUP && msg.x > B_MouseR.topX && msg.y > B_MouseR.topY && msg.x < B_MouseR.bottomX && msg.y < B_MouseR.bottomY)
{
KeyDownNum = 3;
MessageBox(GetHWnd(), L"已将连点按键改为鼠标右键", L"", MB_OK | MB_TOPMOST | MB_SETFOREGROUND);
}
else if (msg.message == WM_LBUTTONUP && msg.x > B_return.topX && msg.y > B_return.topY && msg.x < B_return.bottomX && msg.y < B_return.bottomY)
{
return Setting();
}
}
}
Sleep(1);
}
}
void Setting()
{
Window = 3;
cleardevice();
showSetting();
while (1)
{
if (peekmessage(&msg, EM_MOUSE))
{
if (Window == 3)
{
if (msg.message == WM_LBUTTONUP && msg.x > B_SetInterval.topX && msg.y > B_SetInterval.topY && msg.x < B_SetInterval.bottomX && msg.y < B_SetInterval.bottomY)
{
SetInterval();
}
else if (msg.message == WM_LBUTTONUP && msg.x > B_SetKey.topX && msg.y > B_SetKey.topY && msg.x < B_SetKey.bottomX && msg.y < B_SetKey.bottomY)
{
KeyChoose();
}
else if (msg.message == WM_LBUTTONUP && msg.x > B_return.topX && msg.y > B_return.topY && msg.x < B_return.bottomX && msg.y < B_return.bottomY)
{
return Home();
}
}
}
Sleep(1);
}
}
void Home()
{
Window = 1;
cleardevice();
showHome();
while (true)
{
while (KEY_DOWN(18))
{
if (Window == 1)
{
Sleep(_wtoi(ClickInterval) - 1);
mouse_event(KeyDownValue[KeyDownNum - 1], 0, 0, 0, 0);
mouse_event(KeyUpValue[KeyDownNum - 1], 0, 0, 0, 0);
mouse_event(KeyDownValue[KeyDownNum - 1], 0, 0, 0, 0);
mouse_event(KeyUpValue[KeyDownNum - 1], 0, 0, 0, 0);
}
}
if (peekmessage(&msg, EM_MOUSE))
{
if (Window == 1)
{
if (msg.message == WM_LBUTTONUP && msg.x > B_Setting.topX && msg.y > B_Setting.topY && msg.x < B_Setting.bottomX && msg.y < B_Setting.bottomY)
{
Setting();
}
}
}
Sleep(1);
}
}
重要部分解析
与上一期不同,这次KeyDownNum变量使用了extern关键字,这使得它可以再头文件showUI.h中调用(范围大一点就是跨文件调用)
通过操作ExMessage类型变量msg来实现鼠标与按钮的交互。
mouse_event()实现了模拟鼠标点击(也可以使用PostMessage(),SendMessage()不建议使用,返回较慢,会影响连点器的性能(其实影响也不大,毕竟程序本身的量就不大))
peekmessage()返回一个布偶值,当检测到用户按下指定按键是,返回true,否则返回false
获取鼠标的实时坐标(msg.x, msg.y),判断是否位于圆角矩形(按钮)内,同时判断鼠标按键是否抬起,如果两项条件同时满足,执行跳转或其他指令
通过变量Window标记当前窗口,确保按钮不会重叠,准确的说是由于界面切换是靠cleardevice()函数进行清屏实现的,当我重绘窗口后,上一个窗口按钮的点击范围依然存在,只不过没有被绘制,所以当我在当前窗口点击按钮时,会触发两种效果,或只触发一种效果。(如图所示)
值得注意的是,InputBox()在Easy-X中的定义如下:
bool InputBox(LPTSTR pString, int nMaxCount, LPCTSTR pPrompt = NULL, LPCTSTR pTitle = NULL, LPCTSTR pDefault = NULL, int width = 0, int height = 0, bool bOnlyOK = true);
可见,InputBox()返回的是一个布尔(嘿,之前有一篇写了布偶)值,所以如何获取到啊用户输入的内容就成了难题,好在C++提供了_wtoi()函数,可将用户输入的内容(本质是字符串)转换为数字。(完美解决)
另一个值得注意的点是InputBox中的第一个参数,其类型为LPTSTR,在Visual Studio项目属性零更改的情况下,我们无法使用string、char类型(实质是窄字符)的变量,并且试过的都知道,其传入的是一个指针,在这三个条件下,只剩下一种选择:wchar_t类型数组(本质和指针差不多,但方便初始化,对新手十分友好)。
再见
系列完结,
后续内容
1.番外制作中...(明年出吧,WinAPI太难写了)
2.代码、内容优化(毕竟谁都不愿意看到屎山或极度抽象的烂代码吧)