【问题标题】:PyQt5 Calling a static method inside a class' method messes up the execution order of the method's code [duplicate]PyQt5在类的方法中调用静态方法会打乱方法代码的执行顺序[重复]
【发布时间】:2021-11-08 05:24:59
【问题描述】:

在过去的几天里,我一直在使用 PyQt5 为 GUI 构建一个相当简单的程序,但我遇到了以下问题,我似乎找不到解决方案。

这是我的代码的一个过度简化的版本(保持绝对基础以保持简短):

def run_blueprint(file):
    # doing stuff to the file and returning the path to the output file
    return full_path


class Window(QMainWindow, Ui_MainWindow):
    # some variables here

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.connectSignalsSlots()

    def connectSignalsSlots(self):
        self.pushButton.clicked.connect(self.processFiles)

    def processFiles(self):
        self.toggleElements()
        for i, file in enumerate(self.selected_files_directories):
            temp_full_path = run_blueprint(file)
            self.listWidget_3.insertItem(i, temp_full_path)
        self.toggleElements()

    def toggleElements(self):
        self.pushButton_2.setEnabled(not self.pushButton_2.isEnabled())
        self.listWidget.setEnabled(not self.listWidget.isEnabled())


if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec())

假设我从listWidget 中选择了一个项目,并且还有一个文件路径列表存储在selected_files_directories[] 变量中。

如果我按原样运行代码,那么当 processFiles(self) 运行时,我会遇到一些奇怪的行为。由于某种原因,for 循环首先运行,然后是第一个 toggleElements(),然后是第二个 toggleElements()。这导致我想暂时禁用两个元素,直到 for 循环结束一直保持启用状态。

但是,如果我根本不调用 run_blueprint(file) 方法,而是在 for 循环中运行任何其他代码,甚至完全放弃循环,则首先禁用这两个元素,然后运行其余代码并最后它们再次启用。

我认为问题与我在类外调用静态方法有关。您对如何解决此问题有任何想法吗?提前致谢!


编辑:以下是run_blueprint() 函数的简化版本。 该程序的基本功能如下:我选择一个模板文件(.docx) 并输入文件(.pdf)。然后使用docxtplpdfplumber 库,对于每个选定的输入文件,我从中获取特定的文本片段并将它们放在模板文档中并将其保存为.docx 文件。

在以下代码中,假设template_pathbp_path 分别显示模板文件和蓝图文件的路径。

def run_blueprint(file):
    doc = DocxTemplate(template_path) # docxtpl method
    global lns
    lns = export_data(file) # function I made that uses pdfplumber to get all the text from a pdf file

    global context
    context = {}

    with open(bp_path, 'r', encoding='utf8') as f:
        blueprint = f.read() # blueprint contains python code that populates the context variable. I do it this way to easily change between blueprints
    exec(blueprint, globals(), locals()) # I know it's not a good practice to use exec but it's intended for personal use

    doc.render(context) # docxtpl method
    output_path = "D:\\Desktop" # could be any path
    doc.save(output_path) # docxtpl method
    return output_path

蓝图代码通常包含 API 调用,这些调用需要几毫秒来发送响应。除此之外,它主要是regex 用于定位我要查找的文本。

【问题讨论】:

  • 这可能是画布被重绘的问题,而不是执行代码的问题。在 `toggleElemnts() 调用之后尝试 forcing a repaint 看看是否有帮助。
  • @bicarlsen 哇,它确实有效!我刚刚在第一个self.toggleElements() 之后添加了self.repaint(),它现在就像一个魅力。非常感谢!
  • 很高兴听到 :) 请接受答案,以便其他用户知道已找到解决方案。
  • @NikolasP。有没有机会run_blueprint 一个需要一些时间才能完成的功能?知道它的作用将真正帮助我们了解什么是真正的问题,这可能是repaint 并没有真正解决的问题,因为在许多情况下,调用它实际上是一种解决方法,只是“隐藏”实际问题。
  • @musicamante 是的,它确实需要一些时间才能完成。取决于它正在处理的文件。我用来测试的文件大约需要 2 秒才能完成。

标签: python pyqt5


【解决方案1】:

这是重新绘制画布的问题。在toggleElements 调用之后添加repaint 调用。

def processFiles(self):
    self.toggleElements()
    self.repaint()
    
    for i, file in enumerate(self.selected_files_directories):
        temp_full_path = run_blueprint(file)
        self.listWidget_3.insertItem(i, temp_full_path)
    
    self.toggleElements()

根据@musicamante 的 cmets 建议了解更多背景信息并帮助澄清: 重新绘制画布没有问题。问题是该函数阻塞了主线程。考虑到上下文,更好的解决方案是将阻塞任务卸载到QThreadQProcess 以解除对主线程的阻塞。

【讨论】:

  • 不,这不是画布的问题,而是没有将控制权返回给事件循环的函数。因此,您的解决方案仅(部分)解决了绘图问题,但没有解决事件队列:单击“已禁用”小部件仍然可以工作,因为这些事件将被排队并实际接收到 控制已返回到 UI 线程,此时这些小部件将再次启用。
  • @musicamante,很好的进一步解释。你能发布一个解决这两个问题的解决方案吗?
  • 在帖子关闭之前我不能,我已经投票重新打开它。
猜你喜欢
  • 1970-01-01
  • 2015-09-05
  • 1970-01-01
  • 1970-01-01
  • 2011-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多