《快速掌握PyQt5》第二十四章 装入更多控件

第二十四章  装入更多控件

24.1 拆分窗口QSplitter

24.2 标签页窗口QTabWidget

24.3 堆叠窗口QStackedWidget

24.4 停靠窗口QDockWidget

24.5 多文档界面QMdiArea

24.6 小结


本章将会介绍拆分窗口QSplitter、标签页窗口QTabWidget、堆叠窗口QStackedWidget、停靠窗口QDockWidget以及多文档界面QMidiArea。这些类可以帮助我们加入更多的控件,更多的功能,而且不会让界面看起来混乱。

 

24.1 拆分窗口QSplitter

下面我们将第二十章中的QListView、QTreeView和QTableView放到拆分窗口中,三个视图全都只用QDirModel来显示文件目录。请看下方代码:

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QSplitter, QListView, QTreeView, QTableView, QDirModel


class Demo(QSplitter):                                          # 1
    def __init__(self):
        super(Demo, self).__init__()
        self.dir_model = QDirModel(self)                        # 2

        self.list_view = QListView(self)                        # 3
        self.tree_view = QTreeView(self)
        self.table_view = QTableView(self)
        self.list_view.setModel(self.dir_model)
        self.tree_view.setModel(self.dir_model)
        self.table_view.setModel(self.dir_model)

        self.tree_view.doubleClicked.connect(self.show_func)    # 4

        # self.setOrientation(Qt.Vertical)                      # 5
        self.addWidget(self.list_view)
        self.addWidget(self.tree_view)
        self.insertWidget(0, self.table_view)
        self.setSizes([300, 200, 200])
        print(self.count())

    def show_func(self, index):
        self.list_view.setRootIndex(index)
        self.table_view.setRootIndex(index)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    demo = Demo()
    demo.show()
    sys.exit(app.exec_())

1. 继承QSplitter;

2. 实例化QDirModel模型;

3. 分别实例化QListView、QTreeView和QTableView,然后将这三个视图的模型设为dir_model;

4. 将QTreeView的doubleClicked信号和自定义的槽函数连接起来,QtAssistant中对该信号解释如下:

所以我们知道每当双击时,被双击项的索引就会保存在index中,而这个参数会传给槽函数:

def show_func(self, index):
    self.list_view.setRootIndex(index)
    self.table_view.setRootIndex(index)

在槽函数中我们调用setRootIndex()并传入index值,也就是说,每当我们双击QTreeView中的某项时,QListView和QTableView就会将该项的索引设为自身的根索引,并显示相应的目录结构;

5. 拆分窗口默认是水平的,可以调用setOrientation(Qt.Vertical)方法将其设为垂直方向。调用addWidget()方法将视图添加到拆分窗口中。insrtWidget(int, widget)可以将控件插入到相应的位置,第一个参数为要插入的索引位置,第二个参数为控件。setSizes(iterable)可以设置各个子控件的宽度(如果拆分窗口为垂直方向的话,则该方法会设置高度)。count()方法返回拆分窗口中的控件数量。

 

运行截图如下:

《快速掌握PyQt5》第二十四章 装入更多控件_第1张图片

拖动拆分线可以随意改变子控件大小:

《快速掌握PyQt5》第二十四章 装入更多控件_第2张图片

 

24.2 标签页窗口QTabWidget

我们来用QTabWidget来完成一个简单的信息填写程序:

import sys
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QWidget, QTabWidget, QLabel, QLineEdit, QDateEdit,\
                            QComboBox, QTextEdit, QGridLayout


class Demo(QTabWidget):
    def __init__(self):
        super(Demo, self).__init__()
        self.tab1 = QWidget()                   # 1
        self.tab2 = QWidget()
        self.tab3 = QTextEdit()

        self.tab1_init()                        # 2
        self.tab2_init()

        self.addTab(self.tab1, 'Basic Info')    # 3
        self.addTab(self.tab2, 'Contact Info')
        self.addTab(self.tab3, QIcon('info.ico'), 'More Info')

        self.currentChanged.connect(lambda: print(self.currentIndex()))    # 4

    def tab1_init(self):
        name_label = QLabel('Name:', self.tab1)
        gender_label = QLabel('Gender:', self.tab1)
        bd_label = QLabel('Birth Date:', self.tab1)

        name_line = QLineEdit(self.tab1)
        items = ['Please choose your gender', 'Female', 'Male']
        gender_combo = QComboBox(self.tab1)
        gender_combo.addItems(items)
        bd_dateedit = QDateEdit(self.tab1)

        g_layout = QGridLayout()
        g_layout.addWidget(name_label, 0, 0, 1, 1)
        g_layout.addWidget(name_line, 0, 1, 1, 1)
        g_layout.addWidget(gender_label, 2, 0, 1, 1)
        g_layout.addWidget(gender_combo, 2, 1, 1, 1)
        g_layout.addWidget(bd_label, 3, 0, 1, 1)
        g_layout.addWidget(bd_dateedit, 3, 1, 1, 1)

        self.tab1.setLayout(g_layout)

    def tab2_init(self):
        tel_label = QLabel('Tel:', self.tab2)
        mobile_label = QLabel('Mobile:', self.tab2)
        add_label = QLabel('Address:', self.tab2)

        tel_line = QLineEdit(self.tab2)
        mobile_line = QLineEdit(self.tab2)
        add_line = QLineEdit(self.tab2)

        g_layout = QGridLayout()
        g_layout.addWidget(tel_label, 0, 0, 1, 1)
        g_layout.addWidget(tel_line, 0, 1, 1, 1)
        g_layout.addWidget(mobile_label, 1, 0, 1, 1)
        g_layout.addWidget(mobile_line, 1, 1, 1, 1)
        g_layout.addWidget(add_label, 2, 0, 1, 1)
        g_layout.addWidget(add_line, 2, 1, 1, 1)

        self.tab2.setLayout(g_layout)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    demo = Demo()
    demo.show()
    sys.exit(app.exec_())

1. 实例化两个QWidget窗口和一个QTextEdit控件,之后我们会在tab1_init()中往tab1窗口上添加控件并完成布局,在tab2_int()中往tab2窗口上添加控件并完成布局;

2. 在tab1_init()函数中,我们实例化三个QLabel控件分别显示‘Name:’,‘Gender:’和‘Birth Date:’文本。相应的,我们实例化力了一个QLineEdit控件用于输入姓名,一个QComboBox控件用于选择性别,还有一个QDateEdit控件用于选择出生日期。之后用网格布局管理器QGridLayout完成布局。而在tab2_init()函数中我们就实例化了三个QLabel控件和三个QLineEdit控件。用户可以在此输入电话,手机和地址信息;

3. 通过调用addTab(widget, str)方法就可以将控件添加到QTabWidget中并设置标签页的名字,当然我们也可以调用addTab(widget, QIcon, str)同时设置标签页的图标:self.addTab(self.tab3, QIcon('info.ico'), 'More Info')。除了addTab()方法,当然还有insertTab()方法(add和insert通常一起存在);

4. 每当用户点击不同标签页时,都会触发currentChanged信号,我们在这里进行信号和槽的连接,每当触发信号时,打印当前标签页的索引,而索引可通过currentIndex()方法获取。

 

图片下载地址:https://www.easyicon.net/download/ico/26331/64/

运行截图如下:

《快速掌握PyQt5》第二十四章 装入更多控件_第3张图片

《快速掌握PyQt5》第二十四章 装入更多控件_第4张图片

《快速掌握PyQt5》第二十四章 装入更多控件_第5张图片

 

24.3 堆叠窗口QStackedWidget

QStackedWidget用法跟QTabWdidget用法思想相似,只是界面样式不同。我们经常将QStackedWidget和QListWidget或者QListView搭配使用,请看示例:

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QStackedWidget, QLabel, QLineEdit, QDateEdit,\
                            QComboBox, QTextEdit, QListWidget, QGridLayout, QHBoxLayout


class Demo(QWidget):                                    # 1
    def __init__(self):
        super(Demo, self).__init__()
        self.stack1 = QWidget()
        self.stack2 = QWidget()
        self.stack3 = QTextEdit()

        self.stack1_init()
        self.stack2_init()

        self.stacked_widget = QStackedWidget(self)      # 2
        self.stacked_widget.addWidget(self.stack1)
        self.stacked_widget.addWidget(self.stack2)
        self.stacked_widget.addWidget(self.stack3)
        self.stacked_widget.currentChanged.connect(lambda: print(self.stacked_widget.currentIndex()))

        self.list_widget = QListWidget(self)            # 3
        self.list_widget.addItem('Basic Info')
        self.list_widget.addItem('Contact Info')
        self.list_widget.addItem('More Info')
        self.list_widget.clicked.connect(self.change_func)

        self.h_layout = QHBoxLayout()
        self.h_layout.addWidget(self.list_widget)
        self.h_layout.addWidget(self.stacked_widget)

        self.setLayout(self.h_layout)

    def stack1_init(self):
        name_label = QLabel('Name:', self.stack1)
        gender_label = QLabel('Gender:', self.stack1)
        bd_label = QLabel('Birth Date:', self.stack1)

        name_line = QLineEdit(self.stack1)
        items = ['Please choose your gender', 'Female', 'Male']
        gender_combo = QComboBox(self.stack1)
        gender_combo.addItems(items)
        bd_dateedit = QDateEdit(self.stack1)

        g_layout = QGridLayout()
        g_layout.addWidget(name_label, 0, 0, 1, 1)
        g_layout.addWidget(name_line, 0, 1, 1, 1)
        g_layout.addWidget(gender_label, 2, 0, 1, 1)
        g_layout.addWidget(gender_combo, 2, 1, 1, 1)
        g_layout.addWidget(bd_label, 3, 0, 1, 1)
        g_layout.addWidget(bd_dateedit, 3, 1, 1, 1)

        self.stack1.setLayout(g_layout)

    def stack2_init(self):
        tel_label = QLabel('Tel:', self.stack2)
        mobile_label = QLabel('Mobile:', self.stack2)
        add_label = QLabel('Address:', self.stack2)

        tel_line = QLineEdit(self.stack2)
        mobile_line = QLineEdit(self.stack2)
        add_line = QLineEdit(self.stack2)

        g_layout = QGridLayout()
        g_layout.addWidget(tel_label, 0, 0, 1, 1)
        g_layout.addWidget(tel_line, 0, 1, 1, 1)
        g_layout.addWidget(mobile_label, 1, 0, 1, 1)
        g_layout.addWidget(mobile_line, 1, 1, 1, 1)
        g_layout.addWidget(add_label, 2, 0, 1, 1)
        g_layout.addWidget(add_line, 2, 1, 1, 1)

        self.stack2.setLayout(g_layout)

    def change_func(self):
        self.stacked_widget.setCurrentIndex(self.list_widget.currentIndex().row())


if __name__ == '__main__':
    app = QApplication(sys.argv)
    demo = Demo()
    demo.show()
    sys.exit(app.exec_())

1. 这里我们继承QWidget,将QStackedWidget和QListWidget放在该窗口中;

2. 实例化一个QStackedWidget窗口,将我们已经在stack1_init()和stack2_init()函数中设置好的窗口以及QTextEdit控件添加进去,并且进行信号和槽的连接,每当堆叠层发生变化时,打印相应的索引;

3. 实例一个QListWidget列表控件,添加三个子项'Basic Info',‘Contact Info’和‘More Info’,并且将clicked信号和槽函数连接起来:

def change_func(self):
    self.stacked_widget.setCurrentIndex(self.list_widget.currentIndex().row())

每当用户点击列表控件中不同行时,QStackedWidget调用setCurrentIndex(int)方法传入索引将显示界面设为相应的堆叠层;

 

运行截图如下:

《快速掌握PyQt5》第二十四章 装入更多控件_第6张图片

《快速掌握PyQt5》第二十四章 装入更多控件_第7张图片

《快速掌握PyQt5》第二十四章 装入更多控件_第8张图片

 

24.4 停靠窗口QDockWidget

看过第二十三章的小伙伴应该知道了QDockWidget是要和QMainWindow一起搭配使用的。我们在这里在此放上这张图:

《快速掌握PyQt5》第二十四章 装入更多控件_第9张图片

可以看出浅蓝色部分为停靠窗口可以停靠的区域,不过我们得调用QDockWidget的setAllowedAreas()来可停靠的位置,可以传入的参数有:

参数

描述

Qt.LeftDockWidgetArea

左边停靠区域

Qt.RightDockWidgetArea

右边停靠区域

Qt.TopDockWidgetArea

顶部停靠区域

Qt.BottomDockWidgetArea

底部停靠区域

Qt.AllDockWidgetAreas

全部区域

Qt.NoDockWidgetArea

不可停靠(将不显示Widget)

除了设置停靠窗口的停靠位置,我们也还可以调用setFeatures()方法来设置停靠窗口的功能属性,可传入的参数有:

参数

描述

QDockWidget.DockWidgetClosable

0x01

可关闭停靠窗口。在一些系统上,当停靠窗口浮动时总会有一个关闭按钮(比如MacOS 10.5)。 

QDockWidget.DockWidgetMovable

0x02

停靠窗口可在停靠区域中进行移动。

QDockWidget.DockWidgetFloatable

0x04

停靠窗口可与主窗口分离,以一种浮动的独立窗口显示

QDockWidget.DockWidgetVerticalTitleBar

0x08

在停靠窗口中的左侧显示一个标签栏。这样做可以增加主窗口左侧垂直空间。

QDockWidget.AllDockWidgetFeatures

DockWidgetClosable|DockWidgetMovable|DockWidgetFloatable

前三种的全部功能,即可关闭,可移动和可浮动。由于日后官方会往停靠窗口中加入更多新功能属性,所以请慎用该参数,否则窗口属性可能会与预期有所不同。请尽量单独指定参数。

QDockWidget.NoDockWidgetFeatures

0x00

停靠窗口无法被关闭,移动或以浮动状态显示。

下面我们使用停靠窗口来完成以下界面:

《快速掌握PyQt5》第二十四章 装入更多控件_第10张图片

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QDockWidget, QTextEdit


class Demo(QMainWindow):
    def __init__(self):
        super(Demo, self).__init__()
        # 1
        self.dock1 = QDockWidget('Dock Window 1', self)
        self.dock2 = QDockWidget('Dock Window 2', self)
        self.dock3 = QDockWidget('Dock Window 3', self)

        # 2
        self.dock1.setAllowedAreas(Qt.RightDockWidgetArea | Qt.LeftDockWidgetArea)
        self.dock2.setAllowedAreas(Qt.RightDockWidgetArea | Qt.TopDockWidgetArea)
        self.dock3.setAllowedAreas(Qt.NoDockWidgetArea)

        # 3
        self.dock1.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable)
        self.dock2.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable)
        self.dock3.setFeatures(QDockWidget.DockWidgetClosable)

        # 4
        self.text1 = QTextEdit()
        self.text2 = QTextEdit()
        self.text3 = QTextEdit()

        self.dock1.setWidget(self.text1)
        self.dock2.setWidget(self.text2)
        self.dock3.setWidget(self.text3)

        # 5
        self.addDockWidget(Qt.RightDockWidgetArea, self.dock1)
        self.addDockWidget(Qt.RightDockWidgetArea, self.dock2)
        self.addDockWidget(Qt.RightDockWidgetArea, self.dock3)

        # 6
        self.center_text = QTextEdit()
        self.setCentralWidget(self.center_text)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    demo = Demo()
    demo.show()
    sys.exit(app.exec_())

1. 实例化三个停靠窗口,在实例化的时候直接设置好窗口的标题;

2. 设置三个停靠窗口的可停靠区域;

3. 设置三个停靠窗口的功能属性,因为我们在第二步中对dock3设置为不可停靠,所在这步中就只用设置可关闭的功能属性就好了;

4. 实例化三个文本编辑框QTextEdit,然后调用QDockWidget的setWidget()方法将文本编辑框放入停靠窗口中;

5. 调用主窗口的addDockWidget()方法将停靠窗口加入到主窗口中,注意要规定停靠窗口刚开始的停靠区域;

6. 设置主窗口的中央控件。

 

运行截图如下,我们改变第一个和第二个停靠窗口的停靠位置:

《快速掌握PyQt5》第二十四章 装入更多控件_第11张图片

停靠窗口还可以合体,只需将其中一个窗口放在另一个上就可以了(〃 ̄︶ ̄)人( ̄︶ ̄〃):

《快速掌握PyQt5》第二十四章 装入更多控件_第12张图片

 

24.5 多文档界面QMdiArea

当使用多文档界面功能时,我们是将QMdiArea作为主窗口的中央部件,然后在这个中央部件中,我们可以同时打开很多个子窗口QMdiSubWindow,就像下面这种多窗口状态:

《快速掌握PyQt5》第二十四章 装入更多控件_第13张图片

下面请看示例:

import sys
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QMainWindow, QMdiArea, QMdiSubWindow, QAction, QTextEdit


class Demo(QMainWindow):
    def __init__(self):
        super(Demo, self).__init__()
        self.mdi_area = QMdiArea(self)                                     # 1
        self.setCentralWidget(self.mdi_area)

        self.toolbar = self.addToolBar('Tool Bar')
    
        self.new_action = QAction(QIcon('images/new.ico'), 'New', self)    # 2
        self.close_action = QAction(QIcon('images/close.ico'), 'Close', self)
        self.close_all_action = QAction(QIcon('images/close_all.ico'), 'Close All', self)
        self.mode1_action = QAction(QIcon('cascade.ico'), 'Cascade', self)
        self.mode2_action = QAction(QIcon('tile.ico'), 'Tile', self)

        self.new_action.triggered.connect(self.new_func)                   # 3
        self.close_action.triggered.connect(self.mdi_area.closeActiveSubWindow)
        self.close_all_action.triggered.connect(self.mdi_area.closeAllSubWindows)
        self.mode1_action.triggered.connect(self.mdi_area.cascadeSubWindows)
        self.mode2_action.triggered.connect(self.mdi_area.tileSubWindows)

        self.toolbar.addAction(self.new_action)                            # 4
        self.toolbar.addAction(self.close_action)
        self.toolbar.addAction(self.close_all_action)
        self.toolbar.addAction(self.mode1_action)
        self.toolbar.addAction(self.mode2_action)

    def new_func(self):
        text = QTextEdit()
        sub = QMdiSubWindow()
        sub.setWidget(text)
        self.mdi_area.addSubWindow(sub)
        sub.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    demo = Demo()
    demo.show()
    sys.exit(app.exec_())

1. 实例化一个QMdiArea控件,并调用setCentralWidget()方法将其设为主窗口的中央部件;

2. 实例化五个动作分别用来新建窗口,关闭当前子窗口,关闭所有子窗口,将子窗口布局方式改为层叠布局以及将子窗口布局方式改为平铺布局;

3. 将各个动作进行信号和槽的连接。new_action的信号连接的槽函数为new_func():

def new_func(self):
    text = QTextEdit()
    sub = QMdiSubWindow()
    sub.setWidget(text)
    self.mdi_area.addSubWindow(sub)
    sub.show()

在该槽函数中,我们首先实例化一个文本编辑框QTextEdit和一个子窗口QMdiSubWindow,然后调用子窗口的setWidget()方法将文本编辑框放入子窗口中(类似QDockWidget用法),最后调用QMdiArea的addSubWindow()方法将子窗口加入到中央部件中,当然我们还要用show()方法将子窗口显示出来。close_action的信号连接的槽函数为QMdiArea的closeActiveSubWindow()方法,该方法可以关闭当前激活的窗口(最新的或者正在使用的窗口)。以下三个连接的槽函数分别为QMdiArea的closeAllSubWindow(),cascadeSubWindow()以及tileSubWindow()方法。第一个方法用来关闭所有的子窗口,第一个方法是将当前子窗口的布局改成层叠布局,而第三个方法是将子窗口的布局改成平铺布局;

4. 在这里我们只用调用addAction()方法将动作加入到工具栏上就行了。

 

图片下载地址:

  • new.ico: https://www.easyicon.net/download/ico/28817/24/
  • close.ico: https://www.easyicon.net/download/ico/26353/24/
  • close_all.ico: https://www.easyicon.net/download/ico/572087/24/
  • cascade.ico: https://www.easyicon.net/download/ico/528866/24/
  • tile.ico: https://www.easyicon.net/download/ico/5048/24/

运行截图如下,新建三个子窗口:

《快速掌握PyQt5》第二十四章 装入更多控件_第14张图片

将布局方式改为层叠布局:

《快速掌握PyQt5》第二十四章 装入更多控件_第15张图片

将布局方式改为平铺布局:

《快速掌握PyQt5》第二十四章 装入更多控件_第16张图片

关闭一个激活的子窗口:

《快速掌握PyQt5》第二十四章 装入更多控件_第17张图片

关闭所有子窗口:

《快速掌握PyQt5》第二十四章 装入更多控件_第18张图片

 

24.6 小结

1. 可以把拆分窗口当作一种特殊的布局管理器来使用,让界面各控件更有灵活性;

2. 堆叠窗口QStackedWidget很适合被用来实现App的多界面功能,读者可以在游戏开发中使用该控件;

3. QDockWidget或者QMdiArea应该与QMainWindow主窗口一起搭配使用;

4. QTabWidget用法和QStackedWidget用法类似;而QDockWidget用法则与QMdiArea用法类似。 

 

----------------------------------------------------------------------

喜欢的小伙伴可以加入这个Python QQ交流群一起学习:820934083

《快速掌握PyQt5》第二十四章 装入更多控件_第19张图片

你可能感兴趣的:(《快速掌握PyQt5》)