• Qt第二十七章:QWidget、QMainWindow自定义标题栏并自由移动缩放


    前提:UI必須采用自适应布局。 

    1. 自定义组件【直接CV】custom_components.py
      1. # 自定义组件
      2. """
      3. QCustomTitleBar:自定义标题
      4. QWindowMoveResize:自定义缩放和拖动
      5. """
      6. import PySide6.QtGui
      7. from PySide6.QtGui import QPainter, QBrush
      8. from PySide6 import QtGui, QtWidgets, QtCore
      9. from PySide6.QtCore import Qt, QSize, QRect
      10. from PySide6.QtWidgets import QPushButton, QLabel, QWidget, QProgressBar, QHBoxLayout, QDialog
      11. # 自定义标题栏
      12. class QCustomTitleBar:
      13. def __init__(self, window: QtWidgets):
      14. self.window = window
      15. # 默认标题栏高度 必须设
      16. self.DEFAULT_TITILE_BAR_HEIGHT = 40
      17. # 存储父类的双击事件
      18. self.mouseDoubleClickEvent_parent = self.window.mouseDoubleClickEvent
      19. # 将本类的双击事件赋值给将父类的双击事件
      20. self.window.mouseDoubleClickEvent = self.mouseDoubleClickEvent
      21. # 存储父类的窗口大小改变事件
      22. self.resizeEvent_parent = self.window.resizeEvent
      23. # 将本类的窗口大小改变事件赋值给将父类的窗口大小改变事件
      24. self.window.resizeEvent = self.resizeEvent
      25. # 设置ui文件里main_layout上边距,以免遮挡标题栏
      26. self.window.setContentsMargins(0, self.DEFAULT_TITILE_BAR_HEIGHT, 0, 0)
      27. # 1.设置无边框 和 透明背景 无边框必须设置全,不然会导致点击任务栏不能最小化窗口
      28. self.window.setWindowFlags(
      29. Qt.Window
      30. | Qt.FramelessWindowHint
      31. | Qt.WindowSystemMenuHint
      32. | Qt.WindowMinimizeButtonHint
      33. | Qt.WindowMaximizeButtonHint
      34. )
      35. # self.window.setAttribute(Qt.WA_TranslucentBackground)
      36. # 2.添加自定义的标题栏到最顶部
      37. self.title = QLabel("", self.window)
      38. # 3.设置标题栏样式
      39. self.setStyle()
      40. # 4.添加按钮
      41. # 添加关闭按钮
      42. self.close_btn = QPushButton("", self.window)
      43. self.close_btn.setGeometry(self.window.width() - 33, 10, 20, 20)
      44. # 添加最大化按钮
      45. self.max_btn = QPushButton("", self.window)
      46. self.max_btn.setGeometry(self.window.width() - 66, 10, 20, 20)
      47. # 添加最小化按钮
      48. self.min_btn = QPushButton("", self.window)
      49. self.min_btn.setGeometry(self.window.width() - 99, 10, 20, 20)
      50. # 设置三个按钮的鼠标样式
      51. self.close_btn.setCursor(Qt.PointingHandCursor)
      52. self.max_btn.setCursor(Qt.PointingHandCursor)
      53. self.min_btn.setCursor(Qt.PointingHandCursor)
      54. # 设置三个按钮的样式
      55. self.close_btn.setStyleSheet(
      56. "QPushButton{border-image:url('./images/close.png');background:#ff625f;border-radius:10px;}"
      57. "QPushButton:hover{background:#eb4845;}"
      58. )
      59. self.max_btn.setStyleSheet(
      60. "QPushButton{border-image:url('./images/max.png');background:#ffbe2f;border-radius:10px;}"
      61. "QPushButton:hover{background:#ecae27;}"
      62. )
      63. self.min_btn.setStyleSheet(
      64. "QPushButton{border-image:url('./images/min.png');background:#29c941;border-radius:10px;}"
      65. "QPushButton:hover{background:#1ac033;}"
      66. )
      67. # 5.添加工具栏按钮事件
      68. # 关闭按钮点击绑定窗口关闭事件
      69. self.close_btn.pressed.connect(self.window.close)
      70. # 最大化按钮绑定窗口最大化事件
      71. self.max_btn.pressed.connect(self.setMaxEvent)
      72. # 最小化按钮绑定窗口最小化事件
      73. self.min_btn.pressed.connect(self.window.showMinimized)
      74. # 6.记录全屏窗口的大小-ps非常有用
      75. self.window_max_size = None
      76. # 7.设置标题栏鼠标跟踪 鼠标移入触发,不设置,移入标题栏不触发
      77. self.title.setMouseTracking(True)
      78. def setMaxEvent(self, flag=False):
      79. """
      80. @description 最大化按钮绑定窗口最大化事件和事件 拿出来是因为拖动标题栏时需要恢复界面大小
      81. @param flag 是否是拖动标题栏 bool
      82. @return
      83. """
      84. if flag:
      85. if self.window.isMaximized():
      86. self.window.showNormal()
      87. self.max_btn.setStyleSheet(
      88. "QPushButton{border-image:url('./images/max.png');background:#ffbe2f;border-radius:10px;}"
      89. "QPushButton:hover{background:#ecae27;}"
      90. )
      91. return self.window_max_size
      92. return None
      93. else:
      94. if self.window.isMaximized():
      95. self.window.showNormal()
      96. self.max_btn.setStyleSheet(
      97. "QPushButton{border-image:url('./images/max.png');background:#ffbe2f;border-radius:10px;}"
      98. "QPushButton:hover{background:#ecae27;}"
      99. )
      100. else:
      101. self.window.showMaximized()
      102. self.max_btn.setStyleSheet(
      103. "QPushButton{border-image:url('./images/restore.png');background:#ffbe2f;border-radius:10px;}"
      104. "QPushButton:hover{background:#ecae27;}"
      105. )
      106. # 记录最大化窗口的大小 用于返回最大化时拖动窗口恢复前的大小 这个程序循环帧会取不到恢复前的宽度
      107. self.window_max_size = QSize(self.window.width(), self.window.height())
      108. def setStyle(self, style: str = ""):
      109. """
      110. @description 设置自定义标题栏样式
      111. @param
      112. @return
      113. """
      114. # 想要边框 加上border:1px solid #cccccc;
      115. DEFAULT_STYLE = """
      116. background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #fafafa,stop:1 #d1d1d1);
      117. color:#333333;padding:10px;border:1px solid #c6c6c6;
      118. border-top-left-radius:4px;
      119. border-top-right-radius:4px;
      120. """
      121. self.title.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
      122. # 设置样式
      123. self.title.setStyleSheet(DEFAULT_STYLE if not style else DEFAULT_STYLE + style)
      124. # 设置大小
      125. self.title.setGeometry(0, 0, self.window.width(), self.DEFAULT_TITILE_BAR_HEIGHT)
      126. def mouseDoubleClickEvent(self, a0: QtGui.QMouseEvent) -> None:
      127. """
      128. @description 鼠标双击事件
      129. @param
      130. @return
      131. """
      132. # 如果双击的是鼠标左键 且在标题栏范围内 则放大缩小窗口
      133. if a0.button() == Qt.MouseButton.LeftButton and a0.position().y() < self.title.height():
      134. self.setMaxEvent()
      135. return self.mouseDoubleClickEvent_parent(a0)
      136. def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
      137. """
      138. @description 窗口缩放事件
      139. @param
      140. @return
      141. """
      142. # 最大化最小化的时候,需要去改变按钮组位置
      143. self.close_btn.move(self.window.width() - 33, 10)
      144. self.max_btn.move(self.window.width() - 66, 10)
      145. self.min_btn.move(self.window.width() - 99, 10)
      146. self.title.resize(self.window.width(), self.DEFAULT_TITILE_BAR_HEIGHT)
      147. return self.resizeEvent_parent(a0)
      148. # 实现拖动和缩放Widget
      149. class QWindowMoveResizeWidget(QWidget):
      150. def __init__(self, parent=None):
      151. super(QWindowMoveResizeWidget, self).__init__(parent)
      152. # 1.设置无边框 和 透明背景 无边框必须设置全,不然会导致点击任务栏不能最小化窗口
      153. self.setWindowFlags(
      154. Qt.Window
      155. | Qt.FramelessWindowHint
      156. | Qt.WindowSystemMenuHint
      157. | Qt.WindowMinimizeButtonHint
      158. | Qt.WindowMaximizeButtonHint
      159. )
      160. # 设置窗体透明
      161. self.setAttribute(Qt.WA_TranslucentBackground)
      162. # 默认标题栏高度 必须设
      163. self.DEFAULT_TITILE_BAR_HEIGHT = 40
      164. # 鼠标缩放窗口最小宽度,必须设
      165. self.MIN_WINDOW_WIDTH = 10
      166. self.MIN_WINDOW_HEIGHT = 10
      167. # 鼠标拖动窗口的标识
      168. self.m_flag = False
      169. # 初始化鼠标拖动标题栏标志
      170. self.drag_flag = False
      171. # 记录按下时窗口坐标, 这个用于窗口移动
      172. self.win_x = 0
      173. self.win_y = 0
      174. # 记录按下时鼠标坐标,这个用于计算鼠标移动的距离
      175. self.mouse_x = 0
      176. self.mouse_y = 0
      177. # 记录鼠标移入的拖动区域,共8种区域 左上 左 左下 上 下 右上 右 右下
      178. self.left_up = None
      179. self.left = None
      180. self.left_down = None
      181. self.up = None
      182. self.down = None
      183. self.right_up = None
      184. self.right = None
      185. self.right_down = None
      186. # 设置为True则mouseMoveEvent事件不需要按下也能触发,不然要按着鼠标左键或右键才能触发
      187. self.setMouseTracking(True)
      188. # 设置子类的mousetrack
      189. # self.centralwidget.setMouseTracking(True)
      190. # 记录按下时窗口的大小,用于计算鼠标相对于窗口移动的距离,用于缩放
      191. self.win_w = 0
      192. self.win_h = 0
      193. # 初始化鼠标缩放标志
      194. self.move_left_up_flag = False
      195. self.move_left_flag = False
      196. self.move_left_down_flag = False
      197. self.move_up_flag = False
      198. self.move_down_flag = False
      199. self.move_right_up_flag = False
      200. self.move_right_flag = False
      201. self.move_right_down_flag = False
      202. # 设置边框圆角
      203. def paintEvent(self, event: PySide6.QtGui.QPaintEvent) -> None:
      204. painter = QPainter(self)
      205. painter.setRenderHint(QPainter.RenderHint.Antialiasing) # 设置抗锯齿,不然边框会有明显锯齿
      206. painter.setBrush(Qt.white) # 设置窗体颜色
      207. painter.drawRoundedRect(self.rect(), 10, 10)
      208. super().paintEvent(event)
      209. def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
      210. """
      211. @description 窗口缩放事件
      212. @param
      213. @return
      214. """
      215. # 最大化最小化的时候,需要去改变按钮组位置
      216. # self.titleBar.close_btn.move(self.width() - 33, 10)
      217. # self.titleBar.max_btn.move(self.width() - 66, 10)
      218. # self.titleBar.min_btn.move(self.width() - 99, 10)
      219. # self.titleBar.title.resize(self.width(), DEFAULT_TITILE_BAR_HEIGHT)
      220. # 记录鼠标移入的拖动区域,共8种区域
      221. self.left_up = QRect(0, 0, 10, 10)
      222. self.left = QRect(0, 10, 10, self.height() - 20)
      223. self.left_down = QRect(0, self.height() - 10, 10, 10)
      224. self.up = QRect(10, 0, self.width() - 20, 10)
      225. self.down = QRect(10, self.height() - 10, self.width() - 20, 10)
      226. self.right_up = QRect(self.width() - 10, 0, 10, 10)
      227. self.right = QRect(self.width() - 10, 10, 10, self.height() - 20)
      228. self.right_down = QRect(self.width() - 10, self.height() - 10, 10, 10)
      229. return super().resizeEvent(a0)
      230. def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None:
      231. """
      232. 拖动窗口
      233. """
      234. if a0.button() == QtCore.Qt.LeftButton and self.isMaximized() == False and self.cursor().shape() == QtGui.QCursor(
      235. QtCore.Qt.ArrowCursor).shape():
      236. self.m_flag = True
      237. self.m_Position = a0.globalPosition().toPoint() - self.pos() # 获取鼠标相对窗口的位置
      238. a0.accept()
      239. self.setCursor(QtGui.QCursor(QtCore.Qt.OpenHandCursor)) # 更改鼠标图标
      240. else:
      241. """
      242. @description 鼠标按下事件
      243. @param
      244. @return
      245. """
      246. # 记录按下时窗口坐标, 这个用于窗口移动
      247. self.win_x = self.x()
      248. self.win_y = self.y()
      249. # 记录按下时鼠标坐标,这个用于计算鼠标移动的距离
      250. self.mouse_x = a0.globalPosition().x()
      251. self.mouse_y = a0.globalPosition().y()
      252. # 记录按下时窗口的大小,用于计算鼠标相对于窗口移动的距离,用于缩放
      253. self.win_w = self.width()
      254. self.win_h = self.height()
      255. if not self.isMaximized():
      256. # 如果按下的是鼠标左键
      257. if a0.button() == Qt.MouseButton.LeftButton and self.left_up.contains(a0.position().x(),
      258. a0.position().y()):
      259. self.move_left_up_flag = True
      260. if a0.button() == Qt.MouseButton.LeftButton and self.left.contains(a0.position().x(),
      261. a0.position().y()):
      262. self.move_left_flag = True
      263. if a0.button() == Qt.MouseButton.LeftButton and self.left_down.contains(
      264. a0.position().x(), a0.position().y()
      265. ):
      266. self.move_left_down_flag = True
      267. if a0.button() == Qt.MouseButton.LeftButton and self.up.contains(a0.position().x(), a0.position().y()):
      268. self.move_up_flag = True
      269. if a0.button() == Qt.MouseButton.LeftButton and self.down.contains(a0.position().x(),
      270. a0.position().y()):
      271. self.move_down_flag = True
      272. if a0.button() == Qt.MouseButton.LeftButton and self.right_up.contains(
      273. a0.position().x(), a0.position().y()
      274. ):
      275. self.move_right_up_flag = True
      276. if a0.button() == Qt.MouseButton.LeftButton and self.right.contains(a0.position().x(),
      277. a0.position().y()):
      278. self.move_right_flag = True
      279. if a0.button() == Qt.MouseButton.LeftButton and self.right_down.contains(
      280. a0.position().x(), a0.position().y()
      281. ):
      282. self.move_right_down_flag = True
      283. return super().mousePressEvent(a0)
      284. def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None:
      285. """
      286. 拖动窗口
      287. """
      288. if QtCore.Qt.LeftButton and self.m_flag and self.cursor().shape() == QtGui.QCursor(
      289. QtCore.Qt.OpenHandCursor).shape():
      290. self.move(a0.globalPosition().toPoint() - self.m_Position) # 更改窗口位置
      291. a0.accept()
      292. else:
      293. """
      294. @description 鼠标按下移动事件
      295. @param
      296. @return
      297. """
      298. # 获取移动后鼠标的位置
      299. mouse_move_x = a0.globalPosition().x()
      300. mouse_move_y = a0.globalPosition().y()
      301. # 计算移动的距离
      302. offset_x = mouse_move_x - self.mouse_x
      303. offset_y = mouse_move_y - self.mouse_y
      304. # 移动鼠标时设置鼠标样式
      305. if not self.isMaximized():
      306. # 不是拖动的时才可能是缩放状态
      307. if not self.drag_flag:
      308. # 左上
      309. if self.left_up.contains(a0.position().x(), a0.position().y()):
      310. self.setCursor(Qt.SizeFDiagCursor)
      311. # 左
      312. elif self.left.contains(a0.position().x(), a0.position().y()):
      313. self.setCursor(Qt.SizeHorCursor)
      314. # 左下
      315. elif self.left_down.contains(a0.position().x(), a0.position().y()):
      316. self.setCursor(Qt.SizeBDiagCursor)
      317. # 上
      318. elif self.up.contains(a0.position().x(), a0.position().y()):
      319. self.setCursor(Qt.SizeVerCursor)
      320. # 下
      321. elif self.down.contains(a0.position().x(), a0.position().y()):
      322. self.setCursor(Qt.SizeVerCursor)
      323. # 右上
      324. elif self.right_up.contains(a0.position().x(), a0.position().y()):
      325. self.setCursor(Qt.SizeBDiagCursor)
      326. # 右
      327. elif self.right.contains(a0.position().x(), a0.position().y()):
      328. self.setCursor(Qt.SizeHorCursor)
      329. # 右下
      330. elif self.right_down.contains(a0.position().x(), a0.position().y()):
      331. self.setCursor(Qt.SizeFDiagCursor)
      332. else:
      333. self.setCursor(Qt.ArrowCursor)
      334. else:
      335. self.setCursor(Qt.ArrowCursor)
      336. else:
      337. self.setCursor(Qt.ArrowCursor)
      338. # 如果按下且在左上角范围内则缩放(其他代码参考左上)
      339. if self.move_left_up_flag:
      340. # 拖动的时候也要设置一下形状
      341. self.setCursor(Qt.SizeFDiagCursor)
      342. resize_w = self.win_w - offset_x
      343. resize_h = self.win_h - offset_y
      344. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      345. resize_w = self.MIN_WINDOW_WIDTH if resize_w < self.MIN_WINDOW_WIDTH else resize_w
      346. resize_h = self.MIN_WINDOW_HEIGHT if resize_h < self.MIN_WINDOW_HEIGHT else resize_h
      347. # 设置窗口缩放尺寸
      348. self.resize(resize_w, resize_h)
      349. # 设置窗口移动,需要鼠标跟随
      350. # x y 都要鼠标跟随
      351. if resize_w != self.MIN_WINDOW_WIDTH and resize_h != self.MIN_WINDOW_HEIGHT:
      352. self.move(self.win_x + offset_x, self.win_y + offset_y)
      353. # 缩放宽度等于最小宽度,高度鼠标跟随
      354. if resize_w == self.MIN_WINDOW_WIDTH and resize_h != self.MIN_WINDOW_HEIGHT:
      355. self.move(self.x(), self.win_y + offset_y)
      356. # 缩放高度等于最小高度,宽度鼠标跟随
      357. if resize_w != self.MIN_WINDOW_WIDTH and resize_h == self.MIN_WINDOW_HEIGHT:
      358. self.move(self.win_x + offset_x, self.y())
      359. # 如果按下且在左边范围内则缩放
      360. elif self.move_left_flag:
      361. # 拖动的时候也要设置一下形状
      362. self.setCursor(Qt.SizeHorCursor)
      363. resize_w = self.win_w - offset_x
      364. resize_h = self.win_h
      365. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      366. resize_w = self.MIN_WINDOW_WIDTH if resize_w < self.MIN_WINDOW_WIDTH else resize_w
      367. # 设置窗口缩放尺寸
      368. self.resize(resize_w, resize_h)
      369. # 设置窗口移动,需要鼠标跟随
      370. # 只要宽度鼠标跟随
      371. if resize_w != self.MIN_WINDOW_WIDTH:
      372. self.move(self.win_x + offset_x, self.win_y)
      373. # 如果按下且在左下角范围内则缩放
      374. elif self.move_left_down_flag:
      375. # 拖动的时候也要设置一下形状
      376. self.setCursor(Qt.SizeBDiagCursor)
      377. resize_w = self.win_w - offset_x
      378. resize_h = self.win_h + offset_y
      379. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      380. resize_w = self.MIN_WINDOW_WIDTH if resize_w < self.MIN_WINDOW_WIDTH else resize_w
      381. resize_h = self.MIN_WINDOW_HEIGHT if resize_h < self.MIN_WINDOW_HEIGHT else resize_h
      382. # 设置窗口缩放尺寸
      383. self.resize(resize_w, resize_h)
      384. # 设置窗口移动,需要鼠标跟随
      385. # x y 都要鼠标跟随
      386. if resize_w != self.MIN_WINDOW_WIDTH and resize_h != self.MIN_WINDOW_HEIGHT:
      387. self.move(self.win_x + offset_x, self.y())
      388. # 缩放高度等于最小高度,宽度鼠标跟随
      389. if resize_w != self.MIN_WINDOW_WIDTH and resize_h == self.MIN_WINDOW_HEIGHT:
      390. self.move(self.win_x + offset_x, self.y())
      391. # 如果按下且在上边范围内则缩放
      392. elif self.move_up_flag:
      393. # 拖动的时候也要设置一下形状
      394. self.setCursor(Qt.SizeVerCursor)
      395. resize_w = self.win_w
      396. resize_h = self.win_h - offset_y
      397. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      398. resize_h = self.MIN_WINDOW_HEIGHT if resize_h < self.MIN_WINDOW_HEIGHT else resize_h
      399. # 设置窗口缩放尺寸
      400. self.resize(resize_w, resize_h)
      401. # 设置窗口移动,需要鼠标跟随
      402. # 只要高度鼠标跟随
      403. if resize_h != self.MIN_WINDOW_HEIGHT:
      404. self.move(self.win_x, self.win_y + offset_y)
      405. # 如果按下且在下边范围内则缩放
      406. elif self.move_down_flag:
      407. # 拖动的时候也要设置一下形状
      408. self.setCursor(Qt.SizeVerCursor)
      409. resize_w = self.win_w
      410. resize_h = self.win_h + offset_y
      411. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      412. resize_h = self.MIN_WINDOW_HEIGHT if resize_h < self.MIN_WINDOW_HEIGHT else resize_h
      413. # 设置窗口缩放尺寸
      414. self.resize(resize_w, resize_h)
      415. # 如果按下且在右上角范围内则缩放
      416. elif self.move_right_up_flag:
      417. # 拖动的时候也要设置一下形状
      418. self.setCursor(Qt.SizeBDiagCursor)
      419. resize_w = self.win_w + offset_x
      420. resize_h = self.win_h - offset_y
      421. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      422. resize_w = self.MIN_WINDOW_WIDTH if resize_w < self.MIN_WINDOW_WIDTH else resize_w
      423. resize_h = self.MIN_WINDOW_HEIGHT if resize_h < self.MIN_WINDOW_HEIGHT else resize_h
      424. # 设置窗口缩放尺寸
      425. self.resize(resize_w, resize_h)
      426. # 设置窗口移动,需要鼠标跟随
      427. # x y 都要鼠标跟随
      428. if resize_w != self.MIN_WINDOW_WIDTH and resize_h != self.MIN_WINDOW_HEIGHT:
      429. self.move(self.win_x, self.win_y + offset_y)
      430. # 缩放宽度等于最小宽度,高度鼠标跟随
      431. if resize_w == self.MIN_WINDOW_WIDTH and resize_h != self.MIN_WINDOW_HEIGHT:
      432. self.move(self.x(), self.win_y + offset_y)
      433. # 如果按下且在右边范围内则缩放
      434. elif self.move_right_flag:
      435. # 拖动的时候也要设置一下形状
      436. self.setCursor(Qt.SizeHorCursor)
      437. resize_w = self.win_w + offset_x
      438. resize_h = self.win_h
      439. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      440. resize_w = self.MIN_WINDOW_WIDTH if resize_w < self.MIN_WINDOW_WIDTH else resize_w
      441. # 设置窗口缩放尺寸
      442. self.resize(resize_w, resize_h)
      443. # 如果按下且在右下角范围内则缩放
      444. elif self.move_right_down_flag:
      445. # 拖动的时候也要设置一下形状
      446. self.setCursor(Qt.SizeFDiagCursor)
      447. resize_w = self.win_w + offset_x
      448. resize_h = self.win_h + offset_y
      449. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      450. resize_w = self.MIN_WINDOW_WIDTH if resize_w < self.MIN_WINDOW_WIDTH else resize_w
      451. resize_h = self.MIN_WINDOW_HEIGHT if resize_h < self.MIN_WINDOW_HEIGHT else resize_h
      452. # 设置窗口缩放尺寸
      453. self.resize(resize_w, resize_h)
      454. # 如果按下才能移动
      455. elif self.drag_flag:
      456. # 设置窗口移动的距离
      457. self.move(self.win_x + offset_x, self.win_y + offset_y)
      458. return super().mouseMoveEvent(a0)
      459. def mouseReleaseEvent(self, a0: QtGui.QMouseEvent) -> None:
      460. self.m_flag = False
      461. self.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
      462. """
      463. @description 鼠标按下松开事件
      464. @param
      465. @return
      466. """
      467. self.drag_flag = False
      468. self.move_left_up_flag = False
      469. self.move_left_flag = False
      470. self.move_left_down_flag = False
      471. self.move_up_flag = False
      472. self.move_down_flag = False
      473. self.move_right_up_flag = False
      474. self.move_right_flag = False
      475. self.move_right_down_flag = False
      476. self.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
      477. return super().mouseReleaseEvent(a0)
      478. # 实现拖动和缩放Dialog
      479. class QWindowMoveResizeDialog(QDialog):
      480. def __init__(self, parent=None):
      481. super(QWindowMoveResizeDialog, self).__init__(parent)
      482. # 1.设置无边框 和 透明背景 无边框必须设置全,不然会导致点击任务栏不能最小化窗口
      483. self.setWindowFlags(
      484. Qt.Window
      485. | Qt.FramelessWindowHint
      486. | Qt.WindowSystemMenuHint
      487. | Qt.WindowMinimizeButtonHint
      488. | Qt.WindowMaximizeButtonHint
      489. )
      490. # 窗体透明
      491. self.setAttribute(Qt.WA_TranslucentBackground)
      492. # 默认标题栏高度 必须设
      493. self.DEFAULT_TITILE_BAR_HEIGHT = 40
      494. # 鼠标缩放窗口最小宽度,必须设
      495. self.MIN_WINDOW_WIDTH = 10
      496. self.MIN_WINDOW_HEIGHT = 10
      497. # 鼠标拖动窗口的标识
      498. self.m_flag = False
      499. # 初始化鼠标拖动标题栏标志
      500. self.drag_flag = False
      501. # 记录按下时窗口坐标, 这个用于窗口移动
      502. self.win_x = 0
      503. self.win_y = 0
      504. # 记录按下时鼠标坐标,这个用于计算鼠标移动的距离
      505. self.mouse_x = 0
      506. self.mouse_y = 0
      507. # 记录鼠标移入的拖动区域,共8种区域 左上 左 左下 上 下 右上 右 右下
      508. self.left_up = None
      509. self.left = None
      510. self.left_down = None
      511. self.up = None
      512. self.down = None
      513. self.right_up = None
      514. self.right = None
      515. self.right_down = None
      516. # 设置为True则mouseMoveEvent事件不需要按下也能触发,不然要按着鼠标左键或右键才能触发
      517. self.setMouseTracking(True)
      518. # 设置子类的mousetrack
      519. # self.centralwidget.setMouseTracking(True)
      520. # 记录按下时窗口的大小,用于计算鼠标相对于窗口移动的距离,用于缩放
      521. self.win_w = 0
      522. self.win_h = 0
      523. # 初始化鼠标缩放标志
      524. self.move_left_up_flag = False
      525. self.move_left_flag = False
      526. self.move_left_down_flag = False
      527. self.move_up_flag = False
      528. self.move_down_flag = False
      529. self.move_right_up_flag = False
      530. self.move_right_flag = False
      531. self.move_right_down_flag = False
      532. # 设置边框圆角
      533. def paintEvent(self, event: PySide6.QtGui.QPaintEvent) -> None:
      534. painter = QPainter(self)
      535. painter.setRenderHint(QPainter.RenderHint.Antialiasing) # 设置抗锯齿,不然边框会有明显锯齿
      536. painter.setBrush(Qt.white) # 设置窗体颜色
      537. painter.drawRoundedRect(self.rect(), 10, 10)
      538. super().paintEvent(event)
      539. def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
      540. """
      541. @description 窗口缩放事件
      542. @param
      543. @return
      544. """
      545. # 最大化最小化的时候,需要去改变按钮组位置
      546. # self.titleBar.close_btn.move(self.width() - 33, 10)
      547. # self.titleBar.max_btn.move(self.width() - 66, 10)
      548. # self.titleBar.min_btn.move(self.width() - 99, 10)
      549. # self.titleBar.title.resize(self.width(), DEFAULT_TITILE_BAR_HEIGHT)
      550. # 记录鼠标移入的拖动区域,共8种区域
      551. self.left_up = QRect(0, 0, 10, 10)
      552. self.left = QRect(0, 10, 10, self.height() - 20)
      553. self.left_down = QRect(0, self.height() - 10, 10, 10)
      554. self.up = QRect(10, 0, self.width() - 20, 10)
      555. self.down = QRect(10, self.height() - 10, self.width() - 20, 10)
      556. self.right_up = QRect(self.width() - 10, 0, 10, 10)
      557. self.right = QRect(self.width() - 10, 10, 10, self.height() - 20)
      558. self.right_down = QRect(self.width() - 10, self.height() - 10, 10, 10)
      559. return super().resizeEvent(a0)
      560. def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None:
      561. """
      562. 拖动窗口
      563. """
      564. if a0.button() == QtCore.Qt.LeftButton and self.isMaximized() == False and self.cursor().shape() == QtGui.QCursor(
      565. QtCore.Qt.ArrowCursor).shape():
      566. self.m_flag = True
      567. self.m_Position = a0.globalPosition().toPoint() - self.pos() # 获取鼠标相对窗口的位置
      568. a0.accept()
      569. self.setCursor(QtGui.QCursor(QtCore.Qt.OpenHandCursor)) # 更改鼠标图标
      570. else:
      571. """
      572. @description 鼠标按下事件
      573. @param
      574. @return
      575. """
      576. # 记录按下时窗口坐标, 这个用于窗口移动
      577. self.win_x = self.x()
      578. self.win_y = self.y()
      579. # 记录按下时鼠标坐标,这个用于计算鼠标移动的距离
      580. self.mouse_x = a0.globalPosition().x()
      581. self.mouse_y = a0.globalPosition().y()
      582. # 记录按下时窗口的大小,用于计算鼠标相对于窗口移动的距离,用于缩放
      583. self.win_w = self.width()
      584. self.win_h = self.height()
      585. if not self.isMaximized():
      586. # 如果按下的是鼠标左键
      587. if a0.button() == Qt.MouseButton.LeftButton and self.left_up.contains(a0.position().x(),
      588. a0.position().y()):
      589. self.move_left_up_flag = True
      590. if a0.button() == Qt.MouseButton.LeftButton and self.left.contains(a0.position().x(),
      591. a0.position().y()):
      592. self.move_left_flag = True
      593. if a0.button() == Qt.MouseButton.LeftButton and self.left_down.contains(
      594. a0.position().x(), a0.position().y()
      595. ):
      596. self.move_left_down_flag = True
      597. if a0.button() == Qt.MouseButton.LeftButton and self.up.contains(a0.position().x(), a0.position().y()):
      598. self.move_up_flag = True
      599. if a0.button() == Qt.MouseButton.LeftButton and self.down.contains(a0.position().x(),
      600. a0.position().y()):
      601. self.move_down_flag = True
      602. if a0.button() == Qt.MouseButton.LeftButton and self.right_up.contains(
      603. a0.position().x(), a0.position().y()
      604. ):
      605. self.move_right_up_flag = True
      606. if a0.button() == Qt.MouseButton.LeftButton and self.right.contains(a0.position().x(),
      607. a0.position().y()):
      608. self.move_right_flag = True
      609. if a0.button() == Qt.MouseButton.LeftButton and self.right_down.contains(
      610. a0.position().x(), a0.position().y()
      611. ):
      612. self.move_right_down_flag = True
      613. return super().mousePressEvent(a0)
      614. def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None:
      615. """
      616. 拖动窗口
      617. """
      618. if QtCore.Qt.LeftButton and self.m_flag and self.cursor().shape() == QtGui.QCursor(
      619. QtCore.Qt.OpenHandCursor).shape():
      620. self.move(a0.globalPosition().toPoint() - self.m_Position) # 更改窗口位置
      621. a0.accept()
      622. else:
      623. """
      624. @description 鼠标按下移动事件
      625. @param
      626. @return
      627. """
      628. # 获取移动后鼠标的位置
      629. mouse_move_x = a0.globalPosition().x()
      630. mouse_move_y = a0.globalPosition().y()
      631. # 计算移动的距离
      632. offset_x = mouse_move_x - self.mouse_x
      633. offset_y = mouse_move_y - self.mouse_y
      634. # 移动鼠标时设置鼠标样式
      635. if not self.isMaximized():
      636. # 不是拖动的时才可能是缩放状态
      637. if not self.drag_flag:
      638. # 左上
      639. if self.left_up.contains(a0.position().x(), a0.position().y()):
      640. self.setCursor(Qt.SizeFDiagCursor)
      641. # 左
      642. elif self.left.contains(a0.position().x(), a0.position().y()):
      643. self.setCursor(Qt.SizeHorCursor)
      644. # 左下
      645. elif self.left_down.contains(a0.position().x(), a0.position().y()):
      646. self.setCursor(Qt.SizeBDiagCursor)
      647. # 上
      648. elif self.up.contains(a0.position().x(), a0.position().y()):
      649. self.setCursor(Qt.SizeVerCursor)
      650. # 下
      651. elif self.down.contains(a0.position().x(), a0.position().y()):
      652. self.setCursor(Qt.SizeVerCursor)
      653. # 右上
      654. elif self.right_up.contains(a0.position().x(), a0.position().y()):
      655. self.setCursor(Qt.SizeBDiagCursor)
      656. # 右
      657. elif self.right.contains(a0.position().x(), a0.position().y()):
      658. self.setCursor(Qt.SizeHorCursor)
      659. # 右下
      660. elif self.right_down.contains(a0.position().x(), a0.position().y()):
      661. self.setCursor(Qt.SizeFDiagCursor)
      662. else:
      663. self.setCursor(Qt.ArrowCursor)
      664. else:
      665. self.setCursor(Qt.ArrowCursor)
      666. else:
      667. self.setCursor(Qt.ArrowCursor)
      668. # 如果按下且在左上角范围内则缩放(其他代码参考左上)
      669. if self.move_left_up_flag:
      670. # 拖动的时候也要设置一下形状
      671. self.setCursor(Qt.SizeFDiagCursor)
      672. resize_w = self.win_w - offset_x
      673. resize_h = self.win_h - offset_y
      674. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      675. resize_w = self.MIN_WINDOW_WIDTH if resize_w < self.MIN_WINDOW_WIDTH else resize_w
      676. resize_h = self.MIN_WINDOW_HEIGHT if resize_h < self.MIN_WINDOW_HEIGHT else resize_h
      677. # 设置窗口缩放尺寸
      678. self.resize(resize_w, resize_h)
      679. # 设置窗口移动,需要鼠标跟随
      680. # x y 都要鼠标跟随
      681. if resize_w != self.MIN_WINDOW_WIDTH and resize_h != self.MIN_WINDOW_HEIGHT:
      682. self.move(self.win_x + offset_x, self.win_y + offset_y)
      683. # 缩放宽度等于最小宽度,高度鼠标跟随
      684. if resize_w == self.MIN_WINDOW_WIDTH and resize_h != self.MIN_WINDOW_HEIGHT:
      685. self.move(self.x(), self.win_y + offset_y)
      686. # 缩放高度等于最小高度,宽度鼠标跟随
      687. if resize_w != self.MIN_WINDOW_WIDTH and resize_h == self.MIN_WINDOW_HEIGHT:
      688. self.move(self.win_x + offset_x, self.y())
      689. # 如果按下且在左边范围内则缩放
      690. elif self.move_left_flag:
      691. # 拖动的时候也要设置一下形状
      692. self.setCursor(Qt.SizeHorCursor)
      693. resize_w = self.win_w - offset_x
      694. resize_h = self.win_h
      695. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      696. resize_w = self.MIN_WINDOW_WIDTH if resize_w < self.MIN_WINDOW_WIDTH else resize_w
      697. # 设置窗口缩放尺寸
      698. self.resize(resize_w, resize_h)
      699. # 设置窗口移动,需要鼠标跟随
      700. # 只要宽度鼠标跟随
      701. if resize_w != self.MIN_WINDOW_WIDTH:
      702. self.move(self.win_x + offset_x, self.win_y)
      703. # 如果按下且在左下角范围内则缩放
      704. elif self.move_left_down_flag:
      705. # 拖动的时候也要设置一下形状
      706. self.setCursor(Qt.SizeBDiagCursor)
      707. resize_w = self.win_w - offset_x
      708. resize_h = self.win_h + offset_y
      709. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      710. resize_w = self.MIN_WINDOW_WIDTH if resize_w < self.MIN_WINDOW_WIDTH else resize_w
      711. resize_h = self.MIN_WINDOW_HEIGHT if resize_h < self.MIN_WINDOW_HEIGHT else resize_h
      712. # 设置窗口缩放尺寸
      713. self.resize(resize_w, resize_h)
      714. # 设置窗口移动,需要鼠标跟随
      715. # x y 都要鼠标跟随
      716. if resize_w != self.MIN_WINDOW_WIDTH and resize_h != self.MIN_WINDOW_HEIGHT:
      717. self.move(self.win_x + offset_x, self.y())
      718. # 缩放高度等于最小高度,宽度鼠标跟随
      719. if resize_w != self.MIN_WINDOW_WIDTH and resize_h == self.MIN_WINDOW_HEIGHT:
      720. self.move(self.win_x + offset_x, self.y())
      721. # 如果按下且在上边范围内则缩放
      722. elif self.move_up_flag:
      723. # 拖动的时候也要设置一下形状
      724. self.setCursor(Qt.SizeVerCursor)
      725. resize_w = self.win_w
      726. resize_h = self.win_h - offset_y
      727. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      728. resize_h = self.MIN_WINDOW_HEIGHT if resize_h < self.MIN_WINDOW_HEIGHT else resize_h
      729. # 设置窗口缩放尺寸
      730. self.resize(resize_w, resize_h)
      731. # 设置窗口移动,需要鼠标跟随
      732. # 只要高度鼠标跟随
      733. if resize_h != self.MIN_WINDOW_HEIGHT:
      734. self.move(self.win_x, self.win_y + offset_y)
      735. # 如果按下且在下边范围内则缩放
      736. elif self.move_down_flag:
      737. # 拖动的时候也要设置一下形状
      738. self.setCursor(Qt.SizeVerCursor)
      739. resize_w = self.win_w
      740. resize_h = self.win_h + offset_y
      741. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      742. resize_h = self.MIN_WINDOW_HEIGHT if resize_h < self.MIN_WINDOW_HEIGHT else resize_h
      743. # 设置窗口缩放尺寸
      744. self.resize(resize_w, resize_h)
      745. # 如果按下且在右上角范围内则缩放
      746. elif self.move_right_up_flag:
      747. # 拖动的时候也要设置一下形状
      748. self.setCursor(Qt.SizeBDiagCursor)
      749. resize_w = self.win_w + offset_x
      750. resize_h = self.win_h - offset_y
      751. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      752. resize_w = self.MIN_WINDOW_WIDTH if resize_w < self.MIN_WINDOW_WIDTH else resize_w
      753. resize_h = self.MIN_WINDOW_HEIGHT if resize_h < self.MIN_WINDOW_HEIGHT else resize_h
      754. # 设置窗口缩放尺寸
      755. self.resize(resize_w, resize_h)
      756. # 设置窗口移动,需要鼠标跟随
      757. # x y 都要鼠标跟随
      758. if resize_w != self.MIN_WINDOW_WIDTH and resize_h != self.MIN_WINDOW_HEIGHT:
      759. self.move(self.win_x, self.win_y + offset_y)
      760. # 缩放宽度等于最小宽度,高度鼠标跟随
      761. if resize_w == self.MIN_WINDOW_WIDTH and resize_h != self.MIN_WINDOW_HEIGHT:
      762. self.move(self.x(), self.win_y + offset_y)
      763. # 如果按下且在右边范围内则缩放
      764. elif self.move_right_flag:
      765. # 拖动的时候也要设置一下形状
      766. self.setCursor(Qt.SizeHorCursor)
      767. resize_w = self.win_w + offset_x
      768. resize_h = self.win_h
      769. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      770. resize_w = self.MIN_WINDOW_WIDTH if resize_w < self.MIN_WINDOW_WIDTH else resize_w
      771. # 设置窗口缩放尺寸
      772. self.resize(resize_w, resize_h)
      773. # 如果按下且在右下角范围内则缩放
      774. elif self.move_right_down_flag:
      775. # 拖动的时候也要设置一下形状
      776. self.setCursor(Qt.SizeFDiagCursor)
      777. resize_w = self.win_w + offset_x
      778. resize_h = self.win_h + offset_y
      779. # 如果缩放后的尺寸小于最小尺寸则窗口不能缩放了
      780. resize_w = self.MIN_WINDOW_WIDTH if resize_w < self.MIN_WINDOW_WIDTH else resize_w
      781. resize_h = self.MIN_WINDOW_HEIGHT if resize_h < self.MIN_WINDOW_HEIGHT else resize_h
      782. # 设置窗口缩放尺寸
      783. self.resize(resize_w, resize_h)
      784. # 如果按下才能移动
      785. elif self.drag_flag:
      786. # 设置窗口移动的距离
      787. self.move(self.win_x + offset_x, self.win_y + offset_y)
      788. return super().mouseMoveEvent(a0)
      789. def mouseReleaseEvent(self, a0: QtGui.QMouseEvent) -> None:
      790. self.m_flag = False
      791. self.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
      792. """
      793. @description 鼠标按下松开事件
      794. @param
      795. @return
      796. """
      797. self.drag_flag = False
      798. self.move_left_up_flag = False
      799. self.move_left_flag = False
      800. self.move_left_down_flag = False
      801. self.move_up_flag = False
      802. self.move_down_flag = False
      803. self.move_right_up_flag = False
      804. self.move_right_flag = False
      805. self.move_right_down_flag = False
      806. self.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
      807. return super().mouseReleaseEvent(a0)
    2. 使用实例
      ①QWidget使用示例
      UI文件(转成py文件)
      1. "1.0" encoding="UTF-8"?>
      2. <ui version="4.0">
      3. <class>Formclass>
      4. <widget class="QWidget" name="Form">
      5. <property name="geometry">
      6. <rect>
      7. <x>0x>
      8. <y>0y>
      9. <width>400width>
      10. <height>300height>
      11. rect>
      12. property>
      13. <property name="windowTitle">
      14. <string>Formstring>
      15. property>
      16. <layout class="QHBoxLayout" name="horizontalLayout_2">
      17. <item>
      18. <layout class="QVBoxLayout" name="verticalLayout">
      19. <item>
      20. <layout class="QHBoxLayout" name="horizontalLayout">
      21. <item>
      22. <widget class="QLineEdit" name="lineEdit"/>
      23. item>
      24. <item>
      25. <widget class="QPushButton" name="pushButton">
      26. <property name="text">
      27. <string>PushButtonstring>
      28. property>
      29. widget>
      30. item>
      31. layout>
      32. item>
      33. <item>
      34. <widget class="QTextEdit" name="textEdit"/>
      35. item>
      36. layout>
      37. item>
      38. layout>
      39. widget>
      40. <resources/>
      41. <connections/>
      42. ui>
      1. from PySide6.QtWidgets import QApplication, QWidget
      2. from custom_components import QWindowMoveResizeWidget, QCustomTitleBar
      3. from ui_widget_test import Ui_Form
      4. class TestWindow(QWindowMoveResizeWidget, Ui_Form):
      5. def __init__(self, parent=None):
      6. super(TestWindow, self).__init__(parent)
      7. self.setupUi(self)
      8. # 自定义标题栏
      9. QCustomTitleBar(self)
      10. if __name__ == "__main__":
      11. app = QApplication([])
      12. window = TestWindow()
      13. window.show()
      14. app.exec()

       ②QMainWindow使用示例
      UI文件(转成py文件)

      1. "1.0" encoding="UTF-8"?>
      2. <ui version="4.0">
      3. <class>MainWindowclass>
      4. <widget class="QMainWindow" name="MainWindow">
      5. <property name="geometry">
      6. <rect>
      7. <x>0x>
      8. <y>0y>
      9. <width>641width>
      10. <height>444height>
      11. rect>
      12. property>
      13. <property name="windowTitle">
      14. <string>MainWindowstring>
      15. property>
      16. <widget class="QWidget" name="centralwidget">
      17. <layout class="QHBoxLayout" name="horizontalLayout_2">
      18. <item>
      19. <layout class="QVBoxLayout" name="verticalLayout">
      20. <item>
      21. <layout class="QHBoxLayout" name="horizontalLayout">
      22. <item>
      23. <widget class="QLineEdit" name="lineEdit"/>
      24. item>
      25. <item>
      26. <widget class="QPushButton" name="pushButton">
      27. <property name="text">
      28. <string>PushButtonstring>
      29. property>
      30. widget>
      31. item>
      32. layout>
      33. item>
      34. <item>
      35. <widget class="QTextEdit" name="textEdit"/>
      36. item>
      37. layout>
      38. item>
      39. layout>
      40. widget>
      41. <widget class="QMenuBar" name="menubar">
      42. <property name="geometry">
      43. <rect>
      44. <x>0x>
      45. <y>0y>
      46. <width>641width>
      47. <height>23height>
      48. rect>
      49. property>
      50. <widget class="QMenu" name="menu">
      51. <property name="title">
      52. <string>菜单string>
      53. property>
      54. widget>
      55. <addaction name="menu"/>
      56. widget>
      57. <widget class="QStatusBar" name="statusbar"/>
      58. widget>
      59. <resources/>
      60. <connections/>
      61. ui>
      1. from PySide6 import QtCore, QtGui
      2. from PySide6.QtWidgets import QApplication, QMainWindow
      3. from custom_components import QCustomTitleBar
      4. from ui_main_window_test import Ui_MainWindow
      5. class TestWindow(QMainWindow, Ui_MainWindow):
      6. def __init__(self, parent=None):
      7. super(TestWindow, self).__init__(parent)
      8. self.setupUi(self)
      9. # 自定义标题栏
      10. QCustomTitleBar(self)
      11. def mousePressEvent(self, event):
      12. if event.button() == QtCore.Qt.LeftButton and self.isMaximized() == False:
      13. self.m_flag = True
      14. self.m_Position = event.globalPosition().toPoint() - self.pos() # 获取鼠标相对窗口的位置
      15. event.accept()
      16. self.setCursor(QtGui.QCursor(QtCore.Qt.OpenHandCursor)) # 更改鼠标图标
      17. def mouseMoveEvent(self, mouse_event):
      18. if QtCore.Qt.LeftButton and self.m_flag:
      19. self.move(mouse_event.globalPosition().toPoint() - self.m_Position) # 更改窗口位置
      20. mouse_event.accept()
      21. def mouseReleaseEvent(self, mouse_event):
      22. self.m_flag = False
      23. self.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
      24. if __name__ == "__main__":
      25. app = QApplication([])
      26. window = TestWindow()
      27. window.show()
      28. app.exec()

  • 相关阅读:
    【机器学习范式】监督学习,无监督学习,强化学习, 半监督学习,自监督学习,迁移学习,对比分析+详解与示例代码
    淘宝/天猫API:item_search_img-按图搜索淘宝商品(拍立淘)
    【管理运筹学】第 9 章 | 网络计划(2,时间参数的计算 —— 工作时间的确定与事项的时间参数)
    Visual Studio Code 1.68.1简介及下载地址
    python学习笔记-09
    初识linux(1)
    Word、Excel、PPT文件转PDF文件(C#)
    C#面对对象(用Dictionary字典实现银行取钱系统)
    恭贺弘博创新2023下半年软考(中/高级)认证课程顺利举行
    代码随想录(番外)图论1
  • 原文地址:https://blog.csdn.net/wenxingchen/article/details/128147506