PyQt5提供了一系列标准的对话框,常见的有:消息对话框QMessageBox、颜色对话框QColorDialog、字体对话框QFontDialog、输入对话框QInputDialog以及文件对话框QFileDialog。第一种消息对话框已经在第四章讲解过,下面我们来熟悉下其他四种。
这两种对话框分别可以让用户进行颜色和字体选择。两者用法相似,所以就放在一起讲了:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QTextEdit, QColorDialog, QFontDialog, QPushButton,
QHBoxLayout, QVBoxLayout
class Demo(QWidget):
def __init__(self):
super(Demo, self).__init__()
self.text_edit = QTextEdit(self) # 1
self.color_btn = QPushButton('Color', self) # 2
self.font_btn = QPushButton('Font', self)
self.color_btn.clicked.connect(lambda: self.open_dialog_func(self.color_btn))
self.font_btn.clicked.connect(lambda: self.open_dialog_func(self.font_btn))
self.h_layout = QHBoxLayout()
self.h_layout.addWidget(self.color_btn)
self.h_layout.addWidget(self.font_btn)
self.v_layout = QVBoxLayout()
self.v_layout.addWidget(self.text_edit)
self.v_layout.addLayout(self.h_layout)
self.setLayout(self.v_layout)
def open_dialog_func(self, btn):
if btn == self.color_btn: # 3
color = QColorDialog.getColor()
if color.isValid():
self.text_edit.setTextColor(color)
else: # 4
font, ok = QFontDialog.getFont()
if ok:
self.text_edit.setFont(font)
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
1. QTextEdit控件用于显示文本颜色和字体变化;
2. 实例化两个按钮分别用于打开颜色对话框和字体对话框,然后进行信号和槽的连接;
3. 如果是color_btn被按下的话,则调用QColorDialog的getColor()方法显示颜色对话框,当选择一种颜色后其十六进制的值会保存在color变量中,但如果点击对话框中的取消(Cancel)按钮的话,则color为无效值。通过isValid()方法判断color是否有效,若有效的话则通过setTextColor()方法设置QTextEdit的文本颜色;
4. 如果是font_btn被按下的话,则调用QFontDialog的getFont()方法显示字体对话框,该方法返回两个值,分别为QFont和bool值,如果用户选择了一种字体并按下确定(Ok)的话,则font保存所选择的QFont值,并且ok为True。若按下取消(Cancel)的话,则bool为False。当ok为True时,调用setFont()方法设置QTextEdit的文本字体。
运行截图如下:
颜色对话框:
字体对话框:
注:字体对话框QFontDialog在Mac上不起作用。
输入对话框一共有五种输入方法:
下面请看示例:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QInputDialog, QLineEdit, QTextEdit, QPushButton,
QGridLayout
class Demo(QWidget):
def __init__(self):
super(Demo, self).__init__()
self.name_btn = QPushButton('Name', self)
self.gender_btn = QPushButton('Gender', self)
self.age_btn = QPushButton('Age', self)
self.score_btn = QPushButton('Score', self)
self.info_btn = QPushButton('Info', self)
self.name_btn.clicked.connect(lambda: self.open_dialog_func(self.name_btn))
self.gender_btn.clicked.connect(lambda: self.open_dialog_func(self.gender_btn))
self.age_btn.clicked.connect(lambda: self.open_dialog_func(self.age_btn))
self.score_btn.clicked.connect(lambda: self.open_dialog_func(self.score_btn))
self.info_btn.clicked.connect(lambda: self.open_dialog_func(self.info_btn))
self.name_line = QLineEdit(self)
self.gender_line = QLineEdit(self)
self.age_line = QLineEdit(self)
self.score_line = QLineEdit(self)
self.info_textedit = QTextEdit(self)
self.g_layout = QGridLayout()
self.g_layout.addWidget(self.name_btn, 0, 0, 1, 1)
self.g_layout.addWidget(self.name_line, 0, 1, 1, 1)
self.g_layout.addWidget(self.gender_btn, 1, 0, 1, 1)
self.g_layout.addWidget(self.gender_line,1, 1, 1, 1)
self.g_layout.addWidget(self.age_btn, 2, 0, 1, 1)
self.g_layout.addWidget(self.age_line, 2, 1, 1, 1)
self.g_layout.addWidget(self.score_btn, 3, 0, 1, 1)
self.g_layout.addWidget(self.score_line, 3, 1, 1, 1)
self.g_layout.addWidget(self.info_btn, 4, 0, 1, 1)
self.g_layout.addWidget(self.info_textedit, 4, 1, 1, 1)
self.setLayout(self.g_layout)
def open_dialog_func(self, btn):
if btn == self.name_btn: # 1
name, ok = QInputDialog.getText(self, 'Name Input', 'Please enter the name:')
if ok:
self.name_line.setText(name)
elif btn == self.gender_btn: # 2
gender_list = ['Female', 'Male']
gender, ok = QInputDialog.getItem(self, 'Gender Input', 'Please choose the gender:', gender_list, 0, False)
if ok:
self.gender_line.setText(gender)
elif btn == self.age_btn:
age, ok = QInputDialog.getInt(self, 'Age Input', 'Please select the age:')
if ok:
self.age_line.setText(str(age))
elif btn == self.score_btn:
score, ok = QInputDialog.getDouble(self, 'Score Input', 'Please select the score:')
if ok:
self.score_line.setText(str(score))
else:
info, ok = QInputDialog.getMultiLineText(self, 'Info Input', 'Please enter the info:')
if ok:
self.info_textedit.setText(info)
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
前面就是实例化按钮、单行输入框和文本编辑框并通过布局管理器进行排列,我们重点来看下槽函数:
1. 如果是name_btn被点击的话,则调用QInputDialog的getText(parent, str, str)方法,第一个参数为指定的父类,第二个为输入框的标题,第三个为输入框提示。方法会返回一个字符串和一个布尔值,若点击输入框的ok按钮,则变量ok就为True,接着我们调用QLineEdit的setText()方法将其文本设为所输入的内容;
2. getItem(parent, str, str, iterable, int, bool)方法需要多设置几个参数,前三个与getText()相同,第四个参数为要加入的选项内容,这里我们传入了item_list列表,可以让用户选择男性或女性。第五个参数为最初显示的选项,0代表刚开始显示第一个选项,即Female。最后一个参数是选项内容是否可编辑,这里设为False,不可编辑。
其他方法的使用都是类似的,这里就进行省略了。
运行截图如下:
点击Name按钮输入姓名:
点击Gender按钮选择性别:
点击Age按钮设置年龄:
点击Score按钮设置分数:
点击Info按钮输入多行文本:
设置完毕后显示如下:
我们在第十七章17.1节窗口关闭事件中完成了一个可以保存txt文件的小程序,现在我们来给它添加文件对话框让其功能更加友好:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QTextEdit, QPushButton, QMessageBox, QVBoxLayout,
QHBoxLayout, QFileDialog
class Demo(QWidget):
def __init__(self):
super(Demo, self).__init__()
self.is_saved = True
self.is_saved_first = True # 1
self.path = '' # 2
self.textedit = QTextEdit(self)
self.textedit.textChanged.connect(self.on_textchanged_func)
self.button = QPushButton('Save', self)
self.button.clicked.connect(self.on_clicked_func)
self.button_2 = QPushButton('Open', self) # 3
self.button_2.clicked.connect(self.open_file_func)
self.h_layout = QHBoxLayout()
self.h_layout.addWidget(self.button)
self.h_layout.addWidget(self.button_2)
self.v_layout = QVBoxLayout()
self.v_layout.addWidget(self.textedit)
self.v_layout.addLayout(self.h_layout)
self.setLayout(self.v_layout)
def on_textchanged_func(self):
if self.textedit.toPlainText():
self.is_saved = False
else:
self.is_saved = True
def on_clicked_func(self): # 4
if self.is_saved_first:
self.save_as_func(self.textedit.toPlainText())
else:
self.save_func(self.textedit.toPlainText())
def save_func(self, text):
with open(self.path, 'w') as f:
f.write(text)
self.is_saved = True
def save_as_func(self, text):
self.path, _ = QFileDialog.getSaveFileName(self, 'Save File', './', 'Files (*.txt *.log)')
if self.path:
with open(self.path, 'w') as f:
f.write(text)
self.is_saved = True
self.is_saved_first = False
def open_file_func(self): # 5
file, _ = QFileDialog.getOpenFileName(self, 'Open File', './', 'Files (*.txt *.log)')
if file:
with open(file, 'r') as f:
self.textedit.clear()
self.textedit.setText(f.read())
self.is_saved = True
def closeEvent(self, QCloseEvent):
if not self.is_saved:
choice = QMessageBox.question(self, '', 'Do you want to save the text?',
QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
if choice == QMessageBox.Yes: # 6
self.on_clicked_func()
QCloseEvent.accept()
elif choice == QMessageBox.No:
QCloseEvent.accept()
else:
QCloseEvent.ignore()
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
1. is_saved_first变量用于判断是否是第一次进行保存,如果是的话则会打开文件对话框让用户选择路径,填好文件名然后保存(即另存为)。如果不是第一次保存就说明之前保存过,那用户再点击保存按钮时程序就直接将文本保存在第一次保存时的路径;
2. path变量用于保存路径;
3. 新增了一个打开按钮,点击后可以打开文件对话框用于选择并打开文件;
4. 在on_clicked_func()槽函数中,我们首先判断是不是第一次保存,是的话调用save_as_func()函数进行另存为操作:
def save_as_func(self, text):
self.path, _ = QFileDialog.getSaveFileName(self, 'Save File', './', 'Files (*.txt *.log)')
if self.path:
with open(self.path, 'w') as f:
f.write(text)
self.is_saved = True
self.is_saved_first = False
在该函数中我们调用QFileDialog的getSaveFileName(parent, str, strt, str)方法,第一个参数为指定父类,第二个为文件对话框的标题,第三个为对话框打开时显示的路径,最后一个为文件扩展名过滤器——对话框只会显示该过滤器所提供的扩展名。当用户在对话框中选择好要保存的路径和文件名后,对话框返回绝对路径(我们保存在path中)和扩展过滤器,这里我们用不到后者,所以直接就保存在_中。有了绝对路径后我们就可以使用with open()方法保存文本内容了。
如果不是第一次保存的话,那说明path已经保存了第一次保存时的路径了,那我们就直接使用with open()方法进行保存就行了。
请注意要改变is_saved和is_saved_first的值。
5. 在open_file_func()槽函数中,我们调用QFileDialog的getOpenFileName(parent, str, str, str)方法,用法跟getSaveFileName()类似,这里不再详细讲解。当用户选择了要开打的文件后,file值保存绝对路径,我们用with open()方法打开读取,并将textedit的文本设为该文件内容。请小伙伴们思考下为什么这里要将is_save设为True。
6. 在关闭窗口时,若用户点击Yes,则保存逻辑跟on_clicked_func()槽函数一样,所以直接调用该槽函数就可以了(不过还是有点区别,因为此时is_saved和is_saved_first变量没必要修改了)。
运行截图如下,在编辑框中输入文本,第一次点击保存按钮,则会打开文件保存对话框,这里我们选择保存到桌面,文件名设为French:
保存后,点击打开按钮,则会显示文件打开对话框,这里我们选择已经存在于桌面的English.txt:
之后编辑框显示被打开文件的内容:
1. 不同系统对PyQt5的支持似乎还是有点区别,QFontDialog在Mac上无效(笔者用C++配合Qt也试了一遍,结果一样无效);
2. 方法的话本章是不可能全部用到的,但如果有想用到的方法,请务必查询文档;(•̀ᴗ•́) ̑̑
3. 23.3中的程序功能其实还不是很完整,甚至还有缺陷,我们会在下一章再进行完善。