第15.39节、splitDockWidget和tabifyDockWidget嵌套布局QDockWidget的PyQt人机对话案例:笨笨机器人
一、引言
在第《第三十一章、containers容器类部件QDockWidget停靠窗功能介绍》详细介绍了QDockWidget的属性、方法和信号,并介绍了利用QMainWindow的splitDockWidget和tabifyDockWidget等方法实现基于主窗口布局的方法。本节将利用相关方法实现一个简单的人机对话应用:笨笨机器人。
二、案例介绍
2.1、功能介绍
笨笨机器人是老猿测试QDockWidget的一个测试程序,其运行界面如图所示:
可以看到,该测试程序实现了一个简单的人机对话(机器应答是在设定应答语句中随机挑选一个),可以设置对话双方的名字,可以设置聊天信息的字体和颜色。
2.2、实现思路
要实现这个简单应用,有很多种方法,本次测试是为了使用QDockWidget,因此通过QMainWindow和QDockWidget配套实现。
由于QDockWidget本身的内容区域(内容)也是一个QWidget对象,要实现在QMainWindow上放置多个停靠窗、每个停靠窗要放置对应的内容子部件对象、并对停靠窗进行排列布局操作,在Designer中进行UI设计反而比通过代码实现麻烦很多,因此整体UI布局大部分都是通过代码实现的,在Designer中只实现了昵称配置信息窗口的界面和主窗口QMainWindow的主窗口基本界面。
2.3、昵称设置窗实现
昵称设置设计界面如下:
其中两个输入框的名称分别为myName和robertName。
ui设计后将其生成代码模块文件ui_configWin.py,然后派生类,只实现构造方法,其他都不进行处理。
class configWin(ui_configWin.Ui_configWin,QtWidgets.QWidget):
def __init__(self,parent=None):
super().__init__(parent)
self.setupUi(self)
2.4、主窗口基础实现
2.4.1、ui设计界面
主窗口基本界面就是一个mainWindow,没有放置任何子部件,名称也是mainWindow,设置了标题信息为:“老猿Python:DockWidget测试 网址:https://blog.csdn.net/LaoYuanPython”。生成代码后存放在模块文件ui_mainWin.py中。
2.4.2、主窗口派生类及构造方法
主窗口派生类及构造方法代码如下:
class mainWin(QtWidgets.QMainWindow,ui_mainWin.Ui_mainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.robertColor = Qt.black #设置机器人对话字体初始颜色
self.myColor = Qt.blue #设置输入对话字体初始颜色
self.setDockNestingEnabled(True) #让主窗口支持停靠窗嵌套
w = self.takeCentralWidget() #移除中央窗口部件,请参见第三十一章的介绍
self.initDock() #进行停靠窗生成及排列
说明:
- 构造方法中,除了常规的UI界面类的代码外,初始化了对话文本的颜色,设置了让主窗口支持停靠窗嵌套,最后调用initDock方法进行停靠窗生成及排列;
- 需要关注一下setDockNestingEnabled语句,相关属性的含义可以参考《PyQt(Python+Qt)学习随笔:Qt Designer中主窗口对象dockNestingEnabled属性》。但这个属性说明的字面说明太简单,实际理解还是有些复杂。在这里通过两个截图对比说明一下:上图是主窗口允许嵌套的场景,图中所有有关闭和浮动标记的窗口都是QDockWidget对象,在允许嵌套情况下,“机器人发言字体颜色”设置窗和“本机输入发言字体颜色”两个窗口可以上下堆叠合占一个停靠位的位置。而下图是未设置dockNestingEnabled的,这两个窗口就不能纵向堆叠在一起,只能选项卡式化占用一个停靠位。
所以嵌套是指一个停靠位的位置堆叠或水平排列了两个停靠窗,而这个位置正常情况下只能放置一个对应大小的停靠窗。 - takeCentralWidget作用是将主窗口中央区域部件从主窗口中移除,对象并没有删除,当中央部件没有移除时,停靠窗只能停留在中央部件四周,移除后可以停靠到整个主窗口区域,下面截图对比一下:
同一个程序,左边是移除了中央部件,右边没有,二者的效果对比可以看出移除中央部件的效果。
如果没有调用takeCentralWidget移除中央部件,后续调用setCentralWidget将一个停靠窗口设置为中央部件也可以实现类似的效果。
2.4.3、创建对话显示框停靠窗
对话显示框dialogDisplay 为一个QTextEdit对象,将其作为dialogDisplayDock停靠窗的内容部件,将其放置在主窗口中央部件区域。
self.dialogDisplay = QtWidgets.QTextEdit()
self.dialogDisplayDock = QtWidgets.QDockWidget("对话记录", self)
self.dialogDisplayDock.setWidget(self.dialogDisplay)#将对话窗作为对话停靠窗的内容部件
self.setCentralWidget(self.dialogDisplayDock)
2.4.4、创建输入停靠窗
输入部件input 为一个QLineEdit对象,将其作为inputDock 停靠窗的内容部件,在代码中设置了输入部件的宽度。由于部件是代码创建,因此信号与槽方法的连接关系必须代码实现,在此将输入部件按下回车键作为消息发送的信号连接到主窗口的槽方法inputEnd。
self.input = QtWidgets.QLineEdit()
self.input.geometry().setWidth(200)
self.input.returnPressed.connect(self.inputEnd)
self.inputDock = QtWidgets.QDockWidget("对话输入(回车键发送):", self)
self.inputDock.setWidget(self.input)#将输入部件input作为inputDock停靠窗的内容部件
2.4.5、构建字体选择停靠窗、机器人对话文本颜色停靠窗、发言人对话文本颜色停靠窗
字体选择停靠窗fontDock包含标题和一个设置字体的按钮,按钮点击后能触发字体选择。
self.fontDock = QtWidgets.QDockWidget('字体设置',self)
fontButton = QtWidgets.QPushButton('点此设置字体',self.fontDock)
setFontSizeColor(fontButton,QtGui.QPalette.ButtonText,Qt.red,10)#设置按钮文字的颜色和大小
fontButton.clicked.connect(self.getFont)
self.fontDock.setWidget(fontButton)#将fontButton作为fontDock停靠窗的内容部件
机器人对话文本颜色停靠窗robertFontColorDock用于设置输入文字在对话窗中显示的颜色、发言人对话文本颜色停靠窗myFontColorDock用于设置机器人应答文字在对话窗中显示的颜色,二者的实现与fontDock类似,只是按钮连接的槽方法不同,设置的按钮颜色不同。在此不详细介绍,大家可参考附件代码。
2.4.6、构建昵称设置停靠窗
昵称设置停靠窗configDock包含内容部件configWin,configWin是一个单独的ui设计模块派生的类,用于设置输入者昵称和机器人昵称,其ui设计界面如下:
在构建configDock时,将configWin作为其内容部件。
2.4.7、排列停靠窗
将所有停靠窗对象及对应内容部件对象都创建后,接下来需要在QMainWindow中排列这些窗口,最终程序运行后的初始排列效果如下:
要实现停靠窗的排列,需要分如下两步进行:
2.4.7.1、将所有停靠窗按一定位置加到主窗口
按照上图的排列,dialogDisplayDock在嘴上,输入框在左边,昵称配置窗在右边,其他在最下面,按此规则使用如下语句将停靠窗加入到主窗口:
self.addDockWidget(QtCore.Qt.TopDockWidgetArea, self.dialogDisplayDock)
self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.inputDock)
self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.configDock)
self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.fontDock)
self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.robertFontColorDock)
self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.myFontColorDock)
self.configDock.setMinimumWidth(320) #设置配置窗最小宽度
运行后效果:
2.4.7.2、使用splitDockWidget和tabifyDockWidget调整窗口位置
上图不是我们要的效果,此时需要使用splitDockWidget来调整这些窗口的排列。首先将昵称配置窗调整到输入框右边,然后将字体设置窗调整到输入框下边,最后将两个颜色设置框与字体设置框进行选项卡化。使用如下代码:
self.splitDockWidget(self.inputDock, self.configDock, Qt.Horizontal) #昵称配置窗调整到输入框右边
self.splitDockWidget(self.inputDock,self.fontDock, Qt.Vertical) #将字体设置窗调整到输入框下边
self.tabifyDockWidget(self.fontDock, self.myFontColorDock) #将输入文字颜色设置框与字体设置框进行选项卡化
self.tabifyDockWidget(self.fontDock, self.robertFontColorDock) #将机器人文字颜色设置框与字体设置框进行选项卡化
大家结合上章介绍的内容理解一下splitDockWidget和tabifyDockWidget的作用。
运行效果:
发现达到了想要的效果。
2.4.8、实现对话窗发送消息后的响应槽方法inputEnd
在槽方法内需要设置输入消息的字体颜色并显示输入信息,同时调用机器人应答方法输出机器人应答消息。
def inputEnd(self):
if self.myColor: self.dialogDisplay.setTextColor(self.myColor)
self.dialogDisplay.append(self.configWin.myName.text()+': '+ self.input.text())
self.input.clear()
self.robertAnswer() #输出机器人应答消息
2.4.9、实现设置字体的槽方法getFont
getFont方法调用字体设置对话框来获取需要设置的字体,对话框原字体作为字体设置对话窗的初始字体。
def getFont(self):#,visible):
font = self.dialogDisplay.font() #取现有字体
font,changed = QtWidgets.QFontDialog.getFont(font,self,"字体设置")
if changed: self.dialogDisplay.setFont(font)
2.4.10、实现设置输入文字或机器人应答文字颜色的槽方法getFontColor
getFontColor方法需要判断信号发射对象是来自输入文字颜色设置按钮还是机器人应答消息颜色按钮,然后根据不同取不同的初始颜色,并调用颜色选择对话窗选择颜色,并将颜色记录后,将对应按钮的文字颜色设置文新的颜色。相关代码与设置字体类似,在此就不介绍了。
经过以上步骤,一个比较完整的人机对话简单应用就构建完了。
广告
老猿关于PyQt的付费专栏《使用PyQt开发图形界面Python应用》只需要9.9元,该部分与第十五章的内容基本对应,但同样内容在付费专栏上总体来说更详细、案例更多。本节内容对应付费专栏的《第三十二章、使用splitDockWidget和tabifyDockWidget嵌套布局QDockWidget的PyQt人机对话案例》。如果有兴趣也愿意支持老猿的读者,欢迎购买付费专栏。