You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PySide6使用QProcess批量执行SVN命令时界面冻结、输出混杂求助

解决方案

核心思路

基于Qt原生的信号槽异步机制实现串行任务调度,完全移除阻塞式的waitForFinished调用,既保证svn命令按顺序执行、输出不混杂,也不会阻塞GUI事件循环,避免界面冻结。

  • 用队列存储所有待执行的任务,前一个任务执行完成后通过finished信号自动触发下一个任务
  • 全程无阻塞逻辑,主事件循环正常运行,GUI操作不会卡顿

关键修改点

  • 移除所有waitForFinished调用
  • 新增任务队列实现串行调度:wc_queue存储所有待处理的工作副本路径,sub_task_queue存储当前工作副本下待执行的add/rm/commit子任务
  • QProcess绑定为实例属性,避免局部变量被Python垃圾回收导致信号丢失
  • 每个操作的结果处理、下一个任务的触发都通过信号槽绑定实现

修改后完整代码

import os, sys
from PySide6 import QtCore, QtWidgets, QtGui
from PySide6.QtWidgets import QDialog
from app.ui.pages.commit import Ui_Dialog


class Commit_All(QDialog, Ui_Dialog):
    def __init__(self, parent=None, work_path='', qss=''):
        super().__init__(parent=parent)
        self.work_path = work_path
        self.qss = qss
        # 任务队列
        self.wc_queue = [] # 待处理的工作副本队列
        self.sub_task_queue = [] # 当前工作副本的子任务队列
        self.current_wc = None # 当前处理的工作副本
        self.current_process = None # 当前运行的QProcess

        self.setupUi(self)
        self.setStyleSheet(self.qss) 
        self.setWindowIcon(QtGui.QIcon(QtGui.QPixmap(':icon.ico')))
        self.connectSignalsSlots()   
        self.commit_plain.setReadOnly(True) 
        self.finish_btn.setEnabled(False) 
        self.commit_plain.appendPlainText('Starting Commit\n')

        # 初始化工作副本队列
        for wc_name in os.listdir(self.work_path):
            wc_path = os.path.join(self.work_path, wc_name)
            if os.path.isdir(wc_path): # 只处理文件夹
                self.wc_queue.append(wc_path)
        
        # 启动第一个任务
        self.process_next_wc()

    def connectSignalsSlots(self):
        self.finish_btn.clicked.connect(self.close)

    def process_next_wc(self):
        """处理下一个工作副本"""
        if not self.wc_queue:
            # 所有任务完成
            self.message("\n===== 全部项目提交完成 =====")
            self.finish_btn.setEnabled(True)
            return
        
        self.current_wc = self.wc_queue.pop(0)
        self.sub_task_queue.clear()
        self.finish_btn.setEnabled(False)
        # 先执行svn status检查
        self.current_process = QtCore.QProcess(self)
        self.current_process.setProgram("svn")
        self.current_process.setArguments(['st', self.current_wc])
        self.current_process.finished.connect(self.handle_svn_st_finished)
        self.current_process.start()

    def handle_svn_st_finished(self):
        """处理svn status的返回结果"""
        output = bytes(self.current_process.readAll()).decode("latin1")
        self.current_process.deleteLater()
        self.current_process = None

        if not output.strip():
            self.message(f'Project {os.path.basename(self.current_wc)} 无变更,跳过提交\n')
            self.process_next_wc()
            return
        
        # 解析status结果,生成add/rm子任务
        for status in output.splitlines():
            status_flag = status[:1]
            item_path = status[8:].strip()
            if status_flag == '!':
                self.sub_task_queue.append(('rm', item_path))
            elif status_flag == '?':
                self.sub_task_queue.append(('add', item_path))
        
        # 最后添加commit任务
        msg = f'--> auto commit Proj. {os.path.basename(self.current_wc)}'
        self.sub_task_queue.append(('commit', self.current_wc, msg))

        # 开始执行子任务
        self.process_next_sub_task()

    def process_next_sub_task(self):
        """处理当前工作副本的下一个子任务"""
        if not self.sub_task_queue:
            # 当前工作副本所有子任务完成
            self.message(f'\nProject {os.path.basename(self.current_wc)} 提交完成!\n')
            self.process_next_wc()
            return
        
        task = self.sub_task_queue.pop(0)
        task_type = task[0]
        self.current_process = QtCore.QProcess(self)
        self.current_process.readyReadStandardOutput.connect(self.handle_stdout)
        self.current_process.readyReadStandardError.connect(self.handle_stderr)

        if task_type == 'add':
            item_path = task[1]
            self.current_process.setProgram("svn")
            self.current_process.setArguments(['add', item_path, '--force'])
            self.current_process.finished.connect(self.process_next_sub_task)
        elif task_type == 'rm':
            item_path = task[1]
            self.current_process.setProgram("svn")
            self.current_process.setArguments(['rm', item_path])
            self.current_process.finished.connect(self.process_next_sub_task)
        elif task_type == 'commit':
            wc_path, msg = task[1], task[2]
            self.current_process.setProgram("svn")
            self.current_process.setArguments(['ci', wc_path, '-m', msg])
            self.current_process.finished.connect(self.process_next_sub_task)
        
        self.current_process.start()

    def message(self, s):
        self.commit_plain.appendPlainText(s)
    
    def handle_stderr(self):
        data = self.current_process.readAllStandardError()
        stderr = bytes(data).decode("latin1")
        self.message(stderr)

    def handle_stdout(self):
        data = self.current_process.readAllStandardOutput()
        stdout = bytes(data).decode("latin1")
        stdout = stdout.rstrip('\r\n')
        if stdout:
            self.message(stdout)

内容的提问来源于stack exchange,提问作者Christoph

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 16:15:08