1.窗口
1 在Qt中,生成窗口有三种方式,分别为:QWidget | QMainWindow | QDialog 2 3 QWidget 4 5 控件和窗口的父类,自由度高(什么东西都没有),没有划分菜单,工具栏,主窗口等区域 6 7 QMainWindow 8 9 是QWidget的子类,包含菜单栏,工具栏,状态栏,标题栏等,中间部分则为主窗口区域 10 11 QDialog 12 13 对话框窗口的基类
1.1QWidget
1.2.QMainWindow
1 import sys 2 from PyQt5.QtWidgets import QMainWindow, QLabel, QApplication 3 4 5 class MyWindow(QMainWindow): 6 def __init__(self): 7 super().__init__() 8 self.init_ui() 9 10 def init_ui(self): 11 label = QLabel("这是文字~~") 12 label.setStyleSheet("font-size:30px;color:red") 13 # 调用父类中的menuBar,从而对菜单栏进行操作 14 menu = self.menuBar() 15 # 如果是Mac的话,菜单栏不会在Window中显示而是屏幕顶部系统菜单栏位置 16 # 下面这一行代码使得Mac也按照Windows的那种方式在Window中显示Menu 17 menu.setNativeMenuBar(False) 18 file_menu = menu.addMenu("文件") 19 file_menu.addAction("新建") 20 file_menu.addAction("打开") 21 file_menu.addAction("保存") 22 edit_menu = menu.addMenu("编辑") 23 edit_menu.addAction("复制") 24 edit_menu.addAction("粘贴") 25 edit_menu.addAction("剪切") 26 # 设置中心内容显示 27 self.setCentralWidget(label) 28 29 30 if __name__ == '__main__': 31 app = QApplication(sys.argv) 32 w = MyWindow() 33 # 设置窗口标题 34 w.setWindowTitle("我是窗口标题....") 35 # 展示窗口 36 w.show() 37 # 程序进行循环等待状态 38 app.exec()
1.3.QDialog
不过对话框一般不应该作为主窗口的存在,而是通过点击操作弹出,起到提示作用
!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!
2.信号与槽*****************(信号与槽是Qt的核心内容)
1 1.信号(signal) 2 其实就是事件(按钮点击、内容发生改变、窗口的关闭事件)或者是状态(check选中了,togglebutton切换)。当程序触发了某种状态或者发生了某种事件(比如:按钮被点击了,内容改变等等),那么即可发射出来一个信号。 3 4 2.槽(slot) 5 若向捕获这个信号,然后执行相应的逻辑代码,那么就需要使用到槽,槽实际上是一个函数,当信号发射出来后,会执行与之绑定的槽函数。 6 7 3.将信号与槽链接 8 为了能够实现,当点击某个按钮时执行某个逻辑,需要把具体的信号和具体的槽函数绑定到一起。操作大体流程如下: 9 10 对象.信号.connect(槽函数)
需求:
当出现了某一种信号(某一种事件)的时候,我们需要执行一段代码(用函数来包装这份代码-----信号和槽)
案例1:接收信号
1 import sys 2 from PyQt5.QtWidgets import QApplication, QWidget, QPushButton 3 4 5 class MyWindow(QWidget): 6 def __init__(self): 7 super().__init__() 8 self.init_ui() 9 10 def init_ui(self): 11 # 更改当前窗口的宽高 12 self.resize(500, 300) 13 # 创建一个按钮 14 btn = QPushButton("点我点我", self) 15 # 设置窗口位置、宽高 16 btn.setGeometry(200, 200, 100, 30) 17 # 将按钮被点击时触发的信号与我们定义的函数(方法)进行绑定 18 # 注意:这里没有(),即写函数的名字,而不是名字() 19 btn.clicked.connect(self.click_my_btn) 20 21 def click_my_btn(self, arg): 22 # 槽函数,点击按钮则调用该函数 23 # 这里的参数正好是信号发出,传递的参数 24 print("点击按钮啦~", arg) 25 26 27 if __name__ == '__main__': 28 app = QApplication(sys.argv) 29 w = MyWindow() 30 w.show() 31 app.exec()
案例2:自定义信号【重点】
除了接收Qt自带的信号之外,我们也可以自行定义信号,在合适的时机,自行发射信息。自定义信号需要使用到 pyqtSignal 来声明信号,并且需要在类中的函数之外声明。如果会自定义信号,那么信号和槽基本就掌握了。否则永远只会接收别人发射出的信号。
1 import sys 2 import time 3 from PyQt5.QtWidgets import * 4 from PyQt5.QtCore import * 5 6 7 class MyWindow(QWidget): 8 # 声明一个信号 只能放在函数的外面 9 my_signal = pyqtSignal(str) 10 11 def __init__(self): 12 super().__init__() 13 self.init_ui() 14 self.msg_history = list() # 用来存放消息 15 16 def init_ui(self): 17 self.resize(500, 200) 18 # 创建一个整体布局器 19 container = QVBoxLayout() 20 # 用来显示检测到漏洞的信息 21 self.msg = QLabel("") 22 self.msg.resize(440, 15) 23 # print(self.msg.frameSize()) 24 self.msg.setWordWrap(True) # 自动换行 25 self.msg.setAlignment(Qt.AlignTop) # 靠上 26 # self.msg.setStyleSheet("background-color: yellow; color: black;") 27 # 创建一个滚动对象 28 scroll = QScrollArea() 29 scroll.setWidget(self.msg) 30 # 创建垂直布局器,用来添加自动滚动条 31 v_layout = QVBoxLayout() 32 v_layout.addWidget(scroll) 33 # 创建水平布局器 34 h_layout = QHBoxLayout() 35 btn = QPushButton("开始检测", self) 36 # 绑定按钮的点击,点击按钮则开始检测 37 38 btn.clicked.connect(self.check) 39 40 h_layout.addStretch(1) # 伸缩器 41 h_layout.addWidget(btn) 42 h_layout.addStretch(1) 43 # 操作将要显示的控件以及子布局器添加到container 44 container.addLayout(v_layout) 45 container.addLayout(h_layout) 46 # 设置布局器 47 self.setLayout(container) 48 # 绑定信号和槽 49 self.my_signal.connect(self.my_slot) 50 51 def my_slot(self, msg): 52 # 更新内容 53 print(msg) 54 self.msg_history.append(msg) 55 self.msg.setText("<br>".join(self.msg_history)) 56 self.msg.resize(440, self.msg.frameSize().height() + 15) 57 self.msg.repaint() # 更新内容,如果不更新可能没有显示新内容 58 59 def check(self): 60 for i, ip in enumerate(["192.168.1.%d" % x for x in range(1, 255)]): 61 msg = "模拟,正在检查 %s 上的漏洞...." % ip 62 # print(msg) 63 if i % 5 == 3: 64 # 表示发射信号 对象.信号.发射(参数) 65 self.my_signal.emit(msg + "【发现漏洞】") 66 time.sleep(0.01) 67 68 69 if __name__ == '__main__': 70 app = QApplication(sys.argv) 71 w = MyWindow() 72 w.show() 73 app.exec()
3.QT Designer
3.1.使用流程
- 创建一个基于QWidget的界面
- 此时会创建一个新的窗口,如下效果
- 拖动想要的控件
- 选中控件,看属性
- 修改属性
其实我们可以看到都是一堆前端代码写出来的页面
6.信号与槽
通过这个app左下序号的操作,可以将“点我”这个按钮点击触发的信号与LCDNumber进行关联,从而实现信号与槽的绑定(双击点击下拉选择)
在这里我设置这样的信号与槽,信号的发送者是pushButton,触发条件是当按钮被点击时,信号的接受者为lcdNumber,接受者的执行动作为接受到信号后关闭。然后保存文件,放在pycharm解释器中,写脚本测试
练习
目的:获取用户名、密码,在TextBrowser中显示一些登录的信息
使用的技术:python加载.ui文件获取了界面,对.ui文件中的控件操作,完成信号与槽的绑定等。
首先我们print(self.ui.__dict__) ,查看ui文件中有哪些控件,发现打印出来的数量正好与在设计.ui文件时控件的数量一致,这里我就不放打印出来的图片啦
1 import sys 2 from PyQt5.QtWidgets import * 3 from PyQt5 import uic 4 5 6 class MyWindow(QWidget): 7 def __init__(self): 8 super().__init__() 9 self.init_ui() 10 11 def init_ui(self): 12 self.ui = uic.loadUi(r"C:\Users\19225\PycharmProjects\test\src\user\static\login.ui") 13 # print(self.ui.__dict__) # 查看ui文件中有哪些控件 14 # 提取要操作的控件 15 self.user_name_qwidget = self.ui.lineEdit # 用户名输入框 16 self.password_qwidget = self.ui.lineEdit_2 # 密码输入框 17 self.login_btn = self.ui.pushButton # 登录按钮 18 self.forget_password_btn = self.ui.pushButton_2 # 忘记密码按钮 19 self.textBrowser = self.ui.textBrowser # 文本显示区域 20 # 绑定信号与槽函数 21 self.login_btn.clicked.connect(self.login) 22 23 def login(self): 24 """登录按钮的槽函数""" 25 user_name = self.user_name_qwidget.text() 26 password = self.password_qwidget.text() 27 if user_name == "admin" and password == "123456": 28 self.textBrowser.setText("欢迎%s" % user_name) 29 self.textBrowser.repaint() 30 else: 31 self.textBrowser.setText("用户名或密码错误....请重试") 32 self.textBrowser.repaint() 33 其他逻辑自己添加吧 34 -------------------------------------------------------------------------------------- 35 if __name__ == '__main__': 36 app = QApplication(sys.argv) 37 w = MyWindow() 38 # 展示窗口 39 w.ui.show() 40 app.exec()
4.PyQt多线程
1 多线程与多进程的概念 2 3 多线程 (Multithreading) 4 5 定义:多线程是在单个进程内运行多个线程,每个线程可以执行不同的任务。线程是操作系统调度的基本单位。 6 共享内存空间:线程共享同一个进程的内存空间,因此可以轻松地共享数据,但这也带来了线程安全的问题。 7 轻量级:线程比进程更轻量级,创建和销毁的开销较小。 8 适用场景:适用于I/O密集型任务,如文件读写、网络请求等,因为这些任务往往在等待I/O操作完成时会阻塞线程,但其他线程可以继续执行。 9 10 11 多进程 (Multiprocessing) 12 13 定义:多进程是在操作系统内同时运行多个进程,每个进程拥有独立的内存空间和资源。 14 独立内存空间:进程之间不共享内存空间,这使得它们之间的数据共享需要通过进程间通信(IPC)机制,如管道、消息队列等。 15 重量级:进程比线程更重量级,创建和销毁的开销较大。 16 适用场景:适用于CPU密集型任务,如复杂计算、数据处理等,因为每个进程可以独立运行在多核CPU上,充分利用多核处理能力。
多线程与多进程的区别
1 资源使用: 2 多线程:共享同一进程的内存空间和资源,线程间通信(如通过全局变量或共享对象)更加便捷,但需要处理线程同步问题。 3 4 多进程:每个进程有独立的内存空间和资源,进程间通信相对复杂,但能提供更好的隔离性和稳定性。 5 6 开销: 7 多线程:创建和销毁的开销较小,适合轻量级的并发任务。 8 多进程:创建和销毁的开销较大,但适合需要隔离资源和独立运行的重任务。 9 10 安全性: 11 多线程:由于共享内存空间,线程安全(如竞争条件、死锁)是一个重要问题,需要使用锁、信号量等机制。 12 多进程:各进程独立运行,安全性较高,不会出现线程间资源竞争的问题。 13 14 性能: 15 多线程:适合I/O密集型任务,可以提高程序的响应速度和处理效率。 16 多进程:适合CPU密集型任务,可以充分利用多核CPU的计算能力,提高计算效率。
应用场景
1 多线程应用场景 2 GUI应用:在GUI应用中,多线程可以用来处理后台任务(如文件下载、数据加载),以避免阻塞主线程,使界面保持响应。 3 4 网络应用:网络服务器、爬虫等需要处理大量I/O操作的应用,可以使用多线程来处理多个客户端连接或请求。 5 6 多进程应用场景 7 数据处理:在数据分析、科学计算等需要大量CPU计算的任务中,多进程可以显著提高计算速度。 8 9 独立任务:需要运行彼此独立的任务(如不同的子进程执行不同的任务),避免相互干扰,提高程序的健壮性和稳定性。
4.1.引入
将上面讲解的登录的按钮示例,适当修改代码,详细代码如下:
1 import sys 2 import time 3 from PyQt5.QtWidgets import * 4 from PyQt5 import uic 5 from threading import Thread 6 7 8 class MyWindow(QWidget): 9 def __init__(self): 10 super().__init__() 11 self.init_ui() 12 13 def init_ui(self): 14 self.ui = uic.loadUi(r"C:\Users\19225\PycharmProjects\test\src\user\static\login.ui") 15 # 提取要操作的控件 16 self.user_name_qwidget = self.ui.lineEdit # 用户名输入框 17 self.password_qwidget = self.ui.lineEdit_2 # 密码输入框 18 self.login_btn = self.ui.pushButton # 登录按钮 19 self.forget_password_btn = self.ui.pushButton_2 # 忘记密码按钮 20 self.textBrowser = self.ui.textBrowser # 文本显示区域 21 # 绑定信号与槽函数 22 self.login_btn.clicked.connect(self.start_login_task) 23 24 def start_login_task(self): 25 """启动登录任务的函数,创建一个新线程来执行登录操作""" 26 login_thread = Thread(target=self.login) 27 login_thread.start() 28 29 def login(self): 30 """登录按钮的槽函数""" 31 user_name = self.user_name_qwidget.text() 32 password = self.password_qwidget.text() 33 for i in range(10): 34 print("正在登录服务器....%d" % (i + 1)) 35 time.sleep(1) 36 if user_name == "admin" and password == "123456": 37 self.textBrowser.setText("欢迎%s" % user_name) 38 self.textBrowser.repaint() 39 else: 40 self.textBrowser.setText("用户名或密码错误....请重试") 41 self.textBrowser.repaint() 42 43 44 if __name__ == '__main__': 45 app = QApplication(sys.argv) 46 w = MyWindow() 47 # 展示窗口 48 w.ui.show() 49 app.exec()