GitHub 项目地址:pyqt5-demo
主流软件如网易云音乐、腾讯电脑管家,都有侧边栏。侧边栏能有效扩展应用空间,使软件功能的层次更加分明。
但PyQt5并没有一个专门的方法实现侧边栏。要实现侧边栏,主要有两种技术路线。一是从标签部件(QTabWidget)改造而来,二是用网格布局(QGridLayout)画出来。本文采用第一种技术路线,制作一个简单的侧边栏应用。
Note: 两种技术路线制作的侧边栏有细微的差别。如果采用第二种技术路线(QGridLayout),侧边栏和内容页之间没有明显的分界。所以需要额外地在侧边栏和内容页之间加一条线,来突出两者属于不同的组件。而第一种技术路线(QTabWidget)不需要,创建的侧边栏和内容页之间的区别明显。
下图是一个用第二种技术路线(QGridLayout)制作的应用,可以看到侧边栏和内容页中间画了一条线,以突出两个组件的分界。
步骤分析
在开始写代码之前,我们先分析一下制作侧边栏需要几个步骤。
- 添加部件
- 为部件设置布局
- 将标签部件改造成侧边栏的内容页
- 为侧边栏按钮编写函数,实现内容页之间的跳转
添加部件并设置布局
此段不详述。关于部件和布局的基础知识,在上一篇中有介绍,详见PyQt5 布局浅析。
下面这段代码添加了部件且设置了布局。
from PyQt5.QtWidgets import *
import sys
class Window(QMainWindow):
def __init__(self):
super().__init__()
# set the title of main window
self.setWindowTitle('Sidebar layout - www.luochang.ink')
# set the size of window
self.Width = 800
self.height = int(0.618 * self.Width)
self.resize(self.Width, self.height)
# add all widgets
self.btn_1 = QPushButton('1', self)
self.btn_2 = QPushButton('2', self)
self.btn_3 = QPushButton('3', self)
self.btn_4 = QPushButton('4', self)
self.initUI()
def initUI(self):
left_layout = QVBoxLayout()
left_layout.addWidget(self.btn_1)
left_layout.addWidget(self.btn_2)
left_layout.addWidget(self.btn_3)
left_layout.addWidget(self.btn_4)
left_layout.addStretch(5)
left_layout.setSpacing(20)
left_widget = QWidget()
left_widget.setLayout(left_layout)
self.right_widget = QTabWidget()
self.right_widget.tabBar().setObjectName("mainTab")
main_layout = QHBoxLayout()
main_layout.addWidget(left_widget)
main_layout.addWidget(self.right_widget)
main_layout.setStretch(0, 40)
main_layout.setStretch(1, 200)
main_widget = QWidget()
main_widget.setLayout(main_layout)
self.setCentralWidget(main_widget)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Window()
ex.show()
sys.exit(app.exec_())
上述代码,在__init__
内添加了部件,在initUI
里设置了部件的布局。但因为我们并没有给标签部件(QTabWidget)添加页面,所以现在标签页面正好是没有标签的。后续加上内容页之后,标签(Tab)又会出现,到那是我们要用技巧再把标签隐去。
布局都好了,但这时的标签部件还没有内容页。没有内容页的标签部件是没有灵魂的,所以下面,我们给标签部件添加内容页。
为标签部件添加内容页
添加标签的三个步骤:
- 定义内容页函数
- 调用内容页函数以生成内容页
- 将内容页依次加入标签部件
我们首先编写定义内容页的函数。
下面这段代码定义了内容页。
def ui1(self):
main_layout = QVBoxLayout()
main_layout.addWidget(QLabel('page 1'))
main_layout.addStretch(5)
main = QWidget()
main.setLayout(main_layout)
return main
def ui2(self):
main_layout = QVBoxLayout()
main_layout.addWidget(QLabel('page 2'))
main_layout.addStretch(5)
main = QWidget()
main.setLayout(main_layout)
return main
def ui3(self):
main_layout = QVBoxLayout()
main_layout.addWidget(QLabel('page 3'))
main_layout.addStretch(5)
main = QWidget()
main.setLayout(main_layout)
return main
def ui4(self):
main_layout = QVBoxLayout()
main_layout.addWidget(QLabel('page 4'))
main_layout.addStretch(5)
main = QWidget()
main.setLayout(main_layout)
return main
下一步,在__init__
里调用上述函数以生成内容页。
下面这段代码生成了内容页。
# def __init__(self):
# super().__init__()
# add tabs
self.tab1 = self.ui1()
self.tab2 = self.ui2()
self.tab3 = self.ui3()
self.tab4 = self.ui4()
在initUI
函数里,将内容页加入标签部件。请注意下面这段代码添加的位置,它必须在语句main_layout.addWidget(self.right_widget)
之前,否则程序有可能出错。
下面这段代码将内容页加入标签部件。
# def initUI(self):
self.right_widget.addTab(self.tab1, '')
self.right_widget.addTab(self.tab2, '')
self.right_widget.addTab(self.tab3, '')
self.right_widget.addTab(self.tab4, '')
在上图中可以看见,标签部件的顶上冒出了小方头,在第二节中提到的标签(Tab)出现了。这时我们需要改造标签部件,隐去标签,并将原先点击标签触发的页面跳转功能,迁移到侧边栏按钮上。
改造标签部件
隐藏标签部件的标签,其实只需要一行。下面用QSS隐去标签。
下面这段代码隐藏了标签部件的标签并初始化显示页面。
# def initUI(self):
self.right_widget.setCurrentIndex(0)
self.right_widget.setStyleSheet('''QTabBar::tab{width: 0; \
height: 0; margin: 0; padding: 0; border: none;}''')
通过按钮实现页面跳转
页面跳转功能本质上是通过QTabWidget
的setCurrentIndex(int)
函数实现的,知道这一点就好办了。把按钮代表的页面的索引作为setCurrentIndex(int)
函数的输入,为每个按钮设定如下,即可实现页面跳转。
下面这段代码定义了按钮函数。
def button1(self):
self.right_widget.setCurrentIndex(0)
def button2(self):
self.right_widget.setCurrentIndex(1)
def button3(self):
self.right_widget.setCurrentIndex(2)
def button4(self):
self.right_widget.setCurrentIndex(3)
最后,在__init__
函数里,将按钮部件和函数绑定,一个简单的侧边栏就大功告成了!
下面这段代码为按钮部件绑定函数。
# def __init__(self):
# super().__init__()
self.btn_1.clicked.connect(self.button1)
self.btn_2.clicked.connect(self.button2)
self.btn_3.clicked.connect(self.button3)
self.btn_4.clicked.connect(self.button4)
Note1: 为了让代码结构清晰,易于理解,本例只有最简单的功能和布局。如果你对进一步完善侧边栏感兴趣,请参阅sidebar.py。
Note2: 此外,我在网络上搜了很久,没有搜到一篇关于制作侧边栏的文章。一番折腾后,我在GitHub找到了开源项目MusicBox,然后才从中学会了这个构建侧边栏的方法。这个项目不错,在里面我学到了很多PyQt5的使用方法。在此感谢作者,并默默替他安利一波。
附录:本教程完整代码
from PyQt5.QtWidgets import *
import sys
class Window(QMainWindow):
def __init__(self):
super().__init__()
# set the title of main window
self.setWindowTitle('Sidebar layout - www.luochang.ink')
# set the size of window
self.Width = 800
self.height = int(0.618 * self.Width)
self.resize(self.Width, self.height)
# add all widgets
self.btn_1 = QPushButton('1', self)
self.btn_2 = QPushButton('2', self)
self.btn_3 = QPushButton('3', self)
self.btn_4 = QPushButton('4', self)
self.btn_1.clicked.connect(self.button1)
self.btn_2.clicked.connect(self.button2)
self.btn_3.clicked.connect(self.button3)
self.btn_4.clicked.connect(self.button4)
# add tabs
self.tab1 = self.ui1()
self.tab2 = self.ui2()
self.tab3 = self.ui3()
self.tab4 = self.ui4()
self.initUI()
def initUI(self):
left_layout = QVBoxLayout()
left_layout.addWidget(self.btn_1)
left_layout.addWidget(self.btn_2)
left_layout.addWidget(self.btn_3)
left_layout.addWidget(self.btn_4)
left_layout.addStretch(5)
left_layout.setSpacing(20)
left_widget = QWidget()
left_widget.setLayout(left_layout)
self.right_widget = QTabWidget()
self.right_widget.tabBar().setObjectName("mainTab")
self.right_widget.addTab(self.tab1, '')
self.right_widget.addTab(self.tab2, '')
self.right_widget.addTab(self.tab3, '')
self.right_widget.addTab(self.tab4, '')
self.right_widget.setCurrentIndex(0)
self.right_widget.setStyleSheet('''QTabBar::tab{width: 0; \
height: 0; margin: 0; padding: 0; border: none;}''')
main_layout = QHBoxLayout()
main_layout.addWidget(left_widget)
main_layout.addWidget(self.right_widget)
main_layout.setStretch(0, 40)
main_layout.setStretch(1, 200)
main_widget = QWidget()
main_widget.setLayout(main_layout)
self.setCentralWidget(main_widget)
# -----------------
# buttons
def button1(self):
self.right_widget.setCurrentIndex(0)
def button2(self):
self.right_widget.setCurrentIndex(1)
def button3(self):
self.right_widget.setCurrentIndex(2)
def button4(self):
self.right_widget.setCurrentIndex(3)
# -----------------
# pages
def ui1(self):
main_layout = QVBoxLayout()
main_layout.addWidget(QLabel('page 1'))
main_layout.addStretch(5)
main = QWidget()
main.setLayout(main_layout)
return main
def ui2(self):
main_layout = QVBoxLayout()
main_layout.addWidget(QLabel('page 2'))
main_layout.addStretch(5)
main = QWidget()
main.setLayout(main_layout)
return main
def ui3(self):
main_layout = QVBoxLayout()
main_layout.addWidget(QLabel('page 3'))
main_layout.addStretch(5)
main = QWidget()
main.setLayout(main_layout)
return main
def ui4(self):
main_layout = QVBoxLayout()
main_layout.addWidget(QLabel('page 4'))
main_layout.addStretch(5)
main = QWidget()
main.setLayout(main_layout)
return main
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Window()
ex.show()
sys.exit(app.exec_())