"QCoreApplication::exec: The event loop is already running" except when stopping code at sys.exit(app.exec_())

huangapple go评论92阅读模式
英文:

"QCoreApplication::exec: The event loop is already running" except when stopping code at sys.exit(app.exec_())

问题

抱歉,你提供的内容包含了一些代码,但你希望我只提供翻译。以下是你提供的问题的翻译部分:

我是新手,对Qt和PyQt不太熟悉,我正在尝试将由其他人编写的复杂文件解析库连接到一个简单的GUI。当我点击“处理数据”按钮时,我反复遇到以下错误消息:

```bash
QCoreApplication::exec: 事件循环已经在运行
QCoreApplication::exec: 事件循环已经在运行
QCoreApplication::exec: 事件循环已经在运行
QCoreApplication::exec: 事件循环已经在运行
QCoreApplication::exec: 事件循环已经在运行
...

我注意到当我调试脚本时,如果我在行sys.exit(app.exec_())上设置断点,并且只在选择数据后点击“继续”,则不会出现此问题。在这种特定情况下,如果我这样做一次并按“继续”,程序将在以后的所有尝试中都正常工作以加载文件和文件夹。

以下是相关的代码:

import os
import sys
import traceback
import pandas as pd
import numpy as np
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QFileDialog, QTextEdit)

def ParsingFunction(directory=None, files=[], d_dataAlreadyIn=None):
# ...

# Import/outport function, mostly used for loading the .med files
from DC_Lib import DC_IO_lib 
from DC_Lib import DC_CV_Processing 
from DC_Lib import DC_IV_Processing

class DataProcessingApp(QWidget):
    def __init__(self):
        super().__init__()
        # ...
        self.process_button = QPushButton("处理数据")
        self.process_button.clicked.connect(self.process_data)
        self.layout.addWidget(self.process_button)
        # ...

    def process_data(self):
        if self.directory or self.files:
            self.text_edit.append("正在处理数据...")
            if self.directory:
                self.data = ParsingFunction(directory=self.directory)
            else:
                self.data = ParsingFunction(files=self.files)

            self.text_edit.append("数据处理完成。")
        else:
            self.text_edit.append("请选择要处理的目录或文件。")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = DataProcessingApp()
    window.show()
    sys.exit(app.exec_())

如果问题太模糊,请告诉我,但我认为在调试期间使用exec_()方法的这种行为可能会对具有更多Qt经验的人有所启发。对于如何解决此问题的任何见解或建议将不胜感激。

谢谢!


更新:为了提高可重复性,以下是`ParsingFunction`及其子函数的内容。我不会添加更多的代码,因为我相当确定问题出现在`GetMeDopingType()`中的`input()`调用:

```python
while True:
   inp = str(input())
   if inp.strip()[0].upper() == "C":
      C_IO.Abort()
   if inp.strip()[0].lower() == 'n' or inp.strip()[0].lower() == 'p':
      break
   GLOBAL_d_Dopingtypes[wafername] = inp.strip()[0].lower()

当我硬编码inp时,我不会得到QCoreApplication::exec。有关为什么不会出现的任何想法吗?这个答案听起来与我的问题有关,但我还没有能够将其转化为对我的情况的修复。

这是完整的代码:

def ParsingFunction(directory=None, files=[], d_dataAlreadyIn=None):
    AllData = {}
    if d_dataAlreadyIn is not None:
        AllData = d_dataAlreadyIn
    WarnCount = 0

    if directory is not None:
        for root, dirs, files in os.walk(directory):
            for filename in files:
                WorkMagic_SubFunction(C_IO.EndInSlash(root), filename, AllData)
                WarnCount += 1
                if WarnCount % 100 == 0:
                    print(filename)
                if WarnCount > 10000:
                    print("警告:" + str(WarnCount) + "个文件已处理,可能表明存在无限循环!")
    else:
        for file in files:
            root = os.path.dirname(file)
            filename = os.path.basename(file)
            WorkMagic_SubFunction(C_IO.EndInSlash(root), filename, AllData)
            WarnCount += 1
            if WarnCount % 100 == 0:
                print(filename)
            if WarnCount > 10000:
                print("警告:" + str(WarnCount) + "个文件已处理,可能表明存在无限循环!")

    return AllData

# ...
# 其他函数和代码

# 导致问题的函数
def GetMeDopingType(wafername, techinfo):
    global GLOBAL_d_Dopingtypes
    if "SiP" in wafername:
        return "p"
    if not wafername in GLOBAL_d_Dopingtypes:
        if techinfo is not None and "doping" in techinfo:
            GLOBAL_d_Dopingtypes[wafername] = techinfo["doping"].strip()[0].lower()
        else:
            print("请提供晶圆的掺杂类型:" + str(wafername) + " [n/p]: ")
            while True:
                inp = str(input())
                if inp.strip()[0].upper() == "C":
                    C_IO.Abort()
                if inp.strip()[0].lower() == 'n' or inp.strip()[0].lower() == 'p':
                    break
            GLOBAL_d_Dopingtypes[wafername] = inp.strip()[0].lower()
    return GLOBAL_d_Dopingtypes[wafername]

# ...
# 其他函数和代码

关于我正在使用的调试信息,我在虚拟环境中使用Python 3.7.4,因为项目的一部分依赖于PySide2。

英文:

I'm new to Qt and PyQt and I'm trying to connect an elaborate file parsing library written by someone else to a simple GUI. When I click on the "Process Data" button, I encounter the following error message repeatedly:

QCoreApplication::exec: The event loop is already running
QCoreApplication::exec: The event loop is already running
QCoreApplication::exec: The event loop is already running
QCoreApplication::exec: The event loop is already running
QCoreApplication::exec: The event loop is already running
...

I have noticed that when I debug the script, the issue doesn't occur if I put a breakpoint on the line sys.exit(app.exec_()) and only click on "Resume" after selecting the data. In this specific case, if I do this once and press "Resume" the program works in all subsequent attempts to load file(s) and folders.

Here is the relevant code:

import os
import sys
import traceback
import pandas as pd
import numpy as np
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QFileDialog, QTextEdit)

def ParsingFunction(directory=None, files=[], d_dataAlreadyIn=None):
# ...

# Import/outport function, mostly used for loading the .med files
from DC_Lib import DC_IO_lib 
from DC_Lib import DC_CV_Processing 
from DC_Lib import DC_IV_Processing

class DataProcessingApp(QWidget):
    def __init__(self):
        super().__init__()
        # ...
        self.process_button = QPushButton("Process Data")
        self.process_button.clicked.connect(self.process_data)
        self.layout.addWidget(self.process_button)
        # ...

    def process_data(self):
        if self.directory or self.files:
            self.text_edit.append("Processing data...")
            if self.directory:
                self.data = ParsingFunction(directory=self.directory)
            else:
                self.data = ParsingFunction(files=self.files)

            self.text_edit.append("Data processing complete.")
        else:
            self.text_edit.append("Please select a directory or files to process.")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = DataProcessingApp()
    window.show()
    sys.exit(app.exec_())

If the question is too vague let me know, but I think this behavior with the exec_() method during debugging might ring a bell to someone with more experience with Qt. Any insights or suggestions on how to resolve this issue would be greatly appreciated.

Thank you!

[update]

To improve reproducibility here are the contents of the ParsingFunction and its subfunctions. I won't add more code because I'm pretty sure the problem is with the input() call in GetMeDopingType():

while True:
   inp = str(input())
   if inp.strip()[0].upper() == "C":
      C_IO.Abort()
   if inp.strip()[0].lower() == 'n' or inp.strip()[0].lower() == 'p':
      break
   GLOBAL_d_Dopingtypes[wafername] = inp.strip()[0].lower()

When I hardcode inp, I don't get the QCoreApplication::exec. Any ideas why not? This answer sounds like it has to do with my issue, but I haven't been able to translate into a fix for my case.

Here's the full code:

def ParsingFunction(directory=None, files=[], d_dataAlreadyIn=None):
    AllData = {}
    if d_dataAlreadyIn is not None:
        AllData = d_dataAlreadyIn
    WarnCount = 0

    if directory is not None:
        for root, dirs, files in os.walk(directory):
            for filename in files:
                WorkMagic_SubFunction(C_IO.EndInSlash(root), filename, AllData)
                WarnCount += 1
                if WarnCount % 100 == 0:
                    print(filename)
                if WarnCount > 10000:
                    print("Warning: " + str(WarnCount) + " files processed, could indicate an infinite loop!")
    else:
        for file in files:
            root = os.path.dirname(file)
            filename = os.path.basename(file)
            WorkMagic_SubFunction(C_IO.EndInSlash(root), filename, AllData)
            WarnCount += 1
            if WarnCount % 100 == 0:
                print(filename)
            if WarnCount > 10000:
                print("Warning: " + str(WarnCount) + " files processed, could indicate an infinite loop!")

    return AllData


def WorkMagic_SubFunction(directory, filename, AllData):
    global DotArea
    global ExtentionToLookFor
    global INFER_PMA_FROM_FOLDER

    extention = filename.split('.')[-1]
    if extention == ExtentionToLookFor:
        tmp = C_IO.ReadInMedFile(directory + filename, ForceReadSecondDataSection=HasToHandleBTI)
        mestype = FigureOutWhatThisIs(tmp)
        if 'SiP' in filename:
            tmp["techinfo"]["doping"] = 'P'
        if mestype == "DropThisOne":
            return AllData
        if INFER_PMA_FROM_FOLDER and "_PMA" in directory:
            tmp["techinfo"]["wafer"] = tmp["techinfo"]["wafer"] + "A"
        Device_ID = tmp["techinfo"]["testchip"] + "-" + tmp["techinfo"]["testdevice"]
        if not "DotArea" in tmp["techinfo"]:
            guessarr = FilenameToArea(filename)
            if guessarr is None:
                tmp["techinfo"]["DotArea"] = str(DotArea)
            else:
                tmp["techinfo"]["DotArea"] = str(guessarr)
        else:
            tmp["techinfo"]["DotArea"] = str(tmp["techinfo"]["DotArea"])
        tmp["techinfo"]["ScriptName"] = str(SCRIPTNAME)
        tmp["techinfo"]["ScriptVersion"] = str(VERSION)
        if "devicemap" in tmp["techinfo"]:
            tmp["techinfo"]["devicemap"] = tmp["techinfo"]["devicemap"].replace(r"/imec/other/drec/medusa", r"\\unix\drec\medusa")
        if "chipmap" in tmp["techinfo"]:
            tmp["techinfo"]["chipmap"] = tmp["techinfo"]["chipmap"].replace(r"/imec/other/drec/medusa", r"\\unix\drec\medusa")
        key = tmp["techinfo"]["wafer"] + "___###____" + tmp["techinfo"]["chuck_temperature"]
        if not key in AllData:
            AllData[key] = {}
        if not mestype in AllData[key]:
            AllData[key][mestype] = {}
        if not Device_ID in AllData[key][mestype]:
            AllData[key][mestype][Device_ID] = []
        finalStruct = None
        if not (mestype == "NBTI" or mestype == "PBTI"):
            if HasToHandleBTI:
                techinfo = tmp["techinfo"]

                # call to the input()
                dop = GetMeDopingType(techinfo["wafer"], techinfo)

                techinfo['doping'] = dop
                tmp = C_IO.ReadInMedFile(directory + filename, ForceReadSecondDataSection=False)
                tmp["techinfo"] = techinfo
        if mestype == "MFCV":
            finalStruct = tmp
        elif mestype == "Hys":
            TheStruct = C_CV.CV_HYS_Curve(SampleName=tmp["techinfo"]["wafer"], DopingType=GetMeDopingType(tmp["techinfo"]["wafer"], tmp["techinfo"]), Temperature_K=300)
            TheStruct.Load___read_in_Med_structure(tmp)
            TheStruct.do_areanormalisation(1.0)
            finalStruct = TheStruct
        elif mestype == "NBTI" or mestype == "PBTI":
            TheStruct = C_CV.SFCV()
            TheStruct.Load___read_in_Med_Structure(tmp)
            TheStruct.do_areanormalisation(1.0)
            finalStruct = TheStruct
        elif mestype == "IgVg":
            TheStruct = C_IV.SFIV()
            TheStruct.Load___read_in_Med_Structure(tmp)
            finalStruct = TheStruct
        elif mestype == "CET":
            TheStruct = C_CV.SFCV()
            TheStruct.Load___read_in_Med_Structure(tmp)
            finalStruct = TheStruct
        else:
            print("Mestype not supported! '" + str(mestype) + "' in file:" + directory + filename)
        if not finalStruct is None:
            if not type(finalStruct) is dict:
                dop = GetMeDopingType(tmp["techinfo"]["wafer"], tmp["techinfo"])
                finalStruct._header['doping'] = dop
            AllData[key][mestype][Device_ID].append(finalStruct)
    return AllData

# The function I think is causing the problem
def GetMeDopingType(wafername, techinfo):
    global GLOBAL_d_Dopingtypes
    if "SiP" in wafername:
        return "p"
    if not wafername in GLOBAL_d_Dopingtypes:
        if techinfo is not None and "doping" in techinfo:
            GLOBAL_d_Dopingtypes[wafername] = techinfo["doping"].strip()[0].lower()
        else:
            print("Please prodide doping type for wafer: " + str(wafername) + " [n/p]: ")
            while True:
                inp = str(input())
                if inp.strip()[0].upper() == "C":
                    C_IO.Abort()
                if inp.strip()[0].lower() == 'n' or inp.strip()[0].lower() == 'p':
                    break
            GLOBAL_d_Dopingtypes[wafername] = inp.strip()[0].lower()
    return GLOBAL_d_Dopingtypes[wafername]


def FilenameToArea(filename):
    global ScannedSquareSizes_in_um
    size = None
    for i in ScannedSquareSizes_in_um:
        if "_" + str(i) + "x" + str(i) + "_" in filename:
            size = (float(i) * 1E-4) ** 2
            break
    return size


def FigureOutWhatThisIs(d_info):
    if "targetrole" in d_info["techinfo"]:
        TarRol = d_info["techinfo"]["targetrole"]
        if TarRol == "VFBTEST":
            return "DropThisOne"
        elif TarRol == "CET_EXTRACTION" or TarRol == "REFERENCE":
            return "CET"
        elif TarRol == "FREQ_DISPERSION":
            return "MFCV"
        elif TarRol == "PBTI_MES" or TarRol == "PBTI_Reference" or TarRol == "PBTI_REFERENCE" or TarRol == "PBTI_AFTERCHECK":
            return "PBTI"
        elif TarRol == "NBTI_MES" or TarRol == "NBTI_Reference" or TarRol == "NBTI_REFERENCE" or TarRol == "NBTI_AFTERCHECK":
            return "NBTI"
        elif TarRol == "CVHYS":
            return "Hys"
        elif TarRol == "IgVg_leakage":
            return "IgVg"
        else:
            print("Unknown target role:" + str(TarRol))
            input("just hit enter to continue")
            return "TechniqueNotKnown"
    if "instrument" in d_info["techinfo"]:
        if "Keithley Instruments" in d_info["techinfo"]["instrument"]:
            return "IgVg"
    if "filename" in d_info["techinfo"]:
        tmp = d_info["techinfo"]["filename"]
        if "Vmax" in tmp and not "_f" in tmp:
            return "Hys"
        if not "Vmax" in tmp and "_f" in tmp:
            return "MFCV"
    return "TechniqueNotKnown"

Regarding the debugging information I'm using Python 3.7.4 in a venv because part of the project has a dependency on PySide2.

答案1

得分: 1

The GetMeDopingType() 函数中的 input() 函数似乎与事件循环发生冲突。当脚本正常运行时,事件循环已经处于活动状态,因此在其中使用 input() 会触发错误。

然而,当您在 sys.exit(app.exec_()) 上添加断点,并在选择数据后点击 "继续" 时,当您达到 input() 调用时,事件循环尚未启动。这就是为什么在这种情况下它可以正常工作而没有任何错误的原因。

一种解决方法是修改您的代码,避免在事件循环内使用 input()。例如,尝试使用单独的对话框或输入框来收集用户输入,而不是依赖控制台输入。

from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QLineEdit, QPushButton, QApplication

# ...

def GetMeDopingType(wafername, techinfo):
    global GLOBAL_d_Dopingtypes

    if "SiP" in wafername:
        return "p"

    if not wafername in GLOBAL_d_Dopingtypes:
        if techinfo is not None and "doping" in techinfo:
            GLOBAL_d_Dopingtypes[wafername] = techinfo["doping"].strip()[0].lower()
        else:
            app = QApplication.instance()
            dialog = QDialog()
            layout = QVBoxLayout()
            label = QLabel("请为晶片提供掺杂类型:" + wafername + " [n/p]:")
            input_box = QLineEdit()
            layout.addWidget(label)
            layout.addWidget(input_box)

            ok_button = QPushButton("确定")
            ok_button.clicked.connect(dialog.accept)
            cancel_button = QPushButton("取消")
            cancel_button.clicked.connect(app.exit)  # 终止处理

            layout.addWidget(ok_button)
            layout.addWidget(cancel_button)
            dialog.setLayout(layout)
            dialog.setWindowTitle("掺杂类型输入")

            if dialog.exec_() == QDialog.Accepted:
                inp = input_box.text()
                if inp.strip()[0].upper() == "C":
                    C_IO.Abort()
                GLOBAL_d_Dopingtypes[wafername] = inp.strip()[0].lower()
            else:
                C_IO.Abort()

    return GLOBAL_d_Dopingtypes[wafername]

注意:我已经将代码中的 HTML 实体(如 ")还原为引号字符。

英文:

The input() function within the GetMeDopingType() function seems to cause conflicts with the event loop. When the script is running normally, the event loop is already active, so using input() inside it triggers the error.

However, when you add a breakpoint on sys.exit(app.exec_()) and click "resume" after selecting the data, the event loop hasn't started yet when you reach the input() call. That's why it works without any errors in that case.

A solution can be to modify your code to avoid using input() within the event loop. Try, for example, to use a separate dialog or input box to gather user input instead of relying on console input.

from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QLineEdit, QPushButton, QApplication

# ...

def GetMeDopingType(wafername, techinfo):
    global GLOBAL_d_Dopingtypes

    if "SiP" in wafername:
        return "p"

    if not wafername in GLOBAL_d_Dopingtypes:
        if techinfo is not None and "doping" in techinfo:
            GLOBAL_d_Dopingtypes[wafername] = techinfo["doping"].strip()[0].lower()
        else:
            app = QApplication.instance()
            dialog = QDialog()
            layout = QVBoxLayout()
            label = QLabel("Please provide doping type for wafer: " + wafername + " [n/p]:")
            input_box = QLineEdit()
            layout.addWidget(label)
            layout.addWidget(input_box)

            ok_button = QPushButton("OK")
            ok_button.clicked.connect(dialog.accept)
            cancel_button = QPushButton("Cancel")
            cancel_button.clicked.connect(app.exit)  # Abort the processing

            layout.addWidget(ok_button)
            layout.addWidget(cancel_button)
            dialog.setLayout(layout)
            dialog.setWindowTitle("Doping Type Input")

            if dialog.exec_() == QDialog.Accepted:
                inp = input_box.text()
                if inp.strip()[0].upper() == "C":
                    C_IO.Abort()
                GLOBAL_d_Dopingtypes[wafername] = inp.strip()[0].lower()
            else:
                C_IO.Abort()

    return GLOBAL_d_Dopingtypes[wafername]

答案2

得分: 1

这是一个PyQt5的bug,其中PyQt5在CPython的PyOS_InputHook函数内调用了QCoreApplication::exec()。这个钩子在调用input()或发生调试中断时会导致QCoreApplication::exec()被调用。(详见PyQt5-5.15.9.tar.gz中的sip/QtCore/qcoreapplication.sip文件中的qtcore_input_hook()函数。)

以下示例演示了这种挂钩。

from PyQt5.QtCore import *

app = QCoreApplication([])
def on_timer():
	print('事件循环在没有app.exec()的情况下运行')
QTimer.singleShot(1000, on_timer)
input()

QTimer.singleShot(1000, on_timer)
breakpoint()

在PyQt6中,通过使用QEventLoop来修复了这个bug。

英文:

It's a PyQt5 bug, where PyQt5 calls the QCoreApplication::exec() inside a CPython PyOS_InputHook function. That hook causes the QCoreApplication::exec() to be called when the input() is called or a debug break occurs. (See the function qtcore_input_hook() at the sip/QtCore/qcoreapplication.sip file in the PyQt5-5.15.9.tar.gz for details.)

The following demonstrates this hooking.

from PyQt5.QtCore import *
app = QCoreApplication([])
def on_timer():
print('The event loop is running without app.exec()')
QTimer.singleShot(1000, on_timer)
input()
QTimer.singleShot(1000, on_timer)
breakpoint()

In PyQt6, the bug was fixed by using the QEventLoop.

huangapple
  • 本文由 发表于 2023年7月12日 23:54:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/76672435.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定