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

PySide6线程结束后更新GUI时程序无响应崩溃求助

PySide6线程执行后更新GUI导致程序崩溃问题

问题描述

使用PySide6开发GUI程序,界面通过Qt Designer设计并单独存放。线程执行完成后尝试更新GUI时,程序无响应直接关闭,运行过程无报错信息。

主线程代码

class interact(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self,  *args, **kwargs):
        super(interact, self).__init__( *args, **kwargs)
        self.setupUi(self)
        self.scaleX = 800
        self.scaleY = 800
        self.index = 0
        self.newIndex = 0
        self.translatedFiles = []
        self.files = []
        self.isClicked = False
        self.upload.clicked.connect(self.upload1)#connecting button to self.upload1
        self.upload.clicked.connect(self.change1)
        self.rightArrow.clicked.connect(self.moveRight) #button
        self.leftArrow.clicked.connect(self.moveLeft) #button
        self.translate.clicked.connect(self.translate1) #button
        self.translate.clicked.connect(self.change)
        
    
    def upload1(self):
        filenames, _ =  QFileDialog.getOpenFileNames(
            None,
            "QFileDialog.getOpenFileNames()",
            "",
            "Image files (*.jpg *.png)"
        )
        for file in filenames:
            self.files.append(file)
        self.showImage()

    def moveRight(self):
        if self.isClicked == True:
            if self.newIndex >= len(self.translatedFiles)-1:
                self.newIndex = len(self.translatedFiles)-1
            else:
                self.newIndex += 1
        else:
            if self.index >= len(self.files)-1:
                self.index = len(self.files)-1
            else:
                self.index += 1
        self.showImage()
        
    def moveLeft(self):
        if self.isClicked == True:
            if self.newIndex <= 0:
                self.newIndex = 0
            else:
                self.newIndex -= 1
        else:
            if self.index <= 0:
                self.index = 0
            else:
                self.index -= 1
        self.showImage()

    def showImage(self):
        if self.isClicked == True:
            if not self.translatedFiles:
                return
            im = self.translatedFiles[self.newIndex]
            im = im.convert("RGB")
            data = im.tobytes("raw","RGB")
            qim = QImage(data, im.size[0], im.size[1], QImage.Format_RGB888)
            pix = QPixmap(qim)
            self.Image.setPixmap(pix)
            print("change")
        else:
            if not self.files:
                return
            pix = QPixmap(self.files[self.index])
            self.Image.setPixmap(pix)
            print(self.files[self.index])
            print("KC")

    def afterThread(self, s):
        myarray = np.array(s)
        img = im.fromarray(myarray)
        self.translatedFiles.append(img)
        # 更新索引到最新添加的图片
        self.newIndex = len(self.translatedFiles) - 1
        print("THREAD COMPLETE!")
        
        
    def change(self):
        self.isClicked = True

    def change1(self):
        self.isClicked = False

    def translate1(self):
        self.thread = QThread()
        self.worker = Translate(self.files[self.index])
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.worker.finished.connect(self.afterThread)
        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.worker.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)
        self.thread.finished.connect(self.showImage)
        self.thread.start()



if __name__ == "__main__":
    import sys
    app =QtWidgets.QApplication(sys.argv)
    w = interact()
    w.show()
    sys.exit(app.exec())

线程代码

import cv2
import numpy as np
from PIL import Image
import os
import time
from PySide6.QtCore import QObject, Signal
from MangaOcr import MangaOcr
import easyocr
import ts # 假设这是翻译模块

class Translate(QObject):
    # 修正信号类型:传递numpy数组而非list
    finished = Signal(np.ndarray)
    
    def __init__(self, img_path):
        super(Translate, self).__init__()
        self.img_path = img_path
        self.mocr = MangaOcr()

    def chronos(func):
        def get_time(*args, **kawrgs):
            start = time.time()
            x = func(*args, **kawrgs)
            end = time.time()
            print("{} took {} seconds".format(func, (end-start)))
            return x
        return get_time

    @chronos
    def get_text(self, image):
        reader = easyocr.Reader(['ja'], gpu=False) 
        result = reader.readtext(image, paragraph=True)
        myDict = {}
        for (bbox, text) in result: 
            (tl, tr, br, bl) = bbox
            tl = (int(tl[0]), int(tl[1]))
            tr = (int(tr[0]), int(tr[1]))
            br = (int(br[0]), int(br[1]))
            bl = (int(bl[0]), int(bl[1]))
            myDict[str(bl)] = ([tl , br ])
        return myDict
    
    @chronos
    def get_japanese(self, diction):
        newList = {}
        et = {}
        ts_dict = {}
        directory = os.path.join(os.getcwd(), 'MangaTranslation', 'cropText')
        os.makedirs(directory, exist_ok=True) # 确保目录存在
        for x in diction:
            cropped_image = self.image[diction[x][0][1]:diction[x][1][1], diction[x][0][0]:diction[x][1][0]]
            save_path = os.path.join(directory, f"{x}.jpg")
            if not os.path.exists(save_path):
                cv2.imwrite(save_path, cropped_image)
            et[str(x)] = save_path
        # 只遍历当前目录的文件,避免递归遍历子目录
        for x in os.listdir(directory):
            file_path = os.path.join(directory, x)
            if not os.path.isfile(file_path):
                continue
            try:
                img = Image.open(file_path.strip())
                text = self.mocr(img)
                ts_dict[file_path.strip()] = text
            except Exception as e:
                print(f"处理图片失败: {e}")
        # 优化匹配逻辑,避免双重循环
        for coor, direct in et.items():
            if direct in ts_dict:
                newList[coor] = ts_dict[direct]
        return newList
    
    @chronos
    def translate(self, original):
        for jap in original:
            original[jap] = str(ts.bing(original[jap]))
        return original

    def segment(self, list1):
        for line in list1:
            chars = list(str(list1[line]))
            s = ''
            count = 0
            for c in chars:
                if c == " ":
                    count += 1
                if count == 3:
                    s += "\n"
                    count = 0
                else:
                    s += c
            list1[line] = s
        return list1

    def write(self, img, dict1, list1):
        fontSize = .5
        # 复制原图像避免修改原始数据
        image = img.copy()
        for value in dict1:
            cv2.rectangle(image, dict1[value][0], dict1[value][1], (0, 255, 255), 2)
            # 假设add_text_to_image是自定义函数,确保它返回修改后的图像
            image = add_text_to_image(
                image,
                list1[value],
                font_color_rgb=(255, 0, 0),
                top_left_xy=(dict1[value][0][0], dict1[value][1][1]),
                font_scale= fontSize,
                font_face=cv2.FONT_HERSHEY_DUPLEX
                )
        return image

    def run(self):
        # 将图片读取逻辑移到run方法,避免阻塞主线程
        self.img1 = cv2.imread(self.img_path)
        self.image = cv2.cvtColor(self.img1, cv2.COLOR_BGR2RGB)
        
        gotten_text = self.get_text(self.image)
        finalText = self.get_japanese(gotten_text)
        newList = self.translate(finalText)
        addNewLine = self.segment(newList)
        final = self.write(self.image, gotten_text, addNewLine)
        self.finished.emit(final)

运行日志

<function Translate.get_text at 0x0000013C5D880B80> took 10.577515602111816 seconds
<function Translate.get_japanese at 0x0000013C5DA03E50> took 6.2078752517700195 seconds
<function Translate.translate at 0x0000013C5DA03EE0> took 8.287482023239136 seconds
THREAD COMPLETE!

问题解决要点

  1. 修正信号类型不匹配:原代码中finished信号定义为Signal(list),但实际发射的是numpy数组,类型不匹配会触发未捕获异常导致程序崩溃,改为Signal(np.ndarray)即可。

  2. 将IO操作移到线程中:原Translate类的__init__方法中直接读取图片,这部分代码会在主线程执行,可能阻塞GUI;将图片读取和转换逻辑移到run方法,确保在子线程中执行。

  3. 添加空值判断:在showImage方法中增加对translatedFiles和files的空值判断,避免索引越界错误。

  4. 更新显示索引:线程完成后,将newIndex设置为translatedFiles的最后一个元素索引,确保显示最新生成的翻译图片。

  5. 优化文件处理逻辑:在get_japanese方法中,使用os.makedirs确保目录存在,避免递归遍历子目录,优化匹配逻辑减少双重循环,同时添加异常捕获的具体信息便于调试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 10:40:58