pyQt基础4(对话框)
一、对话框实现
"""
除了消息对话框有两种调用方式,其他对话框只能静态调用
"""
import sysfrom PyQt6 import QtCore, QtGui, QtWidgets, uic
from PyQt6.QtCore import Qt, QFile, QIODevice, QIODeviceBase
from PyQt6.QtGui import QPixmap, QMovie, QIcon, QFont, QColor
from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QLineEdit, QComboBox, QFileDialog, \QMessageBox, QToolButton, QWidget, QFontDialog, QColorDialogclass MyWidget(QWidget):def __init__(self, ):super().__init__()ui = uic.loadUi('2ui_textEdit.ui', self)# 绑定self.textEdit: QLineEdit = ui.textEditself.fontBtn: QPushButton = ui.fontBtnself.colorBtn: QPushButton = ui.colorBtnself.openBtn: QPushButton = ui.openBtnself.saveBtn: QPushButton = ui.saveBtn# 设置信号与槽函数的连接self.fontBtn.clicked.connect(self.fontBtn_slot) # 字体对话框self.colorBtn.clicked.connect(self.colorBtn_slot) # 颜色对话框self.openBtn.clicked.connect(self.openBtn_slot) # 文件对话框self.saveBtn.clicked.connect(self.saveBtn_slot)# 1、字体按钮对应的槽函数# 字体对话框def fontBtn_slot(self):# QFontDialog返回两个值,一个是QFont(包含字体信息),一个是bool值(代表用户是否设置了字体)。font, font_choose = QFontDialog.getFont(QFont("黑体", 10, 10),self,"选择字体",)if font_choose:# self.textEdit.setFont(font) #将全部的文本设置成该字体self.textEdit.setCurrentFont(font) # 设置选中字的字体else:QMessageBox.information(self, "提示", "没有选择字体")# 2、颜色按钮对应的槽函数# 颜色对话框def colorBtn_slot(self):""" getColor(initial: Union[QColor, Qt.GlobalColor, int] = Qt.white, #初始颜色parent: Optional[QWidget] = None, #父对象title: Optional[str] = '', #标题options: QColorDialog.ColorDialogOption = QColorDialog.ColorDialogOptions()) # *设置*-> QColor """color = QColorDialog.getColor(QColor(0, 0, 0),self, "选择颜色",) # 函数返回值是QColor,没有bool值# 判断用户是否选中有效颜色color_choose = color.isValid()if color_choose:self.textEdit.setTextColor(color) # 设置字体颜色# self.textEdit.setTextBackgroundColor(color) #设置字体背景颜色else:QMessageBox.information(self, "提示", "没有选择颜色")def openBtn_slot(self):# 3、文件对话框file_path,file_type = QFileDialog.getOpenFileName(self, # 父组件"打开文件", # 标题"./", # 起始路径,"./"表示当前路径"Images(*.png *.jpg *.ico);;文本Text(*.txt);;XML(*.xml)",# 过滤器文本Text、Images等可以自己设置,类型内部用空格分隔,类型之间用两个分号分隔"文本Text",# 默认的起始过滤器# ["x","x"],# 第一个str表示文件路径,第二个str表示文件类型)if file_path :# QFile实例化一个对象,QIODeviceBase用于读取file = QFile(file_path)file.open(QIODeviceBase.OpenModeFlag.ReadWrite)msg = file.readAll() # 读取全部,返回的是QByteArray类型--------------------------------------------------------------------------------msg = msg.data().decode("utf-8") # 将QByteArray类型转为utf-8字符self.textEdit.setText(msg)file.close()def saveBtn_slot(self):""" getSaveFileName(parent: Optional[QWidget] = None,caption: Optional[str] = '',directory: Optional[str] = '',filter: Optional[str] = '',initialFilter: Optional[str] = '',options: QFileDialog.Option = QFileDialog.Options()) -> tuple[str, str] """file_path,file_type = QFileDialog.getSaveFileName(self,"保存文件","./","Images(*.png *.jpg *.ico);;文本Text(*.txt);;XML(*.xml)","文本Text")# 获取文本内容text = self.textEdit.toPlainText() # 注意这是textEdit,如果时lineedit不是这个if not file_path:# 用户点击取消,什么也不做,直接返回returnif not text:QMessageBox.information(self, "提示", "不存在文本")returnwith open(file_path, "w", encoding="utf-8") as file:file.write(text) # 如果文本量极大可改成一行一行写入。QMessageBox.warning(self, "错误", f"保存文件失败:{e}")if __name__ == '__main__':app = QApplication(sys.argv)myWidget = MyWidget()myWidget.show()sys.exit(app.exec()) # exec的返回值是关闭缩小化等操作,传给sys后即可正常关闭,用于阻塞调用,让应用程序不要运行完就关闭而是等待用户操作
对话框保存功能展示:
二、登录界面再次优化
增加功能:
1、页面跳转2、页面1:不同聊天页面的切换
3、页面2:组的创建,建立了对应聊天对象的双击信号,跳转至对应对象的聊天页面
3、不再手动创建好友列表,而是根据设置一个friend_list,代码根据friend_list自动创建聊天对象按钮和聊天页面
重要知识点:
self.chat_pages[idx] ={
'top': top,
'mid': mid,
'bottom': bottom
}
以上代码中的字典,self.chat_pages注册到了self(即全局),这里实际上保存的就是对象,而不是简单的值,此时top、mid、bottom就已经被这个字典统一保存至全局了,只要用索引寻找到字典的变量就可以对本不是全局变量的top、mid、bottom继续修改
这个方法可以解决无法批量完成变量设置的问题,比如每一页都有变量,不可能chatpage1、chatpage2一页一页手动命名,保存到字典之后由于索引的关系,即使变量名一样也可以同时存在,且非常易于变量管理.
"""
引入信号和槽函数pyside6-uic.exe test.ui -o test.py #将ui界面文件转化成py文件
pyside6-rcc res.qrc -o res_rc.py # 将.qrc文件转化成py文件"""
import sysfrom PySide6.QtCore import Qt
from PySide6.QtWidgets import QWidget, QApplication, QPushButton, QMainWindow, QToolBar, QDockWidget, QMessageBox, \QTextEdit, QStackedWidget, QVBoxLayout, QLabel, QLineEdit, QListWidget, QToolBox
from qss_login_v3_ui import Ui_Form
from ui_2 import Ui_MainWindowfrom PyQt6 import uic# pyside6-uic.exe ui_2.ui -o ui_2.py
class myWindow(QWidget, Ui_Form): # 多重继承,同时存在QWidget和Ui_Form的功能,这种ui读取方法不可与之前的混用def __init__(self):super().__init__()# 加载ui文件ui = self.setupUi(self) # 用 Ui_Form 类中的 setupUi() 方法,把设计好的 UI 元素(按钮xi、输入框、布局等)挂载到 myWindow 这个 QWidget 实例上。self.pushButton_2.clicked.connect(self.pushButton_2_slot) # 不需要self.pushButton_2:QPushButton = self.pushButton_2,用setupUi已经直接加载到self了,与uic.load不同self.pushButton.clicked.connect(pushButton_slot)def pushButton_2_slot(self):self.close()class window_2(QMainWindow, Ui_MainWindow): # setupUi读取而不是uic.load,因为PySide6和PyQt6存在区别def __init__(self):super().__init__()# 加载ui文件ui = self.setupUi(self) # 用 Ui_Form 类中的 setupUi() 方法,把设计好的 UI 元素(按钮、输入框、布局等)挂载到 myWindow 这个 QWidget 实例上。# 实例化所有好友,这里可以转为数据库内容?self.friend_list = ['aaa建材王总', 'aaa五金金总', 'aaa水果张总', 'aaa西兰花李总', 'aaa中介小黄', '山本']# 工具栏toolBar = QToolBar(self) # 表明从属于selfself.addToolBar(Qt.ToolBarArea.LeftToolBarArea, toolBar) # 表示在self中显示self.menu_btn1 = QPushButton('消息')self.menu_btn2 = QPushButton('联系人')toolBar.addWidget(self.menu_btn1)toolBar.addWidget(self.menu_btn2)self.menu_btn1.clicked.connect(self.show_page1)self.menu_btn2.clicked.connect(self.show_page2)# 创建堆叠布局self.stacked_widget = QStackedWidget()self.init_page1()self.init_page2()# 直接将 QStackedWidget 设置为中心部件self.setCentralWidget(self.stacked_widget)def init_page1(self):"""页面1:return:"""page1 = QMainWindow()# 1、聊天部分(中心组件)self.chat_pages = {} # 全局容器,用于后面,基于索引保存所有chat_page的变量# 创建主要组件,用于主题的聊天界面self.chat_stacked_widget = QStackedWidget() # 创建堆叠组件page1.setCentralWidget(self.chat_stacked_widget)# 创建多个chat_page用于聊天for idx, friend in enumerate(self.friend_list):page = QWidget() # 每一页的容器layout = QVBoxLayout(page) # 把布局直接设给 pagetop = QLabel(friend)mid = QListWidget()bottom = QTextEdit()send_btn = QPushButton("发送", bottom)send_btn.move(530, 180)send_btn.clicked.connect(self.page_1_send_messege)layout.addWidget(top)layout.addWidget(mid)layout.addWidget(bottom)layout.setStretchFactor(top, 1)layout.setStretchFactor(mid, 4)layout.setStretchFactor(bottom, 3)self.chat_stacked_widget.addWidget(page) # 把这一页加到栈self.chat_pages[idx] = { # 注意:这里保存的是对象,此时top、mid、bottom就已经被这个字典统一保存至全局了,'top': top,'mid': mid,'bottom': bottom}self.stacked_widget.addWidget(page1)# 2、浮动窗口部分page1_docker = QDockWidget(page1)page1.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, page1_docker)# 实例化容器layout以及浮动窗口的widgetdock_widget = QWidget()dock_layout = QVBoxLayout()dock_layout.setSpacing(0)dock_layout.setContentsMargins(0, 0, 0, 0)# 根据实际的好友列表批量建立按钮self.friendBtn = {}for Btn_idx,Btn_friend in enumerate(self.friend_list):Btn = QPushButton(f'{Btn_friend}')dock_layout.addWidget(Btn)Btn.clicked.connect(self.page_1_switch_chat)self.friendBtn[Btn_idx] = { # 注意:这里保存的是对象,此时top、mid、bottom就已经被这个字典统一保存至全局了,'Btn': Btn,}dock_widget.setLayout(dock_layout)page1_docker.setWidget(dock_widget)def init_page2(self):"""页面2:return:"""page2 = QMainWindow()page2_toolBox = QToolBox()page2.setCentralWidget(page2_toolBox)# 创建组件并保存引用self.family_list = QListWidget()self.friends_list = QListWidget()self.classmates_list = QListWidget()self.yamamoto_list = QListWidget()# 设置双击跳转信号,该信号会同时传递两个信号,一个是触发信号,一个则是item,在槽函数中需要接收itemself.family_list.itemDoubleClicked.connect(self.page_2_on_item_double_clicked)self.friends_list.itemDoubleClicked.connect(self.page_2_on_item_double_clicked)self.classmates_list.itemDoubleClicked.connect(self.page_2_on_item_double_clicked)self.yamamoto_list.itemDoubleClicked.connect(self.page_2_on_item_double_clicked)# 添加到toolBoxpage2_toolBox.addItem(self.family_list, "家人")page2_toolBox.addItem(self.friends_list, "朋友")page2_toolBox.addItem(self.classmates_list, "同学")page2_toolBox.addItem(self.yamamoto_list, "山本")# 现在可以修改具体的列表self.yamamoto_list.addItem("山本")self.friends_list.addItems(["aaa建材王总", "aaa五金金总", "aaa水果张总", "aaa西兰花李", "aaa中介小黄","山本"])self.stacked_widget.addWidget(page2)def page_1_send_messege(self):"""发送消息功能,未联网,只是显示而已.:return:"""idx = self.chat_stacked_widget.currentIndex() # 获取当前页面page = self.chat_pages[idx] # 从获保存页面信息的字典总获取当前页面的三个变量text = page['bottom'].toPlainText().strip() #if not text:QMessageBox.warning(self, "注意", "消息为空")returnpage['mid'].addItem(f"我:{text}")page['bottom'].clear()def page_1_switch_chat(self):"""单击页面1中的浮动窗口中的聊天对象时,自动跳转与其对应的聊天框,不再公用一个聊天框:return:"""target_name = self.sender().text()for idx, page_dict in self.chat_pages.items():if page_dict['top'].text() == target_name:self.chat_stacked_widget.setCurrentIndex(idx)break# page2的函数def page_2_on_item_double_clicked(self, item): # item为点击对象的来源,item.text()及点击目标的文本"""双击页面2群组中的人后自动跳转界面1的对应对象的聊天框.:param item::return:"""target_name = item.text() # ListWidget没有这种功能,无法使用,但是ListWidget会自动发送item信号来表示来源self.stacked_widget.setCurrentIndex(0) # 先切换大页面在切换聊天页面for idx, page_dict in self.chat_pages.items():if page_dict['top'].text() == target_name:self.chat_stacked_widget.setCurrentIndex(idx)break# # 静态函数可以跳过类实例化直接用QMessageBox.information(self, "提示", "山本,我日你仙人!!!") # window表示父窗口def show_page2(self):self.stacked_widget.setCurrentIndex(1)def show_page1(self):self.stacked_widget.setCurrentIndex(0)# 登录按钮
def pushButton_slot(): # 将槽函数设为全局可用而不是某一类下if window.lineEdit.text() == "" and window.lineEdit_2.text() == "":# python基础:window.lineEdit.text()钟text后面要加一个括号是因为这是一个函数,若window.lineEdit.text是一个对象就不需要括号print("匹配成功")window.close()window_2.show()else:print("匹配失败")window.lineEdit.setText("")window.lineEdit_2.setText("")QMessageBox.information(window, "提示", "帐号或密码错误") # window表示父窗口if __name__ == '__main__':app = QApplication(sys.argv)window = myWindow()# 设置纯净窗口(无边框)window.setWindowFlag(Qt.WindowType.FramelessWindowHint)# 去掉窗口多余的空白部分-----------------------------------------------------------------------------------------------window.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)window.show()window_2 = window_2()sys.exit(app.exec())