環境
Python3.5.2
PyQt5
陳述
隱藏掉系統的控制欄,實現了自定義的標題控制欄,以及關閉/最大化/最小化的功能,自由調整窗體大小的功能(跟隨一個大佬學的),代碼內有詳細注釋
只要把MainWindow類自己實現就可以了,我把左側欄的demo(可以看我這篇https://www.cnblogs.com/jyroy/p/9457882.html)搭載上了,效果如下
標題欄的風格我和左側欄的風格統一了,還是模仿網易雲音樂的紅色格調(我覺得網易雲的紅色很ok)
代碼
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author: jyroy 4 import sys 5 6 from PyQt5.QtCore import QSize 7 from PyQt5.QtWidgets import QApplication 8 from PyQt5.QtCore import Qt, pyqtSignal, QPoint 9 from PyQt5.QtGui import QFont, QEnterEvent, QPainter, QColor, QPen 10 from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel,QSpacerItem, QSizePolicy, QPushButton 11 from PyQt5.QtGui import QIcon 12 from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QTextEdit 13 from LeftTabWidget import LeftTabWidget 14 # 樣式 15 StyleSheet = """ 16 /*標題欄*/ 17 TitleBar { 18 background-color: red; 19 } 20 /*最小化最大化關閉按鈕通用默認背景*/ 21 #buttonMinimum,#buttonMaximum,#buttonClose { 22 border: none; 23 background-color: red; 24 } 25 /*懸停*/ 26 #buttonMinimum:hover,#buttonMaximum:hover { 27 background-color: red; 28 color: white; 29 } 30 #buttonClose:hover { 31 color: white; 32 } 33 /*鼠標按下不放*/ 34 #buttonMinimum:pressed,#buttonMaximum:pressed { 35 background-color: Firebrick; 36 } 37 #buttonClose:pressed { 38 color: white; 39 background-color: Firebrick; 40 } 41 """ 42 43 class TitleBar(QWidget): 44 45 # 窗口最小化信號 46 windowMinimumed = pyqtSignal() 47 # 窗口最大化信號 48 windowMaximumed = pyqtSignal() 49 # 窗口還原信號 50 windowNormaled = pyqtSignal() 51 # 窗口關閉信號 52 windowClosed = pyqtSignal() 53 # 窗口移動 54 windowMoved = pyqtSignal(QPoint) 55 56 def __init__(self, *args, **kwargs): 57 super(TitleBar, self).__init__(*args, **kwargs) 58 # 支持qss設置背景 59 self.setAttribute(Qt.WA_StyledBackground, True) 60 self.mPos = None 61 self.iconSize = 20 # 圖標的默認大小 62 # 設置默認背景顏色,否則由於受到父窗口的影響導致透明 63 self.setAutoFillBackground(True) 64 palette = self.palette() 65 palette.setColor(palette.Window, QColor(240, 240, 240)) 66 self.setPalette(palette) 67 # 布局 68 layout = QHBoxLayout(self, spacing=0) 69 layout.setContentsMargins(0, 0, 0, 0) 70 # 窗口圖標 71 self.iconLabel = QLabel(self) 72 # self.iconLabel.setScaledContents(True) 73 layout.addWidget(self.iconLabel) 74 # 窗口標題 75 self.titleLabel = QLabel(self) 76 self.titleLabel.setMargin(2) 77 layout.addWidget(self.titleLabel) 78 # 中間伸縮條 79 layout.addSpacerItem(QSpacerItem( 80 40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)) 81 # 利用Webdings字體來顯示圖標 82 font = self.font() or QFont() 83 font.setFamily('Webdings') 84 # 最小化按鈕 85 self.buttonMinimum = QPushButton( 86 '0', self, clicked=self.windowMinimumed.emit, font=font, objectName='buttonMinimum') 87 layout.addWidget(self.buttonMinimum) 88 # 最大化/還原按鈕 89 self.buttonMaximum = QPushButton( 90 '1', self, clicked=self.showMaximized, font=font, objectName='buttonMaximum') 91 layout.addWidget(self.buttonMaximum) 92 # 關閉按鈕 93 self.buttonClose = QPushButton( 94 'r', self, clicked=self.windowClosed.emit, font=font, objectName='buttonClose') 95 layout.addWidget(self.buttonClose) 96 # 初始高度 97 self.setHeight() 98 99 def showMaximized(self): 100 if self.buttonMaximum.text() == '1': 101 # 最大化 102 self.buttonMaximum.setText('2') 103 self.windowMaximumed.emit() 104 else: # 還原 105 self.buttonMaximum.setText('1') 106 self.windowNormaled.emit() 107 108 def setHeight(self, height=38): 109 """設置標題欄高度""" 110 self.setMinimumHeight(height) 111 self.setMaximumHeight(height) 112 # 設置右邊按鈕的大小 113 self.buttonMinimum.setMinimumSize(height, height) 114 self.buttonMinimum.setMaximumSize(height, height) 115 self.buttonMaximum.setMinimumSize(height, height) 116 self.buttonMaximum.setMaximumSize(height, height) 117 self.buttonClose.setMinimumSize(height, height) 118 self.buttonClose.setMaximumSize(height, height) 119 120 def setTitle(self, title): 121 """設置標題""" 122 self.titleLabel.setText(title) 123 124 def setIcon(self, icon): 125 """設置圖標""" 126 self.iconLabel.setPixmap(icon.pixmap(self.iconSize, self.iconSize)) 127 128 def setIconSize(self, size): 129 """設置圖標大小""" 130 self.iconSize = size 131 132 def enterEvent(self, event): 133 self.setCursor(Qt.ArrowCursor) 134 super(TitleBar, self).enterEvent(event) 135 136 def mouseDoubleClickEvent(self, event): 137 super(TitleBar, self).mouseDoubleClickEvent(event) 138 self.showMaximized() 139 140 def mousePressEvent(self, event): 141 """鼠標點擊事件""" 142 if event.button() == Qt.LeftButton: 143 self.mPos = event.pos() 144 event.accept() 145 146 def mouseReleaseEvent(self, event): 147 '''鼠標彈起事件''' 148 self.mPos = None 149 event.accept() 150 151 def mouseMoveEvent(self, event): 152 if event.buttons() == Qt.LeftButton and self.mPos: 153 self.windowMoved.emit(self.mapToGlobal(event.pos() - self.mPos)) 154 event.accept() 155 156 # 枚舉左上右下以及四個定點 157 Left, Top, Right, Bottom, LeftTop, RightTop, LeftBottom, RightBottom = range(8) 158 159 class FramelessWindow(QWidget): 160 161 # 四周邊距 162 Margins = 5 163 164 def __init__(self, *args, **kwargs): 165 super(FramelessWindow, self).__init__(*args, **kwargs) 166 167 self._pressed = False 168 self.Direction = None 169 # 背景透明 170 self.setAttribute(Qt.WA_TranslucentBackground, True) 171 # 無邊框 172 self.setWindowFlags(Qt.FramelessWindowHint) # 隱藏邊框 173 # 鼠標跟蹤 174 self.setMouseTracking(True) 175 # 布局 176 layout = QVBoxLayout(self, spacing=0) 177 # 預留邊界用於實現無邊框窗口調整大小 178 layout.setContentsMargins( 179 self.Margins, self.Margins, self.Margins, self.Margins) 180 # 標題欄 181 self.titleBar = TitleBar(self) 182 layout.addWidget(self.titleBar) 183 # 信號槽 184 self.titleBar.windowMinimumed.connect(self.showMinimized) 185 self.titleBar.windowMaximumed.connect(self.showMaximized) 186 self.titleBar.windowNormaled.connect(self.showNormal) 187 self.titleBar.windowClosed.connect(self.close) 188 self.titleBar.windowMoved.connect(self.move) 189 self.windowTitleChanged.connect(self.titleBar.setTitle) 190 self.windowIconChanged.connect(self.titleBar.setIcon) 191 192 def setTitleBarHeight(self, height=38): 193 """設置標題欄高度""" 194 self.titleBar.setHeight(height) 195 196 def setIconSize(self, size): 197 """設置圖標的大小""" 198 self.titleBar.setIconSize(size) 199 200 def setWidget(self, widget): 201 """設置自己的控件""" 202 if hasattr(self, '_widget'): 203 return 204 self._widget = widget 205 # 設置默認背景顏色,否則由於受到父窗口的影響導致透明 206 self._widget.setAutoFillBackground(True) 207 palette = self._widget.palette() 208 palette.setColor(palette.Window, QColor(240, 240, 240)) 209 self._widget.setPalette(palette) 210 self._widget.installEventFilter(self) 211 self.layout().addWidget(self._widget) 212 213 def move(self, pos): 214 if self.windowState() == Qt.WindowMaximized or self.windowState() == Qt.WindowFullScreen: 215 # 最大化或者全屏則不允許移動 216 return 217 super(FramelessWindow, self).move(pos) 218 219 def showMaximized(self): 220 """最大化,要去除上下左右邊界,如果不去除則邊框地方會有空隙""" 221 super(FramelessWindow, self).showMaximized() 222 self.layout().setContentsMargins(0, 0, 0, 0) 223 224 def showNormal(self): 225 """還原,要保留上下左右邊界,否則沒有邊框無法調整""" 226 super(FramelessWindow, self).showNormal() 227 self.layout().setContentsMargins( 228 self.Margins, self.Margins, self.Margins, self.Margins) 229 230 def eventFilter(self, obj, event): 231 """事件過濾器,用於解決鼠標進入其它控件后還原為標准鼠標樣式""" 232 if isinstance(event, QEnterEvent): 233 self.setCursor(Qt.ArrowCursor) 234 return super(FramelessWindow, self).eventFilter(obj, event) 235 236 def paintEvent(self, event): 237 """由於是全透明背景窗口,重繪事件中繪制透明度為1的難以發現的邊框,用於調整窗口大小""" 238 super(FramelessWindow, self).paintEvent(event) 239 painter = QPainter(self) 240 painter.setPen(QPen(QColor(255, 255, 255, 1), 2 * self.Margins)) 241 painter.drawRect(self.rect()) 242 243 def mousePressEvent(self, event): 244 """鼠標點擊事件""" 245 super(FramelessWindow, self).mousePressEvent(event) 246 if event.button() == Qt.LeftButton: 247 self._mpos = event.pos() 248 self._pressed = True 249 250 def mouseReleaseEvent(self, event): 251 '''鼠標彈起事件''' 252 super(FramelessWindow, self).mouseReleaseEvent(event) 253 self._pressed = False 254 self.Direction = None 255 256 def mouseMoveEvent(self, event): 257 """鼠標移動事件""" 258 super(FramelessWindow, self).mouseMoveEvent(event) 259 pos = event.pos() 260 xPos, yPos = pos.x(), pos.y() 261 wm, hm = self.width() - self.Margins, self.height() - self.Margins 262 if self.isMaximized() or self.isFullScreen(): 263 self.Direction = None 264 self.setCursor(Qt.ArrowCursor) 265 return 266 if event.buttons() == Qt.LeftButton and self._pressed: 267 self._resizeWidget(pos) 268 return 269 if xPos <= self.Margins and yPos <= self.Margins: 270 # 左上角 271 self.Direction = LeftTop 272 self.setCursor(Qt.SizeFDiagCursor) 273 elif wm <= xPos <= self.width() and hm <= yPos <= self.height(): 274 # 右下角 275 self.Direction = RightBottom 276 self.setCursor(Qt.SizeFDiagCursor) 277 elif wm <= xPos and yPos <= self.Margins: 278 # 右上角 279 self.Direction = RightTop 280 self.setCursor(Qt.SizeBDiagCursor) 281 elif xPos <= self.Margins and hm <= yPos: 282 # 左下角 283 self.Direction = LeftBottom 284 self.setCursor(Qt.SizeBDiagCursor) 285 elif 0 <= xPos <= self.Margins and self.Margins <= yPos <= hm: 286 # 左邊 287 self.Direction = Left 288 self.setCursor(Qt.SizeHorCursor) 289 elif wm <= xPos <= self.width() and self.Margins <= yPos <= hm: 290 # 右邊 291 self.Direction = Right 292 self.setCursor(Qt.SizeHorCursor) 293 elif self.Margins <= xPos <= wm and 0 <= yPos <= self.Margins: 294 # 上面 295 self.Direction = Top 296 self.setCursor(Qt.SizeVerCursor) 297 elif self.Margins <= xPos <= wm and hm <= yPos <= self.height(): 298 # 下面 299 self.Direction = Bottom 300 self.setCursor(Qt.SizeVerCursor) 301 302 def _resizeWidget(self, pos): 303 """調整窗口大小""" 304 if self.Direction == None: 305 return 306 mpos = pos - self._mpos 307 xPos, yPos = mpos.x(), mpos.y() 308 geometry = self.geometry() 309 x, y, w, h = geometry.x(), geometry.y(), geometry.width(), geometry.height() 310 if self.Direction == LeftTop: # 左上角 311 if w - xPos > self.minimumWidth(): 312 x += xPos 313 w -= xPos 314 if h - yPos > self.minimumHeight(): 315 y += yPos 316 h -= yPos 317 elif self.Direction == RightBottom: # 右下角 318 if w + xPos > self.minimumWidth(): 319 w += xPos 320 self._mpos = pos 321 if h + yPos > self.minimumHeight(): 322 h += yPos 323 self._mpos = pos 324 elif self.Direction == RightTop: # 右上角 325 if h - yPos > self.minimumHeight(): 326 y += yPos 327 h -= yPos 328 if w + xPos > self.minimumWidth(): 329 w += xPos 330 self._mpos.setX(pos.x()) 331 elif self.Direction == LeftBottom: # 左下角 332 if w - xPos > self.minimumWidth(): 333 x += xPos 334 w -= xPos 335 if h + yPos > self.minimumHeight(): 336 h += yPos 337 self._mpos.setY(pos.y()) 338 elif self.Direction == Left: # 左邊 339 if w - xPos > self.minimumWidth(): 340 x += xPos 341 w -= xPos 342 else: 343 return 344 elif self.Direction == Right: # 右邊 345 if w + xPos > self.minimumWidth(): 346 w += xPos 347 self._mpos = pos 348 else: 349 return 350 elif self.Direction == Top: # 上面 351 if h - yPos > self.minimumHeight(): 352 y += yPos 353 h -= yPos 354 else: 355 return 356 elif self.Direction == Bottom: # 下面 357 if h + yPos > self.minimumHeight(): 358 h += yPos 359 self._mpos = pos 360 else: 361 return 362 self.setGeometry(x, y, w, h) 363 364 class MainWindow(QWidget): 365 366 def __init__(self, *args, **kwargs): 367 super(MainWindow, self).__init__(*args, **kwargs) 368 layout = QVBoxLayout(self, spacing=0) 369 layout.setContentsMargins(0, 0, 0, 0) 370 371 self.left_tag = LeftTabWidget() 372 layout.addWidget(self.left_tag) 373 374 375 if __name__ == '__main__': 376 377 app = QApplication(sys.argv) 378 app.setStyleSheet(StyleSheet) 379 mainWnd = FramelessWindow() 380 mainWnd.setWindowTitle('測試標題欄') 381 mainWnd.setWindowIcon(QIcon('Qt.ico')) 382 mainWnd.resize(QSize(1250,780)) 383 mainWnd.setWidget(MainWindow(mainWnd)) # 把自己的窗口添加進來 384 mainWnd.show() 385 sys.exit(app.exec_())
效果展示
拓展知識
設置窗口尺寸的方法:
1.設置寬度和高度。
resize(int w,int h)
resize(QSize s)
2.設置窗口的位置、寬度和高度。
setGeometry(int X,int Y,int W,int H)
setGeometry(QRect r)
3.設置窗口為固定值。
setFixedSize(int w,int h)
setFixedSize(QSize s)
注意:窗口標題欄上的最大化按鈕無效;用鼠標無法調整窗口尺寸。
4.設置窗口為固定值。
setFixedWidth(int w)
窗口標題欄上的最大化按鈕無效;用鼠標無法調整窗口的寬度。
5.設置窗口為固定值。
setFixedHeight(int h)
窗口標題欄上的最大化按鈕無效;用鼠標無法調整窗口的高度。
5.設置窗口的最小尺寸。
setMinimumSize(int w,int h)
setMinimumSize(QSize s)
用鼠標可以讓窗口變寬、變高。
設置窗口的最小寬度:
setMinimumWidth(int w)
設置窗口的最小高度:
setMinimumHeight(int h)
6.設置窗口的最大尺寸。
setMaximumSize(int w,int h)
setMaximumSize(QSize s)
用鼠標可以讓窗口變寬、變高。
設置窗口的最小寬度:
setMaximumWidth(int w)
設置窗口的最小高度:
setMaximumHeight(int h)
1.設置寬度和高度。
resize(int w,int h)
resize(QSize s)
2.設置窗口的位置、寬度和高度。
setGeometry(int X,int Y,int W,int H)
setGeometry(QRect r)
3.設置窗口為固定值。
setFixedSize(int w,int h)
setFixedSize(QSize s)
注意:窗口標題欄上的最大化按鈕無效;用鼠標無法調整窗口尺寸。
4.設置窗口為固定值。
setFixedWidth(int w)
窗口標題欄上的最大化按鈕無效;用鼠標無法調整窗口的寬度。
5.設置窗口為固定值。
setFixedHeight(int h)
窗口標題欄上的最大化按鈕無效;用鼠標無法調整窗口的高度。
5.設置窗口的最小尺寸。
setMinimumSize(int w,int h)
setMinimumSize(QSize s)
用鼠標可以讓窗口變寬、變高。
設置窗口的最小寬度:
setMinimumWidth(int w)
設置窗口的最小高度:
setMinimumHeight(int h)
6.設置窗口的最大尺寸。
setMaximumSize(int w,int h)
setMaximumSize(QSize s)
用鼠標可以讓窗口變寬、變高。
設置窗口的最小寬度:
setMaximumWidth(int w)
設置窗口的最小高度:
setMaximumHeight(int h)
說明
因為只是自己寫的簡單的例子,在窗口方面都是利用的寫死的大小。不同的電腦像素會有差別。我的是1920*1080的設備。在實際用的時候盡量加上判斷,來適應不同的設備。