PyQt继续停止上传并终止线程 - 卡住了

时间:2016-01-10 16:09:46

标签: multithreading pyqt python-requests

我有一个应用程序,它与某些API通信以上传数据。

它有ExportDialog,它有ProgressBar。

algorythm如下:

  • 在MainWindow中,我选择路径到sql db并单击导出
  • Db数据存储在列表中
  • 使用按钮开始
  • 显示ExportDialog
  • 当我点击开始时,QThread任务并上传开始。
  • 随着开始变为停止,按钮中断会出现
  • 如果我点击停止,则上传过程停止,QThread任务终止,停止变为开始。程序会保存最后一个列表索引的当前值,因此当我再次单击开始时,QThread任务将启动并继续上传。
  • 如果点击中断,系统会显示StatisticWindow,显示结果

有时,互联网连接可能会丢失,所以我有尝试...除了块,它会捕获请求的ConnectionError。 我的问题是,如果互联网连接丢失,有时会卡住。

让我们在代码中展示它。 的 ExportDialog

class ExportDialog(QtGui.QDialog):

    def __init__(self, table, api):

        super(ExportDialog, self).__init__()
        self.table = table
        self.stat = list()
        self.value = 0
        self.task = WorkThread(self.table)
        self.length = len(self.table)
        self.api = api
        self.initUI()
        self.retranslateUI()
        self.initActions()

WorkThread类

class WorkThread(QtCore.QThread):

    punched = QtCore.pyqtSignal(dict)

    def __init__(self, table, index=0):
        super(WorkThread, self).__init__()
        self.table = table[index:]

    def __del__(self):
        self.wait()

    def run(self):
        for i in self.table:
            self.punched.emit(i)
            time.sleep(1)

    def stop(self):
        self.terminate()

ExportDialog.initActions

def initActions(self):
        self.startButton.clicked.connect(self.changeTask)
        self.breakButton.clicked.connect(self.task.stop)
        self.breakButton.clicked.connect(self.close)

ExportDialog.changeTask

def changeTask(self):
    if self.sender().text() == "Start":
        self.startButton.setText(self.tr("Stop"))
        self.breakButton.show()
        self.setWindowTitle(self.tr("Processing..."))
        if self.value > 0:
            self.task = WorkThread(self.table, self.value)
        self.task.punched.connect(self.onProgress)
        self.task.start()
    else:
        self.task.stop()
        self.startButton.setText(self.tr("Start"))
        self.breakButton.hide()

ExportDialog.onProgress

def onProgress(self, i):
    try:
        row = i
        word = row.get('word').lower()
        context = row.get('context', '')
        translate = self.api.get_translate(word)
        self.api.add_word(translate['word'],
                                translate['tword'],
                                context)
        if translate['is_exist']:
            result = "Exist"
        else:
            if translate['tword'] == "No translation":
                result = "No translation"
            else:
                result = "New"
            self.api.substractMeatballs()
            meatballs = "Meatballs: {}".format(self.api.meatballs)
            self.meatballs_label.setText(meatballs)

        self.stat.append({"word": word,
                          "result": result,
                          "tword": translate['tword']})

    except ConnectionError:
        self.startButton.click()
        # this is where WarningDialog should be shown
        # but in my case it happens 50/50
        warning = WarningDialog("No Internet Connection")
        warning.exec_()
        return

api - API实例,其中包含:

  • 身份验证方法 - 通过请求
  • 进行简单授权
  • add_word 方法 - 通过请求的简单POST请求
  • get_translate 方法 - 通过请求进行简单的GET请求

__

如果我手动点击停止然后开始,一切顺利。
如果我手动放弃互联网连接,就会发生奇怪的事情。

  • 有时,它按预期运行 - WarningDialog显示,任务停止。我可以继续离开的地方
  • 有时它会卡住 - 没有任何反应,MainWindow被冻结,ExportDialog不负责任 - 无法点击停止中断
  • 有时会打开超过100个WarningDialog实例并且整个系统卡住了

我哪里错了?

2 个答案:

答案 0 :(得分:1)

每次调用changeTask时,task.punched都会再次连接onProgress个广告位。这意味着将在每个task.punched信号上多次调用插槽。

如果在onProgress)广告位内发生错误,则会点击startButton。一开始一切正常:按钮的文本是“停止”,因此它只是停止任务。但是插槽多次发射,对吧?第二次是不同的:按钮的文字现在是“开始”,它再次连接task.punchedonProgress并启动任务,最终会调用onProgress ...见面无限递归。

def changeTask(self):
    if self.sender().text() == "Start":
        [...]
        self.task.punched.connect(self.onProgress) # <--
        self.task.start()
    else:
        [...]

def onProgress(self, i):
    try:
        [...]
    except ConnectionError:
        self.startButton.click() # <--

        warning = QtGui.QDialog()
        warning.exec_()
        return

сonclusion:

  • 要解决此问题,请仅将信号连接到插槽一次 (将self.task.punched.connect(self.onProgress)changeTask移至__init__
  • 对于许多目的而言,拥有一个按钮可能不是一个好主意。创建两个按钮(用于启动和停止)并仅显示其中一个按钮会不会更好?

答案 1 :(得分:0)

稍微更改了代码,现在它可以正常工作

<强> WorkThread

def __init__(self):
    super(WorkThread, self).__init__()

# new method
def getData(self, table, index=0):
        self.table = table[index:]

所以,在ExportDialog init

self.task = WorkThread()
self.task.getData(table)

打孔已发送至 initActions

def initActions(self):
        self.startButton.clicked.connect(self.changeTask)
        self.breakButton.clicked.connect(self.task.stop)
        self.breakButton.clicked.connect(self.close)
        self.task.punched.connect(self.onProgress) # <-- Here we go!

和changeTask

def changeTask(self):
    if self.sender().text() == "Start":
        self.startButton.setText(self.tr("Stop"))
        self.breakButton.show()
        self.setWindowTitle(self.tr("Processing..."))
        if self.value > 0:
            self.task.getData(self.table, self.value)
        self.task.start()
    else:
        self.task.stop()
        self.startButton.setText(self.tr("Start"))
        self.breakButton.hide()

感谢 Alexander Lutsenko 给我正确的方式