Pyqt5 QCoreApplication :: exec: цикл обработки событий уже запущен - PullRequest
0 голосов
/ 08 мая 2019

У меня есть главное окно, в котором есть кнопка, которая открывает второе окно при нажатии. Это второе окно содержит сюжет майяви. Каждый раз, когда я нажимаю эту кнопку, появляется сообщение об ошибке

QCoreApplication :: exec: цикл обработки событий уже выполняется

Я рассмотрел несколько похожих вопросов по SO, но не могу решить свою проблему.

код главного окна maingui.py :

# Standard library imports
import re
import sys
import string
import inspect
import platform
import importlib
import ctypes
from os.path import abspath, join, dirname, realpath
from functools import wraps
import inspect
import matplotlib as mpl

# Local imports
from loadhistory.stresscorrection import DnvThicknessStressCorrection

# PyQt5 imports
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox, QListView, QWidget, QGridLayout
from PyQt5.QtGui import QPixmap

# Gui package import
import gui.design
import gui.resources
from gui.inputs import Inputs
from gui.filemanager import Filemanager
from gui.datawrapper import RunThread
from gui.mayaviwindow import MayaviWindow


class MainWindow(QMainWindow, gui.design.Ui_MainWindow):
    '''main window of the gui'''

    def __init__(self):
        QMainWindow.__init__(self)
        super().__init__()

        # Bypass needed to set taskbar icon if on windows
        if "Windows" in platform.system():
            myappid = 'LaboSoete'
            ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)

        self.inputs = Inputs(self)
        self.filemanager = Filemanager(self)
        self.mayaviwindow = MayaviWindow()
        self.setupUi(self)
        self.setWindowTitle("Endurance framework SafeLife")

        # Apply css stylesheet
        sshFile = "gui/stylesheet.css"
        with open(sshFile, "r") as fh:
            self.setStyleSheet(fh.read())

        # Initialize combobox design codes
        self.designcodes = self.inputs.code_dict()
        codes = list(self.designcodes.keys())
        self.design_code_combo.addItems(codes)
        self.design_cat_combo.addItems(self.designcodes[codes[0]])
        self.scrollbar_QComboBox(self.design_code_combo)
        self.scrollbar_QComboBox(self.design_cat_combo)

        # Initialize combobox Damagemodels
        module = importlib.import_module('damagemodels.damagemodels')
        for m in module.__dict__.values():
            if inspect.isclass(m):
                if 'damagemodels.damagemodels.' in str(m):
                    if not inspect.isabstract(m):
                        self.dmgBox.addItem(m.__name__)

        ###### Menubar ######
        self.actionSave.triggered.connect(self.filemanager.save)
        self.actionSave_as.triggered.connect(self.filemanager.save_as)
        self.actionLoad.triggered.connect(self.filemanager.load)
        self.actionOutput.triggered.connect(self.filemanager.change_output_dir)
        self.actionQuit.triggered.connect(self.close)

        ###### Button handeling ######
        self.loadhistory_btn.clicked.connect(self.inputs.lh_browse_file)
        self.odb_btn.clicked.connect(self.inputs.odb_browse_file)
        self.design_code_combo.currentIndexChanged.connect(
            self.inputs.design_combo_currentIndexChanged)
        self.btn_snCustom.clicked.connect(self.inputs.sn_browse_file)
        self.run_btn.clicked.connect(self.run_analysis)
        self.newBtn.clicked.connect(self.filemanager.new)
        self.abaqus_results_btn.clicked.connect(self.filemanager.open_odb_results)
        self.damageNnumber.textChanged.connect(lambda: self.check_numeric_entry(self.damageNnumber))
        self.hss_visualization_btn.clicked.connect(self.mayaviwindow.open_new_window)

        # DNV thickness correction initialization
        self.thicknessFrame.hide()
        self.design_cat_combo.currentIndexChanged.connect(self.show_thickness_correction)
        self.loadhistory_txt.textChanged.connect(self.loadhistory_txt_changed)
        self.radioBtn_snBasquin.toggled.connect(self.show_thickness_correction)
        self.radioBtn_snCustom.toggled.connect(self.show_thickness_correction)
        self.radioBtn_snDesign.toggled.connect(self.show_thickness_correction)
        self.radioBtn_snLog.toggled.connect(self.show_thickness_correction)

        ###### Tab/plot handeling ######

        # Loadhistory plot
        self.tabWidget.setCurrentIndex(0)
        self.loadhistory_txt.textChanged.connect(lambda: self.LhWidget.run(self))
        self.thicknessCorrCheckbox.toggled.connect(lambda: self.LhWidget.run(self))
        self.t_txt.textChanged.connect(lambda: self.LhWidget.run(self))
        self.tref_txt.textChanged.connect(lambda: self.LhWidget.run(self))
        self.k_txt.textChanged.connect(lambda: self.LhWidget.run(self))
        self.mscCheckBox.toggled.connect(lambda: self.LhWidget.run(self))
        self.mscSelectionBox.currentIndexChanged.connect(lambda: self.LhWidget.run(self))


        # Sncurve plot
        self.design_cat_combo.currentIndexChanged.connect(self.inputs.design_cat_combo_change)
        self.design_code_combo.currentIndexChanged.connect(self.inputs.design_combo_currentIndexChanged)
        self.sncurve_txt.textChanged.connect(lambda: self.SnWidget.run(self))
        self.radioBtn_snCustom.clicked.connect(lambda: self.SnWidget.run(self))
        self.radioBtn_snDesign.clicked.connect(lambda: self.SnWidget.run(self))
        self.radioBtn_snBasquin.clicked.connect(self.clear_sntxt_fields)
        self.radioBtn_snLog.clicked.connect(self.clear_sntxt_fields)
        self.Nf_txt.textChanged.connect(lambda: self.SnWidget.run(self))
        self.uts_txt.textChanged.connect(lambda: self.SnWidget.run(self))
        self.m1_txt.textChanged.connect(lambda: self.SnWidget.run(self))
        self.B1_txt.textChanged.connect(lambda: self.SnWidget.run(self))
        self.m2_txt.textChanged.connect(lambda: self.SnWidget.run(self))
        self.B2_txt.textChanged.connect(lambda: self.SnWidget.run(self))

        # HSS handeling
        self.hss_checkbox.toggled.connect(self.show_hss_image)
        self.hss_combobox.currentIndexChanged.connect(self.show_hss_image)
        self.hss_combobox.currentIndexChanged.connect(self.show_hide_hss_spinbox)

        ###### Output related ######
        self.hss_visualization_btn.hide()
        self.hideTab(3)
        self.abaqus_results_btn.hide()
        self.newBtn.hide()
        self.progressBar.hide()


    @staticmethod
    def eval_eq(equation):
        '''
        Check if the string 'equation' only contains allowable characters
        '''
        regex = re.compile(r'(\d+([.]|\*))?\d+(e|[.]|\*)?\d*(e|[.]|\*)?\d*?$')
        if (equation.endswith('e') or equation.endswith('.')) and re.match(regex, equation):
            return eval(equation[:-1])
        elif re.match(regex, equation) and equation.count('e') < 2:
            return eval(equation)
        else:
            raise ValueError(
                'Illegal expression used to define the S-N curve, only e^*'+string.digits+' allowed')

    def check_numeric_entry(self, myQlineEdit):
        '''
        For QlineEdit which can only contain numbers, check whether
        no non-numeric characters are entered
        '''
        if myQlineEdit.text().isdigit() or len(myQlineEdit.text()) < 1:
            return
        else:
            reply = QMessageBox.warning(self, 'TypeError', 'This field only allows for numeric entries')
            if reply == QMessageBox.Ok:
                myQlineEdit.setText(myQlineEdit.text()[:-1])
                return

    def loadhistory_txt_changed(self):
        '''
        If a loadhistory is selected the meanstresscorrection option
        becomes enabled
        '''
        if len(self.loadhistory_txt.text())>0:
            self.mscCheckBox.setEnabled(True)
        else:
            self.mscCheckBox.setDisabled(True)

    def show_thickness_correction(self):
        '''
        Determine whether or not the thickness_correction option should be shown and enabled.
        This is only the case when a DNV SN curve is selected
        '''
        if 'DNV' in self.design_code_combo.currentText() and self.radioBtn_snDesign.isChecked() and len(self.loadhistory_txt.text())>0:
            self.thicknessFrame.show()
            self.thicknessCorrCheckbox.setEnabled(True)
        else:
            self.thicknessFrame.hide()
            self.thicknessCorrCheckbox.setChecked(False)
            self.thicknessCorrCheckbox.setDisabled(True)

    def show_hss_image(self):
        if self.hss_checkbox.isChecked():
            filename = self.hss_combobox.currentText()
            filepath = abspath(join(dirname(realpath(__file__)), 'gui', 'icons', filename))
            pixmap = QPixmap(filepath)
            self.hss_imagelabel.setPixmap(pixmap)
            self.hss_imagelabel.show()
            self.tabWidget.setCurrentIndex(2)
        if not self.hss_checkbox.isChecked():
            self.hss_imagelabel.hide()


    def get_thickness_correction(self):
        '''
        Return a thickness correction object using the values from the
        gui to initialize the object
        '''
        if "DNV" in self.design_code_combo.currentText():
            tref = self.eval_eq(self.tref_txt.text())
            t = self.eval_eq(self.t_txt.text())
            k = self.eval_eq(self.k_txt.text())
            if k >= 1:
                reply = QMessageBox.warning(
                    self, 'ValueError', 'k needs to be less than one, please consult DNV-GL-RP203 for the correct values')
                if reply == QMessageBox.Ok or reply == QMessageBox.Cancel:
                    self.k_txt.clear()
                    return
                else: return
            return DnvThicknessStressCorrection(tref, t, k)

    def show_hide_hss_spinbox(self):
        '''
        hide the hss_spinbox and hss_t_label widgets if a hss method, that does not
        use the plate thickness to determine the extrapolation coords, is selected
        '''
        test = self.hss_combobox.currentText()
        lst = ['1', '2', '3']
        if any(x in test for x in lst):
            self.hss_spinbox.show()
            self.hss_t_label.show()
        else:
            self.hss_t_label.hide()
            self.hss_t_label.show()

    def scrollbar_QComboBox(self, combo):
        '''
        Add a scrollbar to QComboBoxes if the selected text is wider than
        the combobox
        '''
        view = QListView(combo)
        combo.setView(view)
        view.setTextElideMode(Qt.ElideNone)
        view.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)


    def closeEvent(self, event):
        '''
        Handle the event of the user wanting to close the application
        '''
        msgBox = QMessageBox()
        msgBox.setWindowTitle('Exit application?')
        msgBox.setText('Do you want to exit the application?')
        exit_btn = msgBox.addButton('Exit', QMessageBox.YesRole)
        save_exit_btn = msgBox.addButton('Save and Exit', QMessageBox.YesRole)
        cancel_btn = msgBox.addButton('Cancel', QMessageBox.RejectRole)
        msgBox.setEscapeButton(cancel_btn)

        msgBox.exec_()

        if msgBox.clickedButton() == exit_btn:
            event.accept()
        elif msgBox.clickedButton() == save_exit_btn:
            self.filemanager.save()
            event.accept()
        elif msgBox.clickedButton() == cancel_btn:
            event.ignore()



    def start_thread(self, thread):
        '''Pass all MainWindow attributes to the new thread and start the thread'''
        thread.progressBar = self.progressBar
        thread.loadhistory_txt = self.loadhistory_txt
        thread.loadhistory_btn = self.loadhistory_btn
        thread.odb_txt = self.odb_txt
        thread.odb_btn = self.odb_btn
        thread.dmgBox = self.dmgBox
        thread.dmax_box = self.dmax_box
        thread.radioBtn_snCustom = self.radioBtn_snCustom
        thread.radioBtn_snBasquin = self.radioBtn_snBasquin
        thread.radioBtn_snDesign = self.radioBtn_snDesign
        thread.sncurve_txt = self.sncurve_txt
        thread.btn_snCustom = self.btn_snCustom
        thread.design_cat_combo = self.design_cat_combo
        thread.design_code_combo = self.design_code_combo
        thread.mscCheckBox = self.mscCheckBox
        thread.mscSelectionBox = self.mscSelectionBox
        thread.calcLifetimeBox = self.calcLifetimeBox
        thread.calcLifetimeCheckBox = self.calcLifetimeCheckBox
        thread.damageNcheckBox = self.damageNcheckBox
        thread.damageNnumber = self.damageNnumber
        thread.newBtn = self.newBtn
        thread.m1_txt = self.m1_txt
        thread.B1_txt = self.B1_txt
        thread.m2_txt = self.m2_txt
        thread.B2_txt = self.B2_txt
        thread.uts_txt = self.uts_txt
        thread.Nf_txt = self.Nf_txt
        thread.SnWidget = self.SnWidget
        thread.LhWidget = self.LhWidget
        thread.Inputs = self.inputs
        thread.thicknessCorrCheckbox = self.thicknessCorrCheckbox
        thread.k_txt = self.k_txt
        thread.t_txt = self.t_txt
        thread.tref_txt = self.tref_txt
        thread.hss_spinbox = self.hss_spinbox
        thread.hss_combobox = self.hss_combobox
        thread.hss_checkbox = self.hss_combobox
        thread.start()

    def clear_sntxt_fields(self):
        '''Clear all text fields related to SN curve equations'''
        self.Nf_txt.clear()
        self.uts_txt.clear()
        self.m1_txt.clear()
        self.m2_txt.clear()
        self.B1_txt.clear()
        self.B2_txt.clear()

    def initialize(self):
        self.hss_visualization_btn.hide()
        self.run_btn.setDisabled(True)
        self.newBtn.hide()
        self.abaqus_results_btn.hide()
        self.hideTab(3)
        self.output_txt.clear()

    def finished(self):
        if self.hss_checkbox.isChecked():
            self.hss_visualization_btn.show()
        self.run_btn.setEnabled(True)
        self.newBtn.show()
        self.abaqus_results_btn.show()
        self.hide_progressbar()
        self.showTab(3)

    def show_progressbar(self):
        self.progressBar.show()

    def hide_progressbar(self):
        self.progressBar.hide()

    def clear_lhtxt(self):
        self.loadhistory_txt.clear()
        self.loadhistory_txt.setFocus()

    def clear_odbtxt(self):
        self.odb_txt.clear()
        self.odb_txt.setFocus()

    def clear_sncustomtxt(self):
        self.sncurve_txt.clear()
        self.sncurve_txt.setFocus()

    def show_messagebox(self, errortype, msg, methodname):
        reply = QMessageBox.warning(self, errortype, msg)
        if methodname is not "None":
            handle_error = getattr(self, methodname)
        if reply == QMessageBox.Ok:
            if 'handle_error' in locals():
                handle_error()
            return

    def dmg_curve_plot(self, Dplot, nNplot):
        self.DmgWidget.run(self, Dplot, nNplot)

    def hideTab(self, tabIndex):
        self.tabWidget.setTabEnabled(tabIndex, False)

    def showTab(self, tabIndex):
        self.tabWidget.setTabEnabled(tabIndex, True)

    def check_required_inputs(self):
        if "".__eq__(self.loadhistory_txt.text()) or "".__eq__(self.odb_txt.text()) \
            or not self.odb_txt.text().endswith('odb'):
            return False
        if self.radioBtn_snCustom.isChecked() and "".__eq__(self.sncurve_txt.text()):
            return False
        if self.radioBtn_snBasquin.isChecked() or self.radioBtn_snLog.isChecked():
            if ("".__eq__(self.m1_txt.text()) or "".__eq__(self.Nf_txt.text()) or \
               "".__eq__(self.uts_txt.text()) or "".__eq__(self.B1_txt.text())):
                return False
        return True

    def run_analysis(self):

        mpl.pyplot.close(fig="all")

        if self.check_required_inputs() is False:
            self.show_messagebox('IOError', 'Please check all required fields', "None")
            return

        self.run_thread = RunThread(self)
        self.run_thread.initialize.connect(self.initialize)
        self.run_thread.clear_lhtxt.connect(self.clear_lhtxt)
        self.run_thread.clear_odbtxt.connect(self.clear_odbtxt)
        self.run_thread.clear_odbtxt.connect(self.clear_sncustomtxt)
        self.run_thread.show_messagebox.connect(self.show_messagebox)

        self.run_thread.show_progressbar.connect(self.show_progressbar)
        self.run_thread.hide_progressbar.connect(self.hide_progressbar)

        self.run_thread.finished.connect(self.finished)
        self.run_thread.output.connect(self.filemanager.to_output)
        self.run_thread.dmg_curve_plot.connect(self.dmg_curve_plot)
        self.start_thread(self.run_thread)


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

код второго окна mayaviwindow.py :

import os
import sys
import mayavi.mlab as mlab
os.environ['ETS_TOOLKIT'] = 'qt4'
from pyface.qt import QtGui, QtCore
from traits.api import HasTraits, Instance, on_trait_change
from traitsui.api import View, Item
from mayavi.core.ui.api import MayaviScene, MlabSceneModel, SceneEditor

class MayaviWindow(QtGui.QMainWindow):
    def open_new_window(self):
        container = QtGui.QWidget()
        container.setWindowTitle("Hot spot stress")
        layout = QtGui.QGridLayout(container)
        mayavi_widget = MayaviQWidget(container)
        layout.addWidget(mayavi_widget, 1, 1)
        container.show()
        self.window = QtGui.QMainWindow()
        self.window.setCentralWidget(container)
        self.window.show()

class Visualization(HasTraits):
    scene = Instance(MlabSceneModel, ())

    @on_trait_change('scene.activated')
    def update_plot(self):   
        self.scene.mlab.test_points3d()

    # the layout of the dialog screated
    view = View(Item('scene', editor=SceneEditor(scene_class=MayaviScene),
                     height=250, width=300, show_label=False),
                resizable=True # We need this to resize with the parent widget
                )
class MayaviQWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        layout = QtGui.QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self.visualization = Visualization()

        # The edit_traits call will generate the widget to embed.
        self.ui = self.visualization.edit_traits(parent=self,
                                                 kind='subpanel').control
        layout.addWidget(self.ui)
        self.ui.setParent(self)

Может ли кто-нибудь объяснить мне, в чем проблема?

Редактировать изменение класса MayaviWindow для наследования от QWidget вместо QMainwindow не решило проблему

class MayaviWindow(QtGui.QWidget):
    def open_new_window(self):
        self.container = QtGui.QWidget()
        self.container.setWindowTitle("Hot spot stress")
        layout = QtGui.QGridLayout(self.container)
        mayavi_widget = MayaviQWidget(self.container)
        layout.addWidget(mayavi_widget, 1, 1)
        self.container.show()
...