添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
气势凌人的伤疤  ·  C# ...·  6 月前    · 
热情的便当  ·  [02] ...·  1 年前    · 
暗恋学妹的煎饼  ·  java - SpEL (Spring) ...·  1 年前    · 

在制作巡检工具页面时,在执行处理时,出现界面无影响。

pyside6多线程Qthread的实现有两种方法:

方法一:实例化一个thread类,继承Qthread类,通过重写Qthread的run方法来实现

方法二:通过继承Qobject类,通过movetothread将需要处理的函数加入到一个新的子线程中进行处理。

如果不使用多线程,在程序点击开始按钮后,程序会处于无影响的状态,直至所有的巡检任务执行完后,将数据返回至主界面才会解除。为了避免这种情况的发生,必须需要使用多线程进行处理。使用多线程的核心代码如下:

self.my_thread = QThread()      # 实例化一个子线程
self.obj = MyObject()   # 实例化子线程巡检
self.obj.moveToThread(self.my_thread)   # 将子线程移至子线程中处理
self.my_thread.started.connect(self.obj.ssh_to_host)
self.my_thread.start()      # 启动子线程

实现步骤:

  1. 实例化一个线程self.my_thread
  2. 实例化执行任务类MyObject(),MyObject()为执行巡检任务工作的类
  3. 将执行任务的实例移至子线程中
  4. 将新产生的线程连接至执行任务的子线程的槽函数
  5. 启动子线程

程序实现思路:

程序通过两个类来实现,一个类用于页面显示(MainForm),一个类用于处理巡检任务功能(MyObject)。页面显示类,将获取的主机信息通过信号host_signal与处理巡检任务的槽函数建立连接,并将信息发送过去;巡检任务类接收信息并处理完后,将命令执行结果通过信号(update_signal,end_signal)传回显示在文本框中。

程序完整代码如下:

# 测试多线程moveToThread 动态传递参数
import sys
import time
from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *
import paramiko
# 任务处理类
class MyObject(QObject):
    update_signal = Signal(str)     # 发送执行命令结果给主进程用于返回显示至界面
    end_signal = Signal(str)        # 处理完任务发送信息
    def __init__(self,parent=None):
        super(MyObject, self).__init__(parent)
        self.ip = None
    # 执行巡检
    def ssh_to_host(self):
        print('执行任务中的Ip:',self.ip)
        for i in self.ip:
                self.trans = paramiko.Transport((i, 22))  # 使用Transport方式连接
                self.trans.start_client(timeout=0.5)
                # paramiko.util.log_to_file('paramiko-log.log')  # 记录执行日志
                # 用户名密码方式
                self.trans.auth_timeout = 5
                self.trans.auth_password(username='root', password='123456', fallback=True)
            except Exception as e:
                print('连接错误:', e)
            else:
                print('连接主机:{}:{}   ---> 正常'.format('host-70', i))
                # 打开一个通道
                self.channel = self.trans.open_session()
                self.channel.settimeout(100)
                # 获取一个终端
                self.channel.get_pty()
                # 激活器
                self.channel.invoke_shell()
                # 根据配置文件定义command项执行脚本
                cmd_file = (('date\r\nhostname\r\nuname\r\nifconfig',),)
                # print('cmd_file', cmd_file)
                if len(cmd_file) > 0:
                    # print('cmd_file',cmd_file)
                    single_cmd = cmd_file[0][0].split('\r\n')  # 提取配置文件中脚本命令
                    # print('single_cmd',single_cmd)
                    for c in single_cmd:  # 遍历每个命令
                        # print('命令c:', c,type(c))
                        # 发送要执行的命令
                        time.sleep(1)
                        self.channel.send(c + '\n')  # 在每一个命令后加上换行
                        # self.channel.send(c)  # 在每一个命令后加上换行
                        end_symbol = ('# ', '$ ', '$', '> ', '>')  # 设置我们定义的结束符
                        # 将命令执行结果保存到display_result
                        display_result = ''
                        # # 回显很长的命令可能执行较久,通过循环分批次取回回显
                        time.sleep(0.1)
                        while True:
                            result = self.channel.recv(256)
                                result = result.decode('utf-8')
                                # logging.warning('使用UTF-8编码!')
                            except:
                                result = result.decode('gb18030')
                                # logging.warning('使用gb18030编码!')
                            display_result += result  # 输出到日志显示窗口
                            if result.endswith(end_symbol):
                                break
                        self.update_signal.emit(display_result)   # 发送命令返回至主窗口结果
                    print()
                    print('=' * 80)
                else:
                    print('没有配置相关命令!,请配置检查脚本命令后再操作!!')
                    return
            finally:
                self.channel.close()
                self.trans.close()
        self.end_signal.emit('断开SSH连接')  # 发送巡检命令执行完信号
    # 接收主界面线程发送的主机IP信息
    def accpet_hostsinfo(self,ip):
        self.ip = ip
class MainForm(QWidget):
    host_signal = Signal(list)      # 定义主机信息信号
    def __init__(self, parent=None):
        super(MainForm, self).__init__(parent)
        self.setWindowTitle('多线程测试-定时发送消息')
        self.resize(800, 600)
        layout = QVBoxLayout()
        self.start_btu = QPushButton('开始')
        layout.addWidget(self.start_btu)
        self.text = QTextEdit(self)
        layout.addWidget(self.text)
        self.setLayout(layout)
        self.ip = None
        self.my_thread = QThread()      # 实例化一个子线程
        self.start_btu.clicked.connect(self.send_hosts_to_child)   # 将开始按钮的点击信号连接至发送主机信息给子线程的槽函数self.send_hosts_to_child
        self.start_btu.clicked.connect(self.do_worker)      # 将开始按钮的点击事件信号连接至执行任务的槽函数
    # 执行巡检任务命令发送
    def do_worker(self):
        print('开始运行程序。。')
        print('当前线程:', QThread.currentThread(), self.my_thread.isRunning())
        self.text.clear()
        self.obj = MyObject()   # 实例化子线程巡检
        self.host_signal.connect(self.obj.accpet_hostsinfo(self.ip))        # 主机信号连接至任务处理的子线程获取主机信息槽函数
        self.obj.update_signal.connect(self.update_text)    # 任务处理子线程显示巡检结果信号连接至主线程更新槽函数self.update_text)
        self.obj.end_signal.connect(self.stop)      # 任务子线程执行巡检结果完毕信号连接至主线程关闭子线程槽函数self.stop
        self.obj.moveToThread(self.my_thread)   # 将子线程移至子线程中处理
        self.my_thread.started.connect(self.obj.ssh_to_host)
        self.my_thread.start()      # 启动子线程
    # 传递主机IP信息给任务处理子线程
    def send_hosts_to_child(self):
        self.ip = ['192.168.1.70','192.168.1.61']   
        self.host_signal.emit(self.ip)      # 发送主机信息self.ip
    # 更新文本框显示内容
    def update_text(self,text):
        cursor = self.text.textCursor()
        self.text.moveCursor(cursor.End)  # 将光标移动到最后
        self.text.insertPlainText(text)     # 插入文本
    # 关闭线程
    def stop(self):
        print('关闭当前线程')
        self.my_thread.quit()       # 退出子线程
        self.my_thread.wait()
if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = MainForm()
    win.show()
    sys.exit(app.exec())

以上是巡检任务部分的核心功能,基本功能已经实现,其它优化即可。

注:在子进程执行完后,需要手动退出线程,否则再次点击运行时,不能执行,将异常退出。
# 关闭线程
    def stop(self):
        print('关闭当前线程')
        self.my_thread.quit()       # 退出子线程
        self.my_thread.wait()
```python from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton from PySide6.QtCore import QThread, Signal class WorkerThread(QThread): # 自定义信号,用于在线程完成时发出信号 finished = Signal() def run(self): # 在这里执行耗时的操作 # ... # 操作完成后发出finished信号 self.finished.emit() class MainWindow(QMainWindow): def __init__(self): super().__init__() self.button = QPushButton("Start Thread", self) self.button.clicked.connect(self.start_thread) def start_thread(self): self.button.setEnabled(False) # 创建并启动新的线程 self.thread = WorkerThread() self.thread.finished.connect(self.thread_finished) self.thread.start() def thread_finished(self): # 线程完成后的处理逻辑 self.button.setEnabled(True) if __name__ == "__main__": app = QApplication([]) window = MainWindow() window.show() app.exec() 在上面的示例中,`WorkerThread`类继承自`QThread`,并在`run`方法中执行耗时的操作。当操作完成后,通过发出自定义的`finished`信号,通知主线程操作已经完成。在`MainWindow`类中,当用户点击按钮时,会创建并启动一个新的线程,并在线程完成后进行相应的处理。 需要注意的是,PySide6的GUI元素只能在主线程中操作,所以如果需要在多线程中更新UI,通常需要使用信号和槽机制来实现。在上面的示例中,我们通过自定义信号`finished`和槽函数`thread_finished`来实现线程完成后的UI更新。 CSDN-Ada助手: 恭喜您写了第16篇博客!标题中提到了RHEL的bond与team网卡绑定实践,听起来非常有趣和实用。我对这个主题很感兴趣,因为我一直在寻找有关网络配置和优化的内容。您的文章肯定会对我和其他读者有所帮助。 在下一篇博客中,我希望您能够进一步探讨这个主题,比如分享一些实际的使用案例或者提供一些调优建议。我相信您的经验和知识一定能够为我们带来更多的启发。感谢您一直以来的辛勤创作,期待您的下一篇博客! 从百度汉语上爬取古诗 CSDN-Ada助手: 恭喜您撰写了第17篇博客,标题为“从百度汉语上爬取古诗”!您的努力和持续创作真是令人钦佩。通过这篇博客,您向读者展示了一种非常有趣且实用的方法,在百度汉语上爬取古诗。这对于研究古诗、传播中国文化等方面都具有重要意义。 在下一步的创作中,我建议您可以进一步探索这个主题,例如通过分析这些古诗的特点、风格等,深入挖掘其中蕴含的文化内涵,以及它们对于现代社会的影响等。这样的创作将为读者提供更多的知识和启发。 再次恭喜您的成果,期待您未来更多精彩的博客!请继续保持谦虚的态度,不断提升自己的创作水平。 python 下向sql语句中参数化方式传入in值方法 小小鱼er: 请问 要是列表中只有一个元素,tuple后会加,号,变成('eee',)这种形式,怎么解决呢 利用Python获取带合并单元格的表格数据 xiubo1288: 拆解每行的合并单元格的代码是错误的 Zabbix6 安装配置(Oracle Linux 8.4) 代码中徜徉的文人: 您这个第六步初始化数据库里面没有server.sql.gz文件呢。