使用Python3.6.3
PyQt5.13.0
構建了一個無邊框窗口可以使用。
支持鼠標邊緣拖拽大小。
支持雙擊標題最大化和還原。
支持按住標題拖拽位置。
上圖
包含了標題欄。中心窗口,狀態欄。
1 使用方式:
from frame_less import FrameLessWindow
fw = FrameLessWindow()
fw.show()
2 功能和API介紹:
最大化。最小化。關閉。
設置標題 fw.setWindowTitle(''要設置的標題)
設置圖標 fw.setWindowIcon(QIcon('圖標的路徑'))
設置中心窗口 fw.set_center_widget(QWidgetObj)
移除狀態欄 fw.remove_status_bar()
詳細代碼:
修改標題背景的顏色和最大小化按鈕的顏色樣式在TitleBar類QSS
# _*_ coding:utf-8 _*_
# ---------------------------
# Python_Version 3.6.3
# PyQt_Version 5.13.0
# Author: zizle
# Created: 2020-05-16
# ---------------------------
import os
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QPushButton, QLabel, QStatusBar, QStackedWidget
from PyQt5.QtGui import QPixmap, QFont, QEnterEvent, QPainter, QPen, QColor, QIcon
from PyQt5.QtCore import QMargins, Qt
from frames.price_open_interest import PriceOpenInterest
from settings import BASE_DIR
class TitleBar(QWidget):
"""主窗口無邊框後使用的標題欄"""
def __init__(self, *args, **kwargs):
super(TitleBar, self).__init__(*args, **kwargs)
self.setAttribute(Qt.WA_StyledBackground, True) # 支持使用QSS設置背景
self._mouse_pos = None
layout = QHBoxLayout(self)
layout.setContentsMargins(QMargins(3, 3, 3, 3))
layout.setSpacing(2)
self.title_label = QLabel(self)
self.title_label.setScaledContents(True)
self.title_label.setFixedSize(20, 20)
layout.addWidget(self.title_label, alignment=Qt.AlignLeft) # 加入窗口圖標
self.title_text = QLabel(self)
self.title_text.setFixedHeight(20)
layout.addWidget(self.title_text, alignment=Qt.AlignLeft) # 加入窗口文字
layout.addStretch()
self.minimum_button = QPushButton('0', self)
self.maximum_button = QPushButton('1', self)
self.close_button = QPushButton('r', self)
layout.addWidget(self.minimum_button, alignment=Qt.AlignRight)
layout.addWidget(self.maximum_button, alignment=Qt.AlignRight)
layout.addWidget(self.close_button, alignment=Qt.AlignRight)
font = QFont('webdings') # 使用webding字體設置按鈕的圖標
self.minimum_button.setFont(font)
self.maximum_button.setFont(font)
self.close_button.setFont(font)
self.minimum_button.setFixedSize(20, 20)
self.maximum_button.setFixedSize(20, 20)
self.close_button.setFixedSize(20, 20)
self.minimum_button.clicked.connect(self.window_minimum) # 設置點擊的信號事件
self.maximum_button.clicked.connect(self.window_maximum)
self.close_button.clicked.connect(self.window_close)
# 設置objectName來設置樣式
self.setObjectName('titleBar')
self.minimum_button.setObjectName('minimumButton')
self.maximum_button.setObjectName('maximumButton')
self.close_button.setObjectName('closeButton')
self.setStyleSheet("""
#titleBar{
background-color: rgb(34,102,175);
}
#minimumButton,#maximumButton,#closeButton {
border: none;
background-color: rgb(34,102,175);
}
#minimumButton:hover,#maximumButton:hover {
color: rgb(33,165,229);
}
#closeButton:hover {
color: rgb(200,49,61);
}
""")
self.setLayout(layout)
def mouseDoubleClickEvent(self, event):
self.window_maximum()
event.accept() # 接受事件,禁止傳到父控件
# 鼠標移動
def mouseMoveEvent(self, event):
if event.buttons() == Qt.LeftButton and self._mouse_pos:
self.parent().move(self.mapToGlobal(event.pos() - self._mouse_pos))
event.accept() # 接受事件,不傳遞到父控件
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self._mouse_pos = event.pos()
event.accept() # 接受事件,不傳遞到父控件
def mouseReleaseEvent(self, event):
self._mouse_pos = None
event.accept() # 接受事件,不傳遞到父控件
def window_minimum(self):
self.parent().showMinimized()
def window_maximum(self):
if self.maximum_button.text() == '1':
self.maximum_button.setText('2')
self.parent().showMaximized()
else:
self.maximum_button.setText('1')
self.parent().showNormal()
def window_close(self):
self.parent().close()
def set_window_title(self, title):
self.title_text.setText(title)
def set_window_icon(self, pix_map):
if isinstance(pix_map, QPixmap):
self.title_label.setPixmap(pix_map)
class CenterWidget(QStackedWidget):
def __init__(self, *args, **kwargs):
super(CenterWidget, self).__init__(*args, **kwargs)
self.setAutoFillBackground(True)
def clear(self):
for i in range(self.count()):
widget = self.widget(i)
self.removeWidget(widget)
if widget is not None:
widget.deleteLater()
del widget
class StatusBar(QStatusBar):
def __init__(self, *args, **kwargs):
super(StatusBar, self).__init__(*args, **kwargs)
self.setAutoFillBackground(True)
class FrameLessWindow(QWidget):
"""主窗口"""
MARGIN = 5
Left, Top, Right, Bottom, LeftTop, RightTop, LeftBottom, RightBottom = range(8)
def __init__(self, *args, **kwargs):
super(FrameLessWindow, self).__init__(*args, **kwargs)
self.resize(1200, 680)
self.setWindowFlags(Qt.FramelessWindowHint) # 無邊框
self.setAttribute(Qt.WA_TranslucentBackground, True)
self.setMouseTracking(True) # 鼠標追蹤,mouseMoveEvent纔有效果
self._direction = None # 此時鼠標的方向
self._pressed = False # 鼠標是否按下
self._mouse_pos = None # 記錄鼠標位置
layout = QVBoxLayout(self)
layout.setSpacing(0)
layout.setContentsMargins(QMargins(self.MARGIN, self.MARGIN, self.MARGIN, self.MARGIN))
self.title = TitleBar(self)
self.title.installEventFilter(self) # 安裝事件過濾,進入控件還原方向和鼠標狀態
layout.addWidget(self.title, alignment=Qt.AlignTop)
self.center_widget = CenterWidget(self)
self.center_widget.installEventFilter(self)
layout.addWidget(self.center_widget)
self.status_bar = StatusBar(self)
self.status_bar.installEventFilter(self)
layout.addWidget(self.status_bar, alignment=Qt.AlignBottom)
self.setLayout(layout)
def eventFilter(self, obj, event):
if isinstance(event, QEnterEvent):
self.setCursor(Qt.ArrowCursor)
self._direction = None # 去除方向
self._pressed = None # 去除按下標記
return super(FrameLessWindow, self).eventFilter(obj, event)
def mousePressEvent(self, event):
super(FrameLessWindow, self).mousePressEvent(event)
if event.button() == Qt.LeftButton:
self._mouse_pos = event.pos()
self._pressed = True
def mouseReleaseEvent(self, event):
super(FrameLessWindow, self).mouseReleaseEvent(event)
self._pressed = False
self._direction = None
def mouseMoveEvent(self, event):
super(FrameLessWindow, self).mouseMoveEvent(event)
pos = event.pos()
pos_x, pos_y = pos.x(), pos.y()
wm, hm = self.width() - self.MARGIN, self.height() - self.MARGIN
# print(wm, hm)
# 窗口最大無需事件
if self.isMaximized() or self.isFullScreen():
self._direction = None
self.setCursor(Qt.ArrowCursor)
return
if event.buttons() == Qt.LeftButton and self._pressed:
self.resize_window(pos)
if pos_x <= self.MARGIN and pos_y <= self.MARGIN:
# 左上角
self._direction = self.LeftTop
self.setCursor(Qt.SizeFDiagCursor)
elif wm <= pos_x <= self.width() and hm <= pos_y <= self.height():
# 右下角
self._direction = self.RightBottom
self.setCursor(Qt.SizeFDiagCursor)
elif wm <= pos_x and pos_y <= self.MARGIN:
# 右上角
self._direction = self.RightTop
self.setCursor(Qt.SizeBDiagCursor)
elif pos_x <= self.MARGIN and hm <= pos_y:
# 左下角
self._direction = self.LeftBottom
self.setCursor(Qt.SizeBDiagCursor)
elif 0 <= pos_x <= self.MARGIN <= pos_y <= hm:
# 左邊
self._direction = self.Left
self.setCursor(Qt.SizeHorCursor)
elif wm <= pos_x <= self.width() and self.MARGIN <= pos_y <= hm:
# 右邊
self._direction = self.Right
self.setCursor(Qt.SizeHorCursor)
elif wm >= pos_x >= self.MARGIN >= pos_y >= 0:
# 上面
self._direction = self.Top
self.setCursor(Qt.SizeVerCursor)
elif self.MARGIN <= pos_x <= wm and hm <= pos_y <= self.height():
# 下面
self._direction = self.Bottom
self.setCursor(Qt.SizeVerCursor)
else:
pass
def showMaximized(self):
super(FrameLessWindow, self).showMaximized()
self.layout().setContentsMargins(0, 0, 0, 0)
def showNormal(self):
super(FrameLessWindow, self).showNormal()
self.layout().setContentsMargins(self.MARGIN, self.MARGIN, self.MARGIN, self.MARGIN)
def paintEvent(self, event):
super(FrameLessWindow, self).paintEvent(event)
painter = QPainter(self)
painter.setPen(QPen(QColor(200, 200, 200, 1), 2 * self.MARGIN))
painter.drawRect(self.rect())
def setWindowTitle(self, title):
super(FrameLessWindow, self).setWindowTitle(title)
self.title.set_window_title(title)
def setWindowIcon(self, icon):
if isinstance(icon, QIcon):
super(FrameLessWindow, self).setWindowIcon(icon)
self.title.set_window_icon(icon.pixmap(20, 20))
def resize_window(self, pos):
if self._direction is None:
return
mpos = pos - self._mouse_pos
xPos, yPos = mpos.x(), mpos.y()
geometry = self.geometry()
x, y, w, h = geometry.x(), geometry.y(), geometry.width(), geometry.height()
if self._direction == self.LeftTop: # 左上角
if w - xPos > self.minimumWidth():
x += xPos
w -= xPos
if h - yPos > self.minimumHeight():
y += yPos
h -= yPos
elif self._direction == self.RightBottom: # 右下角
if w + xPos > self.minimumWidth():
w += xPos
self._mouse_pos = pos
if h + yPos > self.minimumHeight():
h += yPos
self._mouse_pos = pos
elif self._direction == self.RightTop: # 右上角
if h - yPos > self.minimumHeight():
y += yPos
h -= yPos
if w + xPos > self.minimumWidth():
w += xPos
self._mouse_pos.setX(pos.x())
elif self._direction == self.LeftBottom: # 左下角
if w - xPos > self.minimumWidth():
x += xPos
w -= xPos
if h + yPos > self.minimumHeight():
h += yPos
self._mouse_pos.setY(pos.y())
elif self._direction == self.Left: # 左邊
if w - xPos > self.minimumWidth():
x += xPos
w -= xPos
else:
return
elif self._direction == self.Right: # 右邊
if w + xPos > self.minimumWidth():
w += xPos
self._mouse_pos = pos
else:
return
elif self._direction == self.Top: # 上面
if h - yPos > self.minimumHeight():
y += yPos
h -= yPos
else:
return
elif self._direction == self.Bottom: # 下面
if h + yPos > self.minimumHeight():
h += yPos
self._mouse_pos = pos
else:
return
self.setGeometry(x, y, w, h)
def remove_status_bar(self):
self.status_bar.hide()
def set_center_widget(self, widget):
self.center_widget.clear()
self.center_widget.addWidget(widget)