1. 官方文档
QSplitter — PyQt Documentation v6.6.0
2. 效果展示
- 可拖拽改变宽度比例
- 点击按钮快速收起或展开侧边栏
点击按钮,侧边栏收起,同时按钮图标变为向左箭头 (对应展开功能),再次点击按钮,侧边栏展开,同时按钮图标改变为向右箭头 (对应收起功能)。
3. 代码细节说明
3.1. 控件拉伸布局:QSplitter
3.1.1 Qt Designer中如何添加QSplitter
在 Qt Designer 的控件工具箱中没有 QSplitter 这个控件
添加 QSplitter 步骤
1、同时选中需要被添加到 QSplitter 分割器中的控件 (需要同时选中)
2、鼠标右键 --> 布局 --> 使用分裂器水平/垂直布局
也可以使用工具栏中的 <使用分裂器水平/垂直布局> 工具
3.1.2 QSplitter 常用方法
方法 | 描述 |
setSizes(Iterable[int]) | 将子部件各自的大小设置为列表中给出的值。对于水平分割器,则这些值表示每个部件的宽度(以像素为单位),从左到右;对于垂直分割器,表示每个部件的高度,从上到下。 |
widget(int) → QWidget | 返回拆分器布局中给定索引处的部件。 |
setChildrenCollapsible(bool) setCollapsible(int, bool) | 默认情况下,子部件是可折叠的 (拖拽到边界部件将不显示,如同被折叠起来),这意味着用户可以将它们的大小调整为 0,即使它们具有非零的 minimumSize() 或 minimumSizeHint()。此行为可以修改:可通过函数 setChildrenCollapsible(bool) 将某个小部件改为不可折叠,也可通过函数 setchildrenCollapsible() 对拆分器中的所有小部件统一修改为不可折叠。 |
setHandleWidth(int) | 分隔栏是 QSplitter 中用于分割子部件的一个可拖拽的条状控件,它位于两个子窗口之间。setHandleWidth 方法可以设置分隔栏的宽度。 |
setOpaqueResize(opaque: bool = True) | 默认情况下,QSplitter 会动态调整其子元素的大小。如果希望 QSplitter 仅在调整大小的操作结束时才调整子元素的大小,请调用setOpaqueResize(False)。这个方法的主要作用是提高分割器拖动的性能,因为分割器不需要实时计算。 |
setOrientation(Orientation) | 可使用 setOrientation(Orientation) 方法改变 QSplitter 的分割条的方向。如果参数为 Qt.Orientation.Horizontal,则分割条将水平分割窗口,左右两个区域可以分别调整大小;如果参数设置为 Qt.Orientation.Vertical,则分割条将垂直分割窗口,即上下两个区域可以分别调整大小。 |
(默认) self.splitter.setChildrenCollapsible(True) 效果:
尽可能的拖拽到最右侧,右边的部件将不显示。
self.splitter.setChildrenCollapsible(False) 效果:
即便拖拽到最右侧,右边的部件也能够显示一块内容。
self.splitter.setHandleWidth(20) 执行结果:
self.splitter.setOrientation(Qt.Orientation.Vertical) 执行结果:
3.2. 私有布局
私有布局(Private Layout)是一种基于 QWidget 的布局方式,可以在 QWidget 中添加子控件,并自动排列它们。与其他布局方式不同的是,私有布局是在 QWidget 的私有部分中实现的,因此只能在 QWidget 中使用,而不能在其他的控件中使用。 私有布局的优点是可以在 QWidget 中自由地布置和排列控件,而不必受到其他布局方式的限制。此外,私有布局还可以很好地处理控件之间的间距和边框等问题,能够实现更加灵活和精细的布局效果。私有布局的缺点是需要手动管理控件的位置和大小,需要编写更多的代码来实现复杂的布局效果。
3.3 按钮控件 CSS
对于按钮控件,可使用 border:none; 不显示按钮的边框,同时可以使用 QPushButton:pressed,设置 padding-top 和 padding-bottom 实现点击按钮时按钮小幅跳动的效果,从而使点击操作更明显。
4. 代码分享
版本:PyQt6 -- 6.6.1
4.1 下载左右箭头图标 .png 文件
可参考:
PyQt:辅助网站收集 (图标、颜色)_python pyqt 漂亮的按钮图标哪里下载-CSDN博客
文件放置于 ico_files 文件夹:
│ sidebar.py
│ sidebar_main.py
│
└─ico_files
arrow_left.png
arrow_right.png
4.2 Ui 界面 <sidebar.py>
# Form implementation generated from reading ui file 'sidebar.ui'
#
# Created by: PyQt6 UI code generator 6.6.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt6 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(502, 317)
self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.splitter = QtWidgets.QSplitter(parent=self.centralwidget)
self.splitter.setOrientation(QtCore.Qt.Orientation.Horizontal)
self.splitter.setHandleWidth(0)
self.splitter.setChildrenCollapsible(True)
self.splitter.setObjectName("splitter")
self.frame_2 = QtWidgets.QFrame(parent=self.splitter)
self.frame_2.setStyleSheet("")
self.frame_2.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
self.frame_2.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
self.frame_2.setObjectName("frame_2")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame_2)
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_2.setSpacing(0)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.tabWidget = QtWidgets.QTabWidget(parent=self.frame_2)
self.tabWidget.setStyleSheet("background-color: rgb(240, 240, 240);")
self.tabWidget.setObjectName("tabWidget")
self.tab = QtWidgets.QWidget()
self.tab.setObjectName("tab")
self.tabWidget.addTab(self.tab, "")
self.tab_2 = QtWidgets.QWidget()
self.tab_2.setObjectName("tab_2")
self.tabWidget.addTab(self.tab_2, "")
self.verticalLayout_2.addWidget(self.tabWidget)
self.frame = QtWidgets.QFrame(parent=self.splitter)
self.frame.setStyleSheet("background-color: rgb(255, 170, 0);")
self.frame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
self.frame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
self.frame.setObjectName("frame")
self.verticalLayout = QtWidgets.QVBoxLayout(self.frame)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setSpacing(0)
self.verticalLayout.setObjectName("verticalLayout")
self.groupBox = QtWidgets.QGroupBox(parent=self.frame)
self.groupBox.setObjectName("groupBox")
self.verticalLayout.addWidget(self.groupBox)
self.horizontalLayout.addWidget(self.splitter)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 502, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.tabWidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Tab 1"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Tab 2"))
self.groupBox.setTitle(_translate("MainWindow", "GroupBox"))
4.3 主程序 <sidebar_main.py>
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QIcon, QPixmap
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QHBoxLayout
import sys
import sidebar
import os
class Ui_overlap(sidebar.Ui_MainWindow, QMainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.splitter.setSizes([self.width() // 10 * 7, self.width() // 10 * 3])
self.splitter.setHandleWidth(0)
self.pushButton_expend = QPushButton(self.splitter.widget(0))
self._prepare_ptn_expend()
self.show()
def _prepare_ptn_expend(self):
# 设置大小、图标、功能
self.pushButton_expend.resize(30, 30)
self.pushButton_expend.setIcon(QIcon(QPixmap(
os.path.abspath(os.path.join(os.path.dirname(__file__), 'ico_files', 'arrow_right.png')))))
self.pushButton_expend.clicked.connect(self.adjust_sidebar)
# 设置位置
btne_hlayout = QHBoxLayout(self.tabWidget)
btne_hlayout.addWidget(self.pushButton_expend, 0, Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignTop)
btne_hlayout.setSpacing(0)
btne_hlayout.setContentsMargins(0, 0, 0, 0)
# 设置按钮样式
self.splitter.widget(0).setStyleSheet(
"QPushButton{\n"
" border-top-left-radius:3px;\n"
" border-bottom-left-radius:3px;\n"
# " background-color: rgb(170, 255, 255);\n"
"}\n"
"QPushButton:pressed{\n"
" padding-left:5px;\n"
" padding-top:5px;\n"
"\n"
"}")
def adjust_sidebar(self):
path1 = os.path.abspath(os.path.join(os.path.dirname(__file__), 'ico_files', 'arrow_right.png'))
path2 = os.path.abspath(os.path.join(os.path.dirname(__file__), 'ico_files', 'arrow_left.png'))
icon_to_right = QIcon(QPixmap(path1))
icon_to_left = QIcon(QPixmap(path2))
if self.frame.isHidden():
self.frame.show()
self.pushButton_expend.setIcon(icon_to_right)
else:
self.frame.hide()
self.pushButton_expend.setIcon(icon_to_left)
if __name__ == '__main__':
app = QApplication(sys.argv)
my_ui = Ui_overlap()
sys.exit(app.exec())
参考链接
Qt中控件叠加悬浮显示的两种实现方法_qt 控件堆叠悬浮-CSDN博客
106.PyQt5_QSplitter_可拉伸区域分隔器控件_pyqt5 splitter-CSDN博客