execution_character_set
分类: 科技创建于: 7/24/2025
1#pragma execution_character_set("utf-8") // 这是一个预处理指令,指示编译器使用UTF-8字符集来解释源代码中的字符串字面量。 2 // 这有助于确保在代码中使用中文字符或其他非ASCII字符时不会出现乱码问题。 3#include "mainwindow.h" // 包含MainWindow类的头文件,定义了MainWindow的接口。 4#include "ui_mainwindow.h" // 包含由Qt Designer生成的UI文件对应的头文件,定义了UI元素。 5#include <QGraphicsDropShadowEffect> // 包含QGraphicsDropShadowEffect类,用于为控件添加阴影效果。 6#include <QColor> // 包含QColor类,用于定义颜色。 7#include <QMouseEvent> // 包含QMouseEvent类,用于处理鼠标事件。 8#include <QPushButton> // 包含QPushButton类,一个标准按钮控件。 9#include <QMenuBar> // 包含QMenuBar类,用于创建菜单栏。 10#include <QMenu> // 包含QMenu类,用于创建菜单。 11#include <QDebug> // 包含QDebug类,用于调试输出信息。 12#include <QPropertyAnimation> // 包含QPropertyAnimation类,用于属性动画。 13#include <QScreen> // 包含QScreen类,用于获取屏幕信息。 14#include <QCoreApplication> // 包含QCoreApplication类,提供事件循环和核心功能。 15#include <QTimer> // 包含QTimer类,用于定时器功能。 16#include <QDateTime> // 包含QDateTime类,用于处理日期和时间。 17#include <QClipboard> // 包含QClipboard类,用于访问系统剪贴板。 18#include <QParallelAnimationGroup> // 包含QParallelAnimationGroup类,用于并行播放多个动画。 19#include <Windows.h> // 包含Windows API头文件,提供访问Windows底层功能的接口。 20#include <qt_windows.h> // 包含Qt对Windows平台特定功能的封装头文件。 21#include <QSystemTrayIcon> // 包含QSystemTrayIcon类,用于创建系统托盘图标。 22#include <QFile> // 包含QFile类,用于文件操作。 23#include <QJsonDocument> // 包含QJsonDocument类,用于读写JSON文档。 24#include <QJsonObject> // 包含QJsonObject类,用于表示JSON对象。 25#include <QJsonValue> // 包含QJsonValue类,用于表示JSON值。 26#include <QJsonArray> // 包含QJsonArray类,用于表示JSON数组。 27#include <QShortcut> // 包含QShortcut类,用于定义键盘快捷键。 28#include <QDesktopServices> // 包含QDesktopServices类,用于打开文件或URL。 29// #include <QFile> // 重复包含,已在上面包含过。 30#include <QListView> // 包含QListView类,用于显示列表数据。 31#include <QFormLayout> // 包含QFormLayout类,用于创建表单布局。 32#include <QDesktopWidget> // 包含QDesktopWidget类(在Qt6中已被QScreen取代),用于获取桌面信息。 33#include "windows.h" // 重复包含Windows API头文件。 34#include "windowsx.h" // 包含Windowsx.h,提供一些宏,如GET_X_LPARAM等。 35 36// MainWindow类的构造函数,用于初始化窗口。 37MainWindow::MainWindow(QWidget *parent) : 38 QMainWindow(parent), // 调用父类QMainWindow的构造函数。 39 ui(new Ui::MainWindow) // 初始化ui指针,指向Ui::MainWindow的实例,用于访问UI界面元素。 40{ 41 ui->setupUi(this); // 调用ui对象的setupUi方法,根据.ui文件设置窗口的用户界面。 42 43 // 设置窗口标志: 44 // Qt::FramelessWindowHint:移除窗口的边框和标题栏,实现完全自定义的窗口外观。 45 // Qt::WindowMinimizeButtonHint:虽然是无边框窗口,但允许在任务栏上显示最小化按钮(对于最小化功能)。 46 this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint); 47 // QMainWindow透明显示,当设置主显示窗口的外边距时,防止外边距显示出来。 48 // Qt::WA_TranslucentBackground:设置窗口背景透明。这对于创建阴影效果和自定义边框是必要的, 49 // 因为它允许阴影绘制在窗口内容之外,而不会被非透明背景覆盖。 50 this->setAttribute(Qt::WA_TranslucentBackground, true); 51 52 // 设置窗口内容的边距。 53 // 对于自定义无边框窗口,通常需要设置外部边距来为阴影效果或拉伸区域预留空间。 54 // 这里的10像素边距是为阴影效果和鼠标拖拽窗口边缘调整大小的功能留出的空间。 55 setContentsMargins(10, 10, 10, 10); 56 // 设置最大化按钮的图标。":lib/Icon_max4.png"是Qt资源文件中的路径。 57 ui->toolButton_max->setIcon(QIcon(":lib/Icon_max4.png")); 58 59 // 设置主窗口的阴影效果 60 defaultShadow = new QGraphicsDropShadowEffect(); // 创建一个QGraphicsDropShadowEffect对象。 61 defaultShadow->setBlurRadius(15); // 设置阴影的模糊半径为15像素,值越大阴影越模糊。 62 defaultShadow->setColor(QColor(0, 0, 0)); // 设置阴影颜色为黑色(RGB 0,0,0)。 63 defaultShadow->setOffset(0, 0); // 设置阴影的偏移量为(0,0),表示阴影向四周均匀发散。 64 // 不要直接给this(MainWindow)设置,会报UpdateLayeredWindowIndirect failed错误。 65 // 这是因为WA_TranslucentBackground和QGraphicsDropShadowEffect在某些情况下直接应用于顶层窗口可能引起Windows API的冲突。 66 // 将阴影效果应用于中央控件(ui->centralWidget)可以规避这个问题,并且阴影会正确地显示在窗口外部。 67 ui->centralWidget->setGraphicsEffect(defaultShadow); 68 69 // 双击标题栏事件在这里处理。 70 // 以下代码使用Windows API来修改窗口样式,以启用标准的Windows窗口行为,例如双击标题栏最大化/还原,以及通过边框调整大小。 71 HWND hwnd = (HWND)this->winId(); // 获取当前Qt窗口的Windows句柄(HWND)。 72 DWORD style = ::GetWindowLong(hwnd, GWL_STYLE); // 获取当前窗口的样式。 73 // 设置新的窗口样式: 74 // WS_MAXIMIZEBOX:允许窗口有最大化按钮(尽管我们用自定义按钮,但这个标志有助于Windows内部处理)。 75 // WS_THICKFRAME:允许窗口通过拖拽边框进行调整大小(这是拉伸功能的基础)。 76 // WS_CAPTION:允许窗口有标题栏的行为(即使是自定义的,例如双击最大化)。 77 // CS_DBLCLKS:允许窗口接收双击消息(如WM_LBUTTONDBLCLK),用于双击标题栏最大化。 78 SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION | CS_DBLCLKS); 79 80 81 // 设置侧边栏(ui->widget_side)的阴影效果 82 QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect(); // 创建另一个阴影效果对象。 83 effect->setOffset(2, 0); // 设置阴影向右偏移2像素,垂直方向无偏移。 84 effect->setColor(QColor(10, 31, 57)); // 设置阴影颜色为深蓝色。 85 effect->setBlurRadius(15); // 设置阴影模糊半径为15像素。 86 87 ui->widget_side->setGraphicsEffect(effect); // 将阴影效果应用到侧边栏widget_side。 88 89 // 快捷键 F11 全屏功能 90 QShortcut *shortcut = new QShortcut(QKeySequence(Qt::Key_F11), this); // 创建一个F11键的快捷方式。 91 // 连接快捷方式的activated()信号到rece_toolButton_fullScreen_sign()槽函数, 92 // 当按下F11键时,将触发全屏/退出全屏操作。 93 connect(shortcut, SIGNAL(activated()), this, SLOT(rece_toolButton_fullScreen_sign())); 94 95 // 设置窗口支持拉伸(调整大小)功能。 96 setSupportStretch(true); 97} 98 99// 计算当前窗口的拉伸(调整大小)区域的矩形范围。 100// 这些矩形定义了鼠标指针可以改变窗口大小的特定区域(四个角和四条边)。 101void MainWindow::calculateCurrentStrechRect() 102{ 103 // STRETCH_RECT_WIDTH和STRETCH_RECT_HEIGHT是定义拉伸区域宽度的常量,通常是一个小值, 104 // 例如10像素,用于在窗口边缘或角落形成一个可拖拽的“热区”。 105 106 // 四个角Rect; 107 // 左上角:(0, 0)到(STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT) 108 m_leftTopRect = QRect(0, 0, STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT); 109 // 左下角:(0, 窗口高度 - STRETCH_RECT_HEIGHT)到(STRETCH_RECT_WIDTH, 窗口高度) 110 m_leftBottomRect = QRect(0, this->height() - STRETCH_RECT_HEIGHT, STRETCH_RECT_WIDTH, STRETCH_RECT_WIDTH); // 注意这里宽度也用了STRETCH_RECT_WIDTH 111 // 右上角:(窗口宽度 - STRETCH_RECT_WIDTH, 0)到(窗口宽度, STRETCH_RECT_HEIGHT) 112 m_rightTopRect = QRect(this->width() - STRETCH_RECT_WIDTH, 0, STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT); 113 // 右下角:(窗口宽度 - STRETCH_RECT_WIDTH, 窗口高度 - STRETCH_RECT_HEIGHT)到(窗口宽度, 窗口高度) 114 m_rightBottomRect = QRect(this->width() - STRETCH_RECT_WIDTH, this->height() - STRETCH_RECT_HEIGHT, STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT); 115 116 // 四条边Rect; 117 // 顶部边框:(STRETCH_RECT_WIDTH, 0)到(窗口宽度 - STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT) 118 // 避开了左右两个角,只处理纯粹的边。 119 m_topBorderRect = QRect(STRETCH_RECT_WIDTH, 0, this->width() - STRETCH_RECT_WIDTH * 2, STRETCH_RECT_HEIGHT); 120 // 右边框:(窗口宽度 - STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT)到(窗口宽度, 窗口高度 - STRETCH_RECT_HEIGHT) 121 m_rightBorderRect = QRect(this->width() - STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT, STRETCH_RECT_WIDTH, this->height() - STRETCH_RECT_HEIGHT * 2); 122 // 底部边框:(STRETCH_RECT_WIDTH, 窗口高度 - STRETCH_RECT_HEIGHT)到(窗口宽度 - STRETCH_RECT_WIDTH, 窗口高度) 123 m_bottomBorderRect = QRect(STRETCH_RECT_WIDTH, this->height() - STRETCH_RECT_HEIGHT, this->width() - STRETCH_RECT_WIDTH * 2, STRETCH_RECT_HEIGHT); 124 // 左边框:(0, STRETCH_RECT_HEIGHT)到(STRETCH_RECT_WIDTH, 窗口高度 - STRETCH_RECT_HEIGHT) 125 m_leftBorderRect = QRect(0, STRETCH_RECT_HEIGHT, STRETCH_RECT_WIDTH, this->height() - STRETCH_RECT_HEIGHT * 2); 126} 127 128// 根据当前鼠标光标的位置,判断它处于哪个拉伸区域。 129// 参数:cursorPos - 鼠标在窗口坐标系中的当前位置。 130// 返回:WindowStretchRectState枚举值,表示鼠标所在的拉伸区域类型。 131WindowStretchRectState MainWindow::getCurrentStretchState(QPoint cursorPos) 132{ 133 WindowStretchRectState stretchState; // 声明一个变量来存储拉伸状态。 134 // 依次检查鼠标光标是否包含在预定义的各个拉伸区域矩形中。 135 if (m_leftTopRect.contains(cursorPos)) { 136 stretchState = LEFT_TOP_RECT; // 左上角 137 } else if (m_rightTopRect.contains(cursorPos)) { 138 stretchState = RIGHT_TOP_RECT; // 右上角 139 } else if (m_rightBottomRect.contains(cursorPos)) { 140 stretchState = RIGHT_BOTTOM_RECT; // 右下角 141 } else if (m_leftBottomRect.contains(cursorPos)) { 142 stretchState = LEFT_BOTTOM_RECT; // 左下角 143 } else if (m_topBorderRect.contains(cursorPos)) { 144 stretchState = TOP_BORDER; // 顶部边框 145 } else if (m_rightBorderRect.contains(cursorPos)) { 146 stretchState = RIGHT_BORDER; // 右边框 147 } else if (m_bottomBorderRect.contains(cursorPos)) { 148 stretchState = BOTTOM_BORDER; // 底部边框 149 } else if (m_leftBorderRect.contains(cursorPos)) { 150 stretchState = LEFT_BORDER; // 左边框 151 } else { 152 stretchState = NO_SELECT; // 不在任何拉伸区域内 153 } 154 return stretchState; // 返回确定的拉伸状态。 155} 156 157// 根据当前的拉伸状态,更新鼠标光标的样式。 158// 参数:stretchState - 当前的拉伸区域类型。 159void MainWindow::updateMouseStyle(WindowStretchRectState stretchState) 160{ 161 // 根据拉伸状态设置不同的鼠标光标图标,以提示用户可以进行哪种类型的拖拽。 162 switch (stretchState) 163 { 164 case NO_SELECT: 165 setCursor(Qt::ArrowCursor); // 默认箭头光标,表示不可拉伸。 166 break; 167 case LEFT_TOP_RECT: 168 case RIGHT_BOTTOM_RECT: 169 setCursor(Qt::SizeFDiagCursor); // 对角线拉伸光标(左上-右下方向)。 170 break; 171 case TOP_BORDER: 172 case BOTTOM_BORDER: 173 setCursor(Qt::SizeVerCursor); // 垂直方向拉伸光标。 174 break; 175 case RIGHT_TOP_RECT: 176 case LEFT_BOTTOM_RECT: 177 setCursor(Qt::SizeBDiagCursor); // 对角线拉伸光标(右上-左下方向)。 178 break; 179 case LEFT_BORDER: 180 case RIGHT_BORDER: 181 setCursor(Qt::SizeHorCursor); // 水平方向拉伸光标。 182 break; 183 default: 184 setCursor(Qt::ArrowCursor); // 默认情况,设置为箭头光标。 185 break; 186 } 187} 188 189// 根据当前的拉伸状态和鼠标移动量,更新窗口的大小和位置。 190void MainWindow::updateWindowSize() 191{ 192 // 拉伸时要注意设置窗口最小值; 避免窗口被缩小到不可见或不合理的尺寸。 193 QRect windowRect = m_windowRectBeforeStretch; // 获取鼠标按下时记录的窗口原始几何尺寸。 194 // 计算鼠标从按下点(m_startPoint)到当前点(m_endPoint)的X和Y方向的位移。 195 int delValue_X = m_startPoint.x() - m_endPoint.x(); // 负值表示鼠标向右移动,正值表示向左移动。 196 int delValue_Y = m_startPoint.y() - m_endPoint.y(); // 负值表示鼠标向下移动,正值表示向上移动。 197 int m_windowMinWidth = 600; // 定义窗口的最小宽度。 198 int m_windowMinHeight = 600; // 定义窗口的最小高度。 199 200 // 根据不同的拉伸状态调整窗口的尺寸和位置。 201 if (m_stretchRectState == LEFT_BORDER) { // 拖拽左边框 202 // 检查窗口宽度是否已经达到最小值,并且鼠标是向左拉伸(delValue_X > 0)或向右缩小(delValue_X <= 0)。 203 // 如果已达最小宽度且是缩小操作,则返回,不再缩小。 204 if (this->geometry().width() <= m_windowMinWidth && delValue_X <= 0) { 205 return; 206 } 207 QPoint bottomLeftPoint = windowRect.bottomLeft(); // 获取原始窗口的左下角点。 208 bottomLeftPoint.setX(bottomLeftPoint.x() - delValue_X); // 根据X方向位移更新左下角的X坐标。 209 windowRect.setBottomLeft(bottomLeftPoint); // 更新窗口矩形的左下角点,从而调整宽度和X位置。 210 this->setGeometry(windowRect); // 应用新的窗口几何尺寸。 211 } else if (m_stretchRectState == RIGHT_BORDER) { // 拖拽右边框 212 QPoint bottomRightPoint = windowRect.bottomRight(); // 获取原始窗口的右下角点。 213 bottomRightPoint.setX(bottomRightPoint.x() - delValue_X); // 根据X方向位移更新右下角的X坐标。 214 windowRect.setBottomRight(bottomRightPoint); // 更新窗口矩形的右下角点,从而调整宽度。 215 this->setGeometry(windowRect); // 应用新的窗口几何尺寸。 216 } else if (m_stretchRectState == TOP_BORDER) { // 拖拽上边框 217 // 检查窗口高度是否已经达到最小值,并且鼠标是向上拉伸(delValue_Y > 0)或向下缩小(delValue_Y <= 0)。 218 if (this->geometry().height() <= m_windowMinHeight && delValue_Y <= 0) { 219 return; 220 } 221 QPoint topLeftPoint = windowRect.topLeft(); // 获取原始窗口的左上角点。 222 topLeftPoint.setY(topLeftPoint.y() - delValue_Y); // 根据Y方向位移更新左上角的Y坐标。 223 windowRect.setTopLeft(topLeftPoint); // 更新窗口矩形的左上角点,从而调整高度和Y位置。 224 this->setGeometry(windowRect); // 应用新的窗口几何尺寸。 225 } else if (m_stretchRectState == BOTTOM_BORDER) { // 拖拽下边框 226 QPoint bottomRightPoint = windowRect.bottomRight(); // 获取原始窗口的右下角点。 227 bottomRightPoint.setY(bottomRightPoint.y() - delValue_Y); // 根据Y方向位移更新右下角的Y坐标。 228 windowRect.setBottomRight(bottomRightPoint); // 更新窗口矩形的右下角点,从而调整高度。 229 this->setGeometry(windowRect); // 应用新的窗口几何尺寸。 230 } else if (m_stretchRectState == LEFT_TOP_RECT) { // 拖拽左上角 231 // 检查宽度和高度是否同时达到最小值,并且都在缩小。 232 if (this->geometry().width() - 3 <= m_windowMinWidth && delValue_X <= 0 233 && this->geometry().height() -3 <= m_windowMinHeight && delValue_Y <= 0) { 234 return; 235 } 236 int a = 0; // 用于标记是否宽度/高度达到最小值。 237 if (this->geometry().width() - 3 <= m_windowMinWidth && delValue_X <= 0) { 238 a = 1; // 宽度达到最小值,并且是向右缩小。 239 } 240 if (this->geometry().height() - 3 <= m_windowMinHeight && delValue_Y <= 0) { 241 a = 2; // 高度达到最小值,并且是向下缩小。 242 } 243 // 根据a的值,决定是同时调整宽度和高度,还是只调整其中一个。 244 if (a == 0) { // 宽度和高度都未达到最小值,可以自由拉伸。 245 QPoint topLeftPoint = windowRect.topLeft(); 246 topLeftPoint.setX(topLeftPoint.x() - delValue_X); 247 topLeftPoint.setY(topLeftPoint.y() - delValue_Y); 248 windowRect.setTopLeft(topLeftPoint); 249 this->setGeometry(windowRect); 250 } else if (a == 1) { // 宽度已达最小值,只调整高度。 251 QPoint topLeftPoint = windowRect.topLeft(); 252 topLeftPoint.setX(this->geometry().x()); // 保持当前X坐标不变。 253 topLeftPoint.setY(topLeftPoint.y() - delValue_Y); 254 windowRect.setTopLeft(topLeftPoint); 255 this->setGeometry(windowRect); 256 } else if (a == 2) { // 高度已达最小值,只调整宽度。 257 //qDebug() << "走3"; // 调试输出。 258 QPoint topLeftPoint = windowRect.topLeft(); 259 topLeftPoint.setX(topLeftPoint.x() - delValue_X); 260 topLeftPoint.setY(this->geometry().y()); // 保持当前Y坐标不变。 261 windowRect.setTopLeft(topLeftPoint); 262 this->setGeometry(windowRect); 263 } 264 } else if (m_stretchRectState == RIGHT_TOP_RECT) { // 拖拽右上角 265 // 检查宽度和高度是否同时达到最小值,并且都在缩小。 266 if (this->geometry().width() - 3 <= m_windowMinWidth && delValue_X <= 0 267 && this->geometry().height() -3 <= m_windowMinHeight && delValue_Y <= 0) { 268 return; 269 } 270 int a = 0; 271 if (this->geometry().width() - 3 <= m_windowMinWidth && delValue_X <= 0) { 272 a = 1; 273 } 274 if (this->geometry().height() - 3 <= m_windowMinHeight && delValue_Y <= 0) { 275 a = 2; 276 } 277 278 if (a == 0) { // 宽度和高度都未达到最小值。 279 QPoint topRightPoint = windowRect.topRight(); 280 topRightPoint.setX(topRightPoint.x() - delValue_X); // 根据X位移调整宽度。 281 topRightPoint.setY(topRightPoint.y() - delValue_Y); // 根据Y位移调整高度和Y位置。 282 windowRect.setTopRight(topRightPoint); 283 this->setGeometry(windowRect); 284 } else if (a == 1) { // 宽度已达最小值,只调整高度。 285 QPoint topRightPoint = windowRect.topRight(); 286 topRightPoint.setX(this->geometry().x()); // 保持当前X坐标不变。 287 topRightPoint.setY(topRightPoint.y() - delValue_Y); 288 windowRect.setTopRight(topRightPoint); 289 this->setGeometry(windowRect); 290 } else if (a == 2) { // 高度已达最小值,只调整宽度。 291 //qDebug() << "走3"; 292 QPoint topRightPoint = windowRect.topRight(); 293 topRightPoint.setX(topRightPoint.x() - delValue_X); 294 topRightPoint.setY(this->geometry().y()); // 保持当前Y坐标不变。 295 windowRect.setTopRight(topRightPoint); 296 this->setGeometry(windowRect); 297 } 298 } else if (m_stretchRectState == RIGHT_BOTTOM_RECT) { // 拖拽右下角 299 // 右下角拉伸只改变宽度和高度,不改变窗口位置。 300 QPoint bottomRightPoint = windowRect.bottomRight(); 301 bottomRightPoint.setX(bottomRightPoint.x() - delValue_X); 302 bottomRightPoint.setY(bottomRightPoint.y() - delValue_Y); 303 windowRect.setBottomRight(bottomRightPoint); 304 this->setGeometry(windowRect); 305 } else if (m_stretchRectState == LEFT_BOTTOM_RECT) { // 拖拽左下角 306 // 检查宽度和高度是否同时达到最小值,并且都在缩小。 307 if (this->geometry().width() - 3 <= m_windowMinWidth && delValue_X <= 0 308 && this->geometry().height() -3 <= m_windowMinHeight && delValue_Y <= 0) { 309 return; 310 } 311 int a = 0; 312 if (this->geometry().width() - 3 <= m_windowMinWidth && delValue_X <= 0) { 313 a = 1; 314 } 315 if (this->geometry().height() - 3 <= m_windowMinHeight && delValue_Y <= 0) { 316 a = 2; 317 } 318 if (a == 0) { // 宽度和高度都未达到最小值。 319 QPoint bottomLeftPoint = windowRect.bottomLeft(); 320 bottomLeftPoint.setX(bottomLeftPoint.x() - delValue_X); // 根据X位移调整宽度和X位置。 321 bottomLeftPoint.setY(bottomLeftPoint.y() - delValue_Y); // 根据Y位移调整高度。 322 windowRect.setBottomLeft(bottomLeftPoint); 323 this->setGeometry(windowRect); 324 } else if (a == 1) { // 宽度已达最小值,只调整高度。 325 QPoint bottomLeftPoint = windowRect.bottomLeft(); 326 bottomLeftPoint.setX(this->geometry().x()); // 保持当前X坐标不变。 327 bottomLeftPoint.setY(bottomLeftPoint.y() - delValue_Y); 328 windowRect.setBottomLeft(bottomLeftPoint); 329 this->setGeometry(windowRect); 330 } else if (a == 2) { // 高度已达最小值,只调整宽度。 331 QPoint bottomLeftPoint = windowRect.bottomLeft(); 332 bottomLeftPoint.setX(bottomLeftPoint.x() - delValue_X); 333 bottomLeftPoint.setY(this->geometry().y()); // 保持当前Y坐标不变。 334 windowRect.setBottomLeft(bottomLeftPoint); 335 this->setGeometry(windowRect); 336 } 337 } 338} 339 340// 设置是否支持窗口拉伸(调整大小)功能。 341// 参数:isSupportStretch - 布尔值,true表示支持拉伸,false表示不支持。 342void MainWindow::setSupportStretch(bool isSupportStretch) 343{ 344 // 因为需要在鼠标未按下的情况下通过mouseMoveEvent事件捕捉鼠标位置,所以需要设置setMouseTracking为true(如果窗口支持拉伸); 345 m_isSupportStretch = isSupportStretch; // 更新成员变量m_isSupportStretch。 346 this->setMouseTracking(isSupportStretch); // 启用/禁用窗口的鼠标跟踪。 347 // 当鼠标跟踪启用时,即使没有按住任何鼠标按钮, 348 // QWidget也会在其几何区域内生成QMouseEvent::MouseMove事件。 349 // 这对于实时更新鼠标光标样式以指示可拉伸区域至关重要。 350 351 // 这里对子控件也进行了设置,是因为如果不对子控件设置,当鼠标移动到子控件上时, 352 // 不会发送mouseMoveEvent事件,也就获取不到当前鼠标位置,无法判断鼠标状态及显示样式了。 353 QList<QWidget*> widgetList = this->findChildren<QWidget*>(); // 获取当前窗口的所有子控件。 354 for(int i = 0; i < widgetList.length(); i ++) { 355 widgetList[i]->setMouseTracking(isSupportStretch); // 对每个子控件也启用/禁用鼠标跟踪。 356 } 357} 358 359// 鼠标按下事件处理函数。 360// 参数:event - QMouseEvent对象,包含鼠标事件的信息。 361void MainWindow::mousePressEvent(QMouseEvent *event) 362{ 363 // 检查鼠标是否在标题栏区域(ui->widget_title)且当前未处于最大化状态(!showFlag)。 364 // 如果是,则将isPressedWidget标记为true,表示正在拖拽整个窗口。 365 // showFlag在这里通常与窗口的最大化状态有关,最大化时通常不允许直接拖拽。 366 if(ui->widget_title->underMouse() && !showFlag) { 367 isPressedWidget = true; // 当前鼠标按下的即是QWidget而非界面上布局的其它控件 368 } 369 last = event->globalPos(); // 记录鼠标按下时的全局坐标,用于后续计算窗口移动量。 370 371 // 当前鼠标进入了以上指定的8个区域(拉伸区域),并且是左键按下时才开始进行窗口拉伸; 372 if (m_stretchRectState != NO_SELECT && event->button() == Qt::LeftButton) 373 { 374 m_isMousePressed = true; // 标记鼠标左键已按下,准备进行拉伸操作。 375 // 记录下当前鼠标位置的全局坐标,为后面计算拉伸位置提供起始点。 376 m_startPoint = this->mapToGlobal(event->pos()); 377 // 保存下拉伸前的窗口位置及大小,以便在拉伸过程中基于原始尺寸进行计算。 378 m_windowRectBeforeStretch = this->geometry(); 379 } 380} 381 382// 鼠标移动事件处理函数。 383// 参数:event - QMouseEvent对象,包含鼠标事件的信息。 384void MainWindow::mouseMoveEvent(QMouseEvent *event) { 385 // 如果isPressedWidget为true(表示正在拖拽标题栏移动窗口) 386 if (isPressedWidget) { 387 // 计算鼠标相对于上次移动的位移量。 388 int dx = event->globalX() - last.x(); 389 int dy = event->globalY() - last.y(); 390 last = event->globalPos(); // 更新last为当前鼠标的全局坐标。 391 move(x()+dx, y()+dy); // 移动窗口到新位置。 392 } 393 394 // 如果鼠标左键没有按下(即没有在拖拽或拉伸),则更新鼠标光标样式。 395 if (!m_isMousePressed) { 396 QPoint cursorPos = event->pos(); // 获取鼠标在窗口坐标系中的当前位置。 397 // 根据当前鼠标的位置显示不同的样式; 398 m_stretchRectState = getCurrentStretchState(cursorPos); // 判断鼠标处于哪个拉伸区域。 399 updateMouseStyle(m_stretchRectState); // 更新鼠标光标样式。 400 } else { 401 // 如果当前鼠标左键已经按下(正在进行拉伸操作),则记录下第二个点的位置,并更新窗口的大小; 402 m_endPoint = this->mapToGlobal(event->pos()); // 记录当前鼠标的全局坐标作为拉伸的结束点。 403 updateWindowSize(); // 根据起始点和结束点以及拉伸状态更新窗口尺寸。 404 } 405} 406 407// 鼠标释放事件处理函数。 408// 参数:event - QMouseEvent对象,包含鼠标事件的信息。 409void MainWindow::mouseReleaseEvent(QMouseEvent *event) 410{ 411 isPressedWidget = false; // 鼠标松开时,将拖拽窗口的标志置为false。 412 m_isMousePressed = false; // 鼠标松开时,将正在拉伸的标志置为false。 413 calculateCurrentStrechRect(); // 重新计算拉伸区域矩形,以应对窗口大小可能已经改变的情况。 414} 415 416// 鼠标双击事件处理函数。 417// 此处为空,因为双击标题栏最大化/还原的逻辑是在nativeEvent中通过Windows API处理的。 418void MainWindow::mouseDoubleClickEvent(QMouseEvent *event) 419{ 420 //不调用 (此函数为空,表示不直接通过Qt事件处理双击,而是依赖原生事件WM_NCHITTEST和WM_GETMINMAXINFO) 421} 422 423// 窗口显示事件处理函数。 424// 参数:event - QShowEvent对象。 425void MainWindow::showEvent(QShowEvent *event) 426{ 427 calculateCurrentStrechRect(); // 窗口显示时,重新计算拉伸区域,确保正确。 428 429 // 防止假死。 430 // Qt::WA_Mapped属性通常在窗口显示后设置,确保窗口句柄已经创建并且窗口已被映射到屏幕。 431 // 有时,在某些复杂的图形效果或窗口管理下,这可以避免一些显示问题或假死状态。 432 setAttribute(Qt::WA_Mapped); 433 QMainWindow::showEvent(event); // 调用基类的showEvent确保标准行为。 434 435 // 这段代码是一个常见的技巧,用于强制Qt重新计算窗口的布局和阴影, 436 // 特别是在使用QGraphicsDropShadowEffect时,有时需要先稍微改变窗口大小再恢复, 437 // 以确保阴影正确渲染,避免阴影显示不全或延迟显示的问题。 438 QSize oldSize = this->size(); 439 resize(oldSize + QSize(10, 10)); // 临时增大窗口尺寸。 440 resize(oldSize); // 恢复窗口到原始尺寸。 441} 442 443// 窗口状态改变事件处理函数。 444// 当窗口最小化、最大化、全屏或从这些状态恢复时触发。 445// 参数:event - QEvent对象。 446void MainWindow::changeEvent(QEvent *event) 447{ 448 // 检查事件类型是否为窗口状态改变事件。 449 if(QEvent::WindowStateChange == event->type()) 450 { 451 // 将QEvent转换为QWindowStateChangeEvent,以便获取更多状态信息。 452 QWindowStateChangeEvent * stateEvent = dynamic_cast<QWindowStateChangeEvent*>(event); 453 if(Q_NULLPTR != stateEvent) // 确保转换成功。 454 { 455 // 判断当前窗口状态。 456 if(this->windowState() == Qt::WindowMinimized) 457 { 458 //qDebug() << "当前最小化"; // 窗口最小化。 459 } else if (this->windowState() == Qt::WindowNoState && stateEvent->oldState() == Qt::WindowMaximized) { 460 //qDebug() << "当前正常"; // 窗口从最大化状态恢复到正常状态。 461 // 恢复centralWidget的样式,包括背景图和圆角。 462 ui->centralWidget->setStyleSheet("#centralWidget {background-color: rgb(67, 67, 67);border-image: url(:/lib/back1.png);border-radius:10px;}"); 463 // 恢复widget_side的样式,包括圆角。 464 ui->widget_side->setStyleSheet("#widget_side { \ 465 color: rgb(255, 255, 255); \ 466 border-top-left-radius: 10px; \ 467 border-bottom-left-radius: 10px; \ 468 background-color: rgb(10, 31, 57); \ 469 }"); 470 // 恢复关闭按钮的样式,包括圆角和鼠标悬停效果。 471 ui->toolButton_close->setStyleSheet("QToolButton {\ 472 color: rgb(255, 255, 255);\ 473 border-top-right-radius: 9px;\ 474 background-color: rgba(94, 255, 210, 0);\ 475 border: none;\ 476 }\ 477 QToolButton::menu-indicator { \ 478 image: None;\ 479 }\ 480 QToolButton:hover {\ 481 color: rgb(255, 255, 255);\ 482 background-color: rgb(200, 0, 0);\ 483 border: none;\ 484 }"); 485 // 恢复最大化按钮的图标为“最大化”图标。 486 ui->toolButton_max->setIcon(QIcon(":lib/Icon_max4.png")); 487 } else if (this->windowState() == Qt::WindowMaximized && stateEvent->oldState() == Qt::WindowNoState) { 488 //qDebug() << "当前最大化"; // 窗口从正常状态变为最大化状态。 489 // 移除centralWidget的圆角,使之铺满整个屏幕。 490 ui->centralWidget->setStyleSheet("#centralWidget {background-color: rgb(67, 67, 67);border-image: url(:/lib/back1.png);border-radius:0px;}"); 491 // 移除widget_side的圆角。 492 ui->widget_side->setStyleSheet("#widget_side { \ 493 color: rgb(255, 255, 255); \ 494 border-top-left-radius: 0px; \ 495 border-bottom-left-radius: 0px; \ 496 background-color: rgb(10, 31, 57); \ 497 }"); 498 // 移除关闭按钮的圆角。 499 ui->toolButton_close->setStyleSheet("QToolButton {\ 500 color: rgb(255, 255, 255);\ 501 border-top-right-radius: 0px;\ 502 background-color: rgba(94, 255, 210, 0);\ 503 border: none;\ 504 }\ 505 QToolButton::menu-indicator { \ 506 image: None;\ 507 }\ 508 QToolButton:hover {\ 509 color: rgb(255, 255, 255);\ 510 background-color: rgb(200, 0, 0);\ 511 border: none;\ 512 }"); 513 // 更改最大化按钮的图标为“还原”图标。 514 ui->toolButton_max->setIcon(QIcon(":lib/icon-copy.png")); 515 } 516 } 517 } 518} 519 520// 处理Windows原生事件。 521// 这是实现自定义无边框窗口行为的关键部分,如拖拽、最大化、调整大小等。 522// 参数:eventType - 事件类型(QByteArray),message - 消息结构体指针,result - 结果指针。 523// 返回值:如果事件已处理,则返回true;否则返回false,让Qt继续处理。 524bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) 525{ 526 MSG* msg = (MSG*)message; // 将void*消息指针转换为Windows MSG结构体指针。 527 switch (msg->message) { 528 // 处理WM_NCCALCSIZE消息:在窗口的非客户区(标题栏、边框)需要计算大小时发送。 529 // 没有这一段,将不会显示窗口(或显示为默认样式,无法自定义)。 530 // 返回true并设置*result为0,告诉系统我们自己负责绘制非客户区,不使用默认边框和标题栏。 531 case WM_NCCALCSIZE: 532 *result = 0; // 告诉系统不需要默认的非客户区计算,完全由我们自己绘制。 533 return true; 534 535 // 处理WM_NCHITTEST消息:当鼠标光标移动时,或者鼠标按钮按下/松开时发送, 536 // 用于确定鼠标光标在哪一部分窗口区域(如标题栏、边框、客户区)。 537 case WM_NCHITTEST: 538 { 539 //qDebug() << "触发WM_NCHITTEST"; // 调试输出。 540 qreal ratio = 1.0; // DPI缩放比例,此处默认为1.0。 541 // 获取鼠标光标的屏幕坐标,并考虑DPI缩放。 542 long x = GET_X_LPARAM(msg->lParam) / ratio; 543 long y = GET_Y_LPARAM(msg->lParam) / ratio; 544 // 将屏幕坐标转换为窗口的局部坐标。 545 QPoint pos = mapFromGlobal(QPoint(x, y)); 546 //qDebug() << "pos = " << pos; // 调试输出鼠标在窗口中的位置。 547 548 // 如果鼠标Y坐标大于10像素(避开顶部的拉伸区域),并且鼠标在ui->widget_title2(通常是自定义标题栏)的矩形范围内。 549 // 这里的10像素是为了避免与顶部边框的拉伸区域冲突。 550 if (pos.y() > 10 && ui->widget_title2->rect().contains(pos)) { 551 //qDebug() << "标题栏被按下"; // 调试输出。 552 // 根据当前鼠标的位置显示不同的样式; 553 // 设置*result为HTCAPTION,告诉Windows,鼠标当前位于标题栏区域。 554 // 这将启用标准的标题栏行为,如拖拽窗口移动,以及双击标题栏最大化/还原。 555 *result = HTCAPTION; 556 return true; // 表示已处理此消息。 557 } 558 } 559 // 处理WM_GETMINMAXINFO消息:当窗口最大化或最小化时,或者当窗口需要计算其最大化/最小化大小和位置时发送。 560 case WM_GETMINMAXINFO: 561 { 562 // 检查窗口当前是否处于最大化状态。 563 if (::IsZoomed(msg->hwnd)) { // ::IsZoomed是Windows API函数,检查窗口是否最大化。 564 isMaxShow = true; // 设置最大化状态标志。 565 showFlag = true; // 设置另一个状态标志,可能用于控制UI元素的显示。 566 // 最大化时会超出屏幕,所以填充边框间距。 567 // 当自定义无边框窗口最大化时,Windows默认会将窗口扩展到整个屏幕,包括任务栏区域,导致窗口边缘超出屏幕。 568 // AdjustWindowRectEx用于计算一个窗口矩形(frame)所需的尺寸,以便客户区达到指定大小。 569 // 这里反过来使用它来获取Windows默认的窗口边框大小。 570 RECT frame = { 0, 0, 0, 0 }; 571 // WS_OVERLAPPEDWINDOW是标准重叠窗口的样式,FALSE表示不含菜单,0表示无扩展样式。 572 AdjustWindowRectEx(&frame, WS_OVERLAPPEDWINDOW, FALSE, 0); 573 frame.left = abs(frame.left); // 获取左边框的宽度(通常为负值,取绝对值)。 574 frame.top = abs(frame.bottom); // 获取上边框的高度(通常bottom为负值,取绝对值)。 575 // 注意:这里用frame.bottom来获取top边距可能是历史遗留或特定平台行为, 576 // 正常应为abs(frame.top)。如果出现显示问题,此处值得检查。 577 // 设置窗口内容的边距,抵消系统边框,使窗口内容在最大化时正好占据可用屏幕区域。 578 this->setContentsMargins(frame.left, frame.top, frame.right, frame.bottom); 579 } else { 580 isMaxShow = false; // 不处于最大化状态。 581 showFlag = false; // 对应状态标志设为false。 582 } 583 // 调用默认的窗口过程处理此消息,以确保系统继续处理最大化/最小化的其他方面。 584 *result = ::DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam); 585 return true; // 表示已处理此消息。 586 } 587 } 588 // 对于其他未特殊处理的Windows消息,交由QMainWindow的默认nativeEvent处理。 589 return QMainWindow::nativeEvent(eventType, message, result); 590} 591 592// 创建系统托盘图标和菜单。 593void MainWindow::createSystemTray() 594{ 595 // 创建系统托盘图标。 596 // QIcon(":lib/icon9.png")设置托盘图标的图片。 597 // this指定父对象为MainWindow,确保在MainWindow销毁时托盘图标也被正确销毁。 598 trayIcon = new QSystemTrayIcon(QIcon(":lib/icon9.png"), this); 599 trayIcon->setToolTip("Fdog-Kit"); // 设置鼠标悬停在托盘图标上时显示的提示文本。 600 trayIcon->show(); // 显示系统托盘图标。 601 602 // 创建一个菜单。 603 QMenu* menu = new QMenu(); 604 // 设置菜单的窗口标志,使其为无边框窗口,并移除默认阴影,以实现自定义外观。 605 // 这对于菜单的自定义样式通常是必要的,例如实现圆角或自定义背景。 606 menu->setWindowFlags(menu->windowFlags() | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint); 607 menu->setAttribute(Qt::WA_TranslucentBackground); // 设置菜单背景透明。 608 QAction* openAction = new QAction("打开"); // 创建“打开”菜单项。 609 QAction* closeAction = new QAction("退出"); // 创建“退出”菜单项。 610 menu->addAction(openAction); // 将“打开”动作添加到菜单。 611 menu->addAction(closeAction); // 将“退出”动作添加到菜单。 612 // 将菜单设置给系统托盘图标,使其在点击托盘图标时弹出。 613 trayIcon->setContextMenu(menu); 614 615 // 连接菜单项的triggered()信号到槽函数。 616 // 当点击“打开”或“退出”菜单项时,都会触发rece_systemTrayMenu()槽函数。 617 connect(openAction, SIGNAL(triggered()), this, SLOT(rece_systemTrayMenu())); 618 connect(closeAction, SIGNAL(triggered()), this, SLOT(rece_systemTrayMenu())); 619} 620 621// 根据配置文件设置窗口属性。 622// 这个函数当前是空的,表示此功能尚未实现或处于待开发状态。 623void MainWindow::setWindowsByConf() 624{ 625 // 待实现:从配置文件加载窗口位置、大小、主题等设置。 626} 627 628// 关闭按钮点击事件槽函数。 629void MainWindow::on_toolButton_close_clicked() 630{ 631 this->close(); // 关闭当前窗口。 632} 633 634// 最小化按钮点击事件槽函数。 635void MainWindow::on_toolButton_min_clicked() 636{ 637 this->showMinimized(); // 最小化当前窗口。 638} 639 640// 最大化/还原按钮点击事件槽函数。 641void MainWindow::on_toolButton_max_clicked() 642{ 643 // showFlag是一个状态标志,通常用于指示窗口是否处于最大化或全屏状态。 644 // 如果showFlag为false(表示当前未最大化或全屏,处于正常状态) 645 if (!showFlag) { 646 setContentsMargins(0, 0, 0, 0); // 移除内容边距,使窗口内容铺满整个屏幕(无边框,无阴影空间)。 647 this->setWindowState(Qt::WindowState::WindowMaximized); // 将窗口设置为最大化状态。 648 isMaxShow = true; // 更新最大化状态标志。 649 showFlag = true; // 更新通用状态标志。 650 } else { // 如果showFlag为true(表示当前已最大化或全屏) 651 setContentsMargins(10, 10, 10, 10); // 恢复内容边距,为阴影和拉伸区域留出空间。 652 this->setWindowState(Qt::WindowState::WindowNoState); // 将窗口恢复到正常状态。 653 isMaxShow = false; // 更新最大化状态标志。 654 showFlag = false; // 更新通用状态标志。 655 } 656} 657 658// 槽函数:关闭窗口。 659// 此函数功能与on_toolButton_close_clicked()类似,但这里调用的是showMinimized()而不是close()。 660// 这意味着点击这个关闭按钮(如果实际使用它)会将窗口最小化到任务栏或托盘,而不是完全退出程序。 661void MainWindow::closeWindow() 662{ 663 this->showMinimized(); // 将窗口最小化。 664} 665 666// 槽函数:最小化窗口。 667// 此函数功能与on_toolButton_min_clicked()完全相同,可能是为了不同的信号连接或未来的扩展。 668void MainWindow::minWindow() 669{ 670 this->showMinimized(); // 将窗口最小化。 671} 672 673// 槽函数:最大化窗口。 674// 此函数体为空,可能是预留的接口或尚未实现的逻辑。 675void MainWindow::maxWindow() 676{ 677 //mWindow // 占位符或未完成的代码。 678} 679 680// 槽函数:处理全屏切换。 681// 通常由F11快捷键或特定的全屏按钮触发。 682void MainWindow::rece_toolButton_fullScreen_sign() 683{ 684 if (!isFullScreen) { // 如果当前不是全屏状态 685 setContentsMargins(0, 0, 0, 0); // 移除内容边距,确保全屏时内容完全填充屏幕。 686 // 移除centralWidget的圆角,使其铺满整个屏幕。 687 ui->centralWidget->setStyleSheet("#centralWidget {border-image: url(:/lib/back1.png);border-radius:0px;}"); 688 ui->toolButton_max->setIcon(QIcon(":lib/icon-copy.png")); // 更改最大化按钮图标为“还原”样式。 689 //this->showNormal(); // 注释掉,因为接下来会直接showFullScreen。 690 ui->toolButton_min->hide(); // 隐藏最小化按钮。 691 ui->toolButton_max->hide(); // 隐藏最大化按钮。 692 ui->toolButton_close->hide(); // 隐藏关闭按钮。 693 this->showFullScreen(); // 将窗口设置为全屏模式。 694 isFullScreen = true; // 更新全屏状态标志。 695 } else { // 如果当前是全屏状态 696 ui->toolButton_min->show(); // 显示最小化按钮。 697 ui->toolButton_max->show(); // 显示最大化按钮。 698 ui->toolButton_close->show(); // 显示关闭按钮。 699 setContentsMargins(10, 10, 10, 10); // 恢复内容边距。 700 ui->centralWidget->setStyleSheet("#centralWidget {border-image: url(:/lib/back1.png);border-radius:10px;}"); // 恢复centralWidget的圆角样式。 701 ui->toolButton_max->setIcon(QIcon(":lib/Icon_max4.png")); // 恢复最大化按钮图标为“最大化”样式。 702 703 if (showFlag) { // 如果在进入全屏前是最大化状态 704 ui->centralWidget->setStyleSheet("#centralWidget {border-image: url(:/lib/back1.png);border-radius:0px;}"); // 保持centralWidget无圆角(因为是最大化)。 705 setContentsMargins(0, 0, 0, 0); // 保持无边距(因为是最大化)。 706 ui->toolButton_max->setIcon(QIcon(":lib/icon-copy.png")); // 保持最大化图标(因为是最大化)。 707 this->showMaximized(); // 恢复到最大化状态。 708 } else { // 如果在进入全屏前是正常状态 709 this->showNormal(); // 恢复到正常窗口状态。 710 } 711 isFullScreen = false; // 更新全屏状态标志。 712 } 713} 714 715// MainWindow类的析构函数。 716MainWindow::~MainWindow() 717{ 718 delete ui; // 释放由new Ui::MainWindow创建的ui对象内存,防止内存泄漏。 719}