注:系列文章,前后关联,请结合完整代码参考本系列文章;现已开源在 GitHub PyOc
开发环境
- 操作系统:Windows 10 x64 18363
- Python:3.8.2 64-bit
- Qt:PyQt5_5.14.1
- IDE:Geany 1.34.1
纯 console 界面的 python 脚本实在是不太美观,且对用户不友好。所有就有开发 GUI 的需求,幸好 python 有内置的轻量化的 tkinter,对强大的 Qt 也有紧密支持的 PyQt
参考链接
展望
以 Office Tool Plus 为蓝本,实现 GUI 样式:
实现
- 去除系统标题栏
- 实现最大化、最小化及关闭窗口功能
- 【可选】实现窗口移动
去除标题栏
setWindowFlags(Qt.FramelessWindowHint)
设计 UI
这里我们使用 QWidget 加 QHBoxLayout 的布局,使用两个 QLabel(Icon、标题),三个 QPushButton(最小化、最大化、关闭)
【可选】换肤按钮、菜单按钮
这里使用 QT Designer 只是因为演示方便,实际可纯代码完成。
class OTitleBar(QWidget):
"""自定义标题栏
实现了最大化与最小化,需配合 OWindow 使用"""
def __init__(self, parent):
super().__init__()
# 设置尺寸策略
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.setFixedHeight(36)
# ~ self.setAttribute(Qt.WA_TransparentForMouseEvents) # 鼠标点击穿透
self._parent = parent # 父(主)窗口
self._menu_button = None # 菜单按钮(QWidget)
self._skin_button = None # 换肤按钮(QWidget)
self._title_icon = None # 图标(QLabel)
self.title_label = None # 标题(QLabel)
self._min_button = None # 最小化按钮(QWidget)
self._max_button = None # 最大化按钮(QWidget)
self._close_button = None # 关闭按钮(QWidget)
self._central_layout = None # 主布局(水平)
self._menu_icon = None # 菜单图标
self._skin_icon = None # 换肤图标
self._min_icon = None # 最小化按钮图标
self._max_icon = None # 最大化按钮图标
self._restore_icon = None # 最大化按钮恢复图标
self._close_icon = None # 关闭按钮图标
self._series = 'black' # 图标样式(默认黑色)
# 初始化标题栏属性
self.init_titlebar()
# 设置最窄宽度
self.setMinimumWidth(self._title_icon.width() * 13)
# 去除布局及控件间的间隔及边距
set_all_gap(self._central_layout, left=8)
self.setMouseTracking(True)
self._title_icon.setMouseTracking(True)
self.title_label.setMouseTracking(True)
def init_titlebar(self):
"""初始化标题栏属性"""
# 添加标题控件
self._title_icon = QLabel()
self.title_label = QLabel()
self._min_button = QPushButton()
self._max_button = QPushButton()
self._close_button = QPushButton()
self._title_icon.setObjectName('OTitleIcon')
self.title_label.setObjectName('OTitleLabel')
self._min_button.setObjectName('OMinButton')
self._max_button.setObjectName('OMaxButton')
self._close_button.setObjectName('OCloseButton')
# 设置 icon
set_icon(self._title_icon, ':/res/images/one_ccs.ico')
self._load_icon()
# 设置 icon 宽度
self._title_icon.setFixedHeight(24)
self._title_icon.setFixedWidth(24)
# 设置标题控件策略
expanding = QSizePolicy.Expanding
self.title_label.setSizePolicy(expanding, expanding)
# 设置窗口主布局,并放入主容器中
self._central_layout = QHBoxLayout(self)
# 把控件添加进标题栏布局
self._central_layout.addWidget(self._title_icon, alignment=Qt.AlignVCenter)
self._central_layout.addWidget(self.title_label, alignment=Qt.AlignVCenter)
self._central_layout.addWidget(self._min_button, alignment=Qt.AlignVCenter)
self._central_layout.addWidget(self._max_button, alignment=Qt.AlignVCenter)
self._central_layout.addWidget(self._close_button, alignment=Qt.AlignVCenter)
# 链接信号/槽
self._min_button.clicked.connect(self.min_button_click)
self._max_button.clicked.connect(self.max_button_click)
self._close_button.clicked.connect(self.close_button_click)
def set_menu_icon(self, icon):
"""设置菜单按钮图标"""
if not isinstance(icon, QPixmap):
icon = QPixmap(icon)
icon = icon.scaled(width, height, Qt.KeepAspectRatio, Qt.SmoothTransformation)
self._menu_icon = QIcon(QPixmap(icon))
self._menu_button.setPixmap()
self._menu_button.setIcon(self._menu_icon)
def set_skin_icon(self, icon):
"""设置最换肤按钮图标"""
self._skin_icon = QIcon(QPixmap(icon))
self._skin_button.setIcon(self._skin_icon)
def set_min_icon(self, icon):
"""设置最小化按钮图标"""
self._min_icon = QIcon(QPixmap(icon))
self._min_button.setIcon(self._min_icon)
def set_max_icon(self, icon):
"""设置最大化按钮图标"""
self._max_icon = QIcon(QPixmap(icon))
self._max_button.setIcon(self._max_icon)
def set_restore_icon(self, icon):
"""设置恢复按钮图标"""
self._restore_icon = QIcon(QPixmap(icon))
def set_close_icon(self, icon):
"""设置关闭按钮图标"""
self._close_icon = QIcon(QPixmap(icon))
self._close_button.setIcon(self._close_icon)
def set_icon_series(self, series):
"""设置标题图标系列(黑/白)"""
if series == 'black' or series == 'white':
self._series = series
self._load_icon()
def add_menu_button(self):
"""添加菜单按钮"""
self._menu_button = QPushButton()
self.setObjectName('OMenuButton')
self._menu_button.installEventFilter(self._parent)
if self._series == 'black':
self.set_menu_icon(':/res/images/title_menu.ico')
elif self._series == 'white':
self.set_menu_icon(':/res/images/title_menu_w.ico')
self._central_layout.addWidget(self._menu_button)
# 重新放置主按钮
self._central_layout.addWidget(self._min_button)
self._central_layout.addWidget(self._max_button)
self._central_layout.addWidget(self._close_button)
return self._menu_button
def add_skin_button(self):
"""添加换肤按钮"""
self._skin_button = QPushButton()
self._skin_button.setObjectName('OSkinButton')
self._skin_button.installEventFilter(self._parent)
if self._series == 'black':
self.set_skin_icon(':/res/images/title_skin.ico')
elif self._series == 'white':
self.set_skin_icon(':/res/images/title_skin_w.ico')
# 设置图标大小
# ~ self._skin_button.setIconSize(QSize(22, 22))
self._central_layout.addWidget(self._skin_button)
# 重新放置主按钮
self._central_layout.addWidget(self._min_button)
self._central_layout.addWidget(self._max_button)
self._central_layout.addWidget(self._close_button)
return self._skin_button
def _load_icon(self):
"""设置图标"""
res = ':/res/images/'
if self._series == 'black':
if self._menu_button:
self.set_menu_icon(res + 'title_menu.ico')
if self._skin_button:
self.set_skin_icon(res + 'title_skin.ico')
self.set_min_icon(res + 'title_min.ico')
self.set_max_icon(res + 'title_max.ico')
self.set_restore_icon(res + 'title_restore.ico')
self.set_close_icon(res + 'title_close.ico')
elif self._series == 'white':
if self._menu_button:
self.set_menu_icon(res + 'title_menu_w.ico')
if self._skin_button:
self.set_skin_icon(res + 'title_skin_w.ico')
self.set_min_icon(res + 'title_min_w.ico')
self.set_max_icon(res + 'title_max_w.ico')
self.set_restore_icon(res + 'title_restore_w.ico')
self.set_close_icon(res + 'title_close_w.ico')
def _check_parent(self):
"""判断是否有父窗口"""
if isinstance(self._parent, QWidget) or (
isinstance(self._parent, QMainWindow)):
return True
else:
return False
# 槽函数
def min_button_click(self):
"""最小化"""
if self._check_parent():
self._parent.showMinimized()
else:
self.showMinimized()
def max_button_click(self):
"""最大化(恢复)"""
if self._check_parent():
if self._parent.isMaximized():
self._parent.showNormal()
self._max_button.setIcon(self._max_icon)
else:
self._parent.showMaximized()
self._max_button.setIcon(self._restore_icon)
def close_button_click(self):
"""关闭"""
if self._check_parent():
self._parent.close()
else:
self.close()
def set_icon(obj, icon, width=20, height=20):
"""为 QLabel 或 QPushButton 设置图像"""
if not isinstance(obj, QLabel) and not isinstance(obj, QPushButton):
logging.warning('set_icon(obj: Union[QLabel, QPushButton], icon,' +
' width=20, height=20): argument 1 is unexpected' +
' type {}'.format(type(obj)))
return None
KAR = Qt.KeepAspectRatio
STFM = Qt.SmoothTransformation
if isinstance(obj, QLabel):
if isinstance(icon, str):
icon = QPixmap(icon).scaled(width, height, KAR, STFM)
obj.setPixmap(icon)
else:
if isinstance(icon, str):
icon = QIcon(QPixmap(icon))
if isinstance(icon, QPixmap):
icon = QIcon(icon)
obj.setIcon(icon)
obj.setIconSize(QSize(width, height))
return icon
本文为 one-ccs 原创文章,引用必须注明出处!
https://blog.csdn.net/qq_43155814/article/details/104625188