Exemple #1
0
 def readContours(self):
     """Reads contours saved in xml format (Echoplaque compatible)"""
     if self.image == False:
         warning = QErrorMessage()
         warning.setWindowModality(Qt.WindowModal)
         warning.showMessage(
             'Reading of contours failed. Images must be loaded prior to loading contours'
         )
         warning.exec_()
     else:
         options = QFileDialog.Options()
         options |= QFileDialog.DontUseNativeDialog
         fileName, _ = QFileDialog.getOpenFileName(
             self,
             "QFileDialog.getOpenFileName()",
             "",
             "XML file (*.xml)",
             options=options)
         self.lumen, self.plaque, self.stent, self.resolution, frames = read_xml.read(
             fileName)
         self.lumen = self.mapToList(self.lumen)
         self.plaque = self.mapToList(self.plaque)
         self.stent = self.mapToList(self.stent)
         self.contours = True
         self.resizeContours()
         self.wid.setData(self.lumen, self.plaque, self.images)
         self.hideBox.setChecked(False)
Exemple #2
0
def display_error(err):
    app = QApplication.instance()
    window = app.activeWindow()
    dialog = QErrorMessage(window)
    dialog.setWindowModality(Qt.WindowModal)
    dialog.setWindowTitle("Error")
    dialog.showMessage(err)
Exemple #3
0
 def segment(self):
     """Segmentation and phenotyping of IVUS images"""
     warning = QErrorMessage()
     warning.setWindowModality(Qt.WindowModal)
     warning.showMessage(
         'Warning: IVUS Phenotyping is currently only supported for 20MHz images. Interpret other images with extreme caution'
     )
     warning.exec_()
 def on_save_button_clicked(self):
     file_name, _ = QFileDialog.getSaveFileName(self, "Save Image",
                                                self.current_name + ".png",
                                                "Images (*.png)")
     if file_name != "":
         try:
             self.current_image.save(file_name)
         except:
             error = QErrorMessage(self)
             error.showMessage("Could not save file")
             error.setWindowModality(Qt.WindowModal)
Exemple #5
0
    def readContours(self):
        """Reads contours.

        Reads contours  saved in xml format (Echoplaque compatible) and 
        displays the contours in the graphics scene
        """

        if not self.image:
            warning = QErrorMessage()
            warning.setWindowModality(Qt.WindowModal)
            warning.showMessage(
                'Reading of contours failed. Images must be loaded prior to loading contours'
            )
            warning.exec_()
        else:
            options = QFileDialog.Options()
            options |= QFileDialog.DontUseNativeDialog
            fileName, _ = QFileDialog.getOpenFileName(
                self,
                "QFileDialog.getOpenFileName()",
                "",
                "XML file (*.xml)",
                options=options)
            if fileName:
                self.lumen, self.plaque, self.stent, self.resolution, frames = read_xml.read(
                    fileName)

                if len(self.lumen[0]) != self.dicom.NumberOfFrames:
                    warning = QErrorMessage()
                    warning.setWindowModality(Qt.WindowModal)
                    warning.showMessage(
                        'Reading of contours failed. File must contain the same number of frames as loaded dicom'
                    )
                    warning.exec_()
                else:
                    self.resolution = float(self.resolution[0])
                    self.lumen = self.mapToList(self.lumen)
                    self.plaque = self.mapToList(self.plaque)
                    self.stent = self.mapToList(self.stent)
                    self.contours = True
                    self.wid.setData(self.lumen, self.plaque, self.stent,
                                     self.images)
                    self.hideBox.setChecked(False)

                    gatedFrames = [
                        frame for frame in range(len(self.lumen[0]))
                        if self.lumen[0][frame] or self.plaque[0][frame]
                    ]
                    self.gatedFrames = gatedFrames
                    self.useGatedBox.setChecked(True)
                    self.slider.addGatedFrames(self.gatedFrames)
Exemple #6
0
    def segment(self):
        """Segmentation and phenotyping of IVUS images"""

        save_path = os.path.join(os.getcwd(), 'model', 'saved_model.pb')
        save_path = os.path.join('/home/microway/Documents/IVUS', 'model_2021',
                                 'saved_model.pb')
        if not os.path.isfile(save_path):
            message = "No saved weights have been found, segmentation will be unsuccessful, check that weights are saved in {}".format(
                os.path.join(os.getcwd(), 'model'))
            error = QMessageBox()
            error.setIcon(QMessageBox.Critical)
            error.setWindowTitle("Error")
            error.setModal(True)
            error.setWindowModality(Qt.WindowModal)
            error.setText(message)
            error.exec_()
            return -1

        warning = QErrorMessage()
        warning.setWindowModality(Qt.WindowModal)
        warning.showMessage(
            'Warning: IVUS Phenotyping is currently only supported for 20MHz images. Interpret other images with extreme caution'
        )
        warning.exec_()

        image_dim = self.images.shape

        if self.useGatedBox.isChecked():
            masks = np.zeros((self.numberOfFrames, image_dim[1], image_dim[2]),
                             dtype=np.uint8)
            masks_gated = predict(self.images[self.gatedFrames, :, :])
            masks[self.gatedFrames, :, :] = masks_gated
        else:
            masks = predict(self.images)

        # compute metrics such as plaque burden
        self.metrics = self.computeMetrics(masks)
        self.segmentation = True

        # convert masks to contours
        self.lumen, self.plaque = self.maskToContours(masks)
        self.contours = True

        # stent contours currently unsupported so create empty list
        self.stent = [[[] for i in range(image_dim[0])],
                      [[] for i in range(image_dim[0])]]

        self.wid.setData(self.lumen, self.plaque, self.stent, self.images)
        self.hideBox.setChecked(False)
        self.successMessage('Segmentation')
Exemple #7
0
    def gate(self):
        """Extract end diastolic frames and stores in new variable"""

        self.gatedFrames = IVUS_gating(self.images, self.ivusPullbackRate,
                                       self.dicom.CineRate)
        if self.gatedFrames:
            self.slider.addGatedFrames(self.gatedFrames)
            self.useGatedBox.setChecked(True)
            self.successMessage(
                "Diastolic frame (change with up and down arrows) extraction")
        else:
            warning = QErrorMessage()
            warning.setWindowModality(Qt.WindowModal)
            warning.showMessage('Diastolic frame extraction was unsuccessful')
            warning.exec_()
Exemple #8
0
    def store_new_argument(self):
        wrong = self.ui.wrong.text()
        right = self.ui.right.text()
        lang = self.ui.languages.currentText()
        # Check argument is not circular
        if right == wrong:
            msg = QErrorMessage(self)
            msg.setWindowModality(QtCore.Qt.WindowModal)
            msg.showMessage(
                'You can’t replace a word with itself. It will create a loop.')

        lang_path = self.script_path + lang + '.json'
        typo_data = open_typo_file(lang_path)
        typo_data[right].add(wrong)
        save_typo_data(lang_path, typo_data)
Exemple #9
0
    def logoFromUrlDialog(self, team):
        """Open dialog for team logo."""

        url = "http://"
        regex = re.compile(
            r'^(?:http|ftp)s?://'  # http:// or https://
            # domain...
            r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+'
            r'(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'
            r'localhost|'  # localhost...
            r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'  # ...or ip
            r'(?::\d+)?'  # optional port
            r'(?:/?|[/?]\S+)$',
            re.IGNORECASE)
        emsg = QErrorMessage(self)
        emsg.setWindowModality(Qt.WindowModal)

        while True:

            url, status = QInputDialog.getText(self, _("Logo from URL"),
                                               _("URL of Team Logo") + ":",
                                               QLineEdit.Normal, url)

            if status:

                if not regex.match(url):
                    QMessageBox.critical(
                        self, "Invalid URL",
                        _('{} is not a valid URL.').format(url))
                    continue
                else:
                    logo = LogoDownloader(self.controller, self,
                                          url).download()
                    logo.refreshData()
                    map = logo.provideQPixmap()

                    if team == 1:
                        self.controller.logoManager.setTeam1Logo(logo)
                        self.team1_icon.setPixmap(map)
                        self.refreshLastUsed()
                    elif team == 2:
                        self.controller.logoManager.setTeam2Logo(logo)
                        self.team2_icon.setPixmap(map)
                        self.refreshLastUsed()
                    break
            else:
                break
Exemple #10
0
    def send_serial(self, command_string):
        #See what we plan on sending.
        print(bytes(command_string, 'ascii'))
        #Send the command as a bytes string
        try:
            self.ser.write(bytes(command_string + '\n', 'ascii'))
            #get some feedback that the command worked.
            rate = self.ser.readline().decode(
                'ascii', 'ignore').strip()  #remove whitespace

            self.slider_label.setText("Blink Rate: {} ms".format(rate))

        except Exception as e:
            error_dialog = QErrorMessage(self)
            error_dialog.showMessage(
                repr(e) + "\nPlease reconnect the serial device.")
            error_dialog.setWindowModality(Qt.ApplicationModal)
            error_dialog.exec_()
Exemple #11
0
class Messenger_Window(QMainWindow):

    # noinspection PyArgumentList
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        myFont = QFont('Arial', pointSize=14, weight=400)
        self.font = myFont
        self.errorMessageDialog = QErrorMessage(self)
        self.errorMessageDialog.setWindowModality(Qt.WindowModal)
        self.chat_area = QTextEdit(
            'В чат пока ничего не написали',
            readOnly=True,
        )
        self.chat_area.setFont(myFont)
        self.chat_label = QLabel()
        self.chat_label.setFont(myFont)
        self.message_area = QTextEdit('Введите сообщение')
        self.message_area.setFont(myFont)
        self.message_area.setMaximumHeight(100)

        v_splitter = QSplitter(Qt.Vertical)
        v_splitter.addWidget(self.chat_label)
        v_splitter.addWidget(self.chat_area)
        v_splitter.addWidget(self.message_area)
        self.setCentralWidget(v_splitter)
        self.setGeometry(200, 50, 500, 900)
        self.create_actions()
        self.init_tool_bar()
        self.layout().setSpacing(0)
        self.layout().setContentsMargins(0, 0, 0, 0)
        app_icon = QIcon(os.path.join(IMAGES_PATH, 'worldwide.png'))
        self.setWindowIcon(app_icon)
        self.enable_actions(True)
        self.setWindowTitle('My awesome client')

        self.message_area.setFocus()
        self.statusBar().showMessage('Ready')

    # noinspection PyArgumentList
    def create_actions(self):
        send_icon = QIcon(os.path.join(IMAGES_PATH, 'paper-plane-1.png'))
        save_icon = QIcon(os.path.join(IMAGES_PATH, 'save.png'))
        profile_icon = QIcon(os.path.join(IMAGES_PATH, 'emoji.png'))
        bold_icon = QIcon(os.path.join(IMAGES_PATH, '012-bold.png'))
        italic_icon = QIcon(os.path.join(IMAGES_PATH, '057-italic.png'))
        underline_icon = QIcon(os.path.join(IMAGES_PATH, '093-underline.png'))
        smile_icon = QIcon(os.path.join(IMAGES_PATH, 'croco.png'))
        logon_icon = QIcon(os.path.join(IMAGES_PATH, 'key.png'))
        random_icon = QIcon(os.path.join(IMAGES_PATH, 'magic-wand.png'))
        exit_icon = QIcon(os.path.join(IMAGES_PATH, 'exit.png'))
        join_chat_icon = QIcon(os.path.join(IMAGES_PATH, 'users-1.png'))
        contact_user_icon = QIcon(os.path.join(IMAGES_PATH, 'user-4.png'))
        debug_icon = QIcon(os.path.join(IMAGES_PATH, 'settings-1'))
        self.save_action = QAction(save_icon,
                                   'Save chat as...',
                                   self,
                                   shortcut='Ctrl+S',
                                   triggered=self.dialog_save)
        self.send_action = QAction(send_icon,
                                   'Send message',
                                   self,
                                   shortcut='Ctrl+Enter',
                                   triggered=self.send)
        self.edit_profile_action = QAction(profile_icon,
                                           'Edit profile',
                                           self,
                                           triggered=self.edit_profile)
        self.bold_action = QAction(bold_icon,
                                   'Bold',
                                   self,
                                   shortcut='Ctrl+S',
                                   triggered=self.make_bold)
        self.italic_action = QAction(italic_icon,
                                     'Italic',
                                     self,
                                     shortcut='Ctrl+Enter',
                                     triggered=self.make_italic)
        self.underline_action = QAction(underline_icon,
                                        'Underlined',
                                        self,
                                        triggered=self.make_underline)
        self.smile_action = QAction(smile_icon,
                                    'Smile!',
                                    self,
                                    triggered=self.insert_smile)
        self.logon_action = QAction(logon_icon,
                                    'Logon',
                                    self,
                                    triggered=self.logon)
        self.random_action = QAction(random_icon,
                                     'Random message!',
                                     self,
                                     triggered=self.random_message)
        self.exit_action = QAction(exit_icon,
                                   'Exit',
                                   self,
                                   triggered=self.exit)
        self.join_chat_action = QAction(join_chat_icon,
                                        'Select chat',
                                        self,
                                        triggered=self.dialog_join_chat)
        self.contact_user_action = QAction(contact_user_icon,
                                           'Select user',
                                           self,
                                           triggered=self.dialog_contact_user)
        self.debug_action = QAction(debug_icon,
                                    'Debug',
                                    self,
                                    triggered=self.debug)

    def exit(self):
        self.before_exit()
        qApp.quit()

    def init_tool_bar(self):
        toolbar = QToolBar()
        toolbar.setIconSize(QSize(30, 30))
        toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon
                                   | Qt.AlignLeading)  # <= Toolbuttonstyle
        self.addToolBar(Qt.LeftToolBarArea, toolbar)
        self.toolbar = toolbar
        self.toolbar.addAction(self.logon_action)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.join_chat_action)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.contact_user_action)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.edit_profile_action)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.save_action)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.send_action)
        self.toolbar.addAction(self.random_action)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.bold_action)
        self.toolbar.addAction(self.italic_action)
        self.toolbar.addAction(self.underline_action)
        self.toolbar.addAction(self.smile_action)
        self.toolbar.addAction(self.exit_action)
        self.toolbar.addSeparator()
        self.toolbar.addSeparator()
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.debug_action)
        self.toolbar.setMovable(False)

    def enable_actions(self, state):
        for action in self.toolbar.actions():
            action.setEnabled(state)

    def enable_server_buttons(self, state):
        self.send_action.setEnabled(state)
        self.edit_profile_action.setEnabled(state)
        self.join_chat_action.setEnabled(state)
        self.contact_user_action.setEnabled(state)

    def enable_communication_buttons(self, state):
        self.send_action.setEnabled(state)
        self.random_action.setEnabled(state)
        self.smile_action.setEnabled(state)
        self.bold_action.setEnabled(state)
        self.italic_action.setEnabled(state)
        self.underline_action.setEnabled(state)
        self.message_area.setEnabled(state)

    def insert_smile(self):
        # TODO insert smiles in unicode
        url = os.path.join(IMAGES_PATH, 'croco-small.png')
        self.message_area.insertHtml('<img src="%s" />' % url)

    def make_bold(self):
        self.font.setBold(True)
        self.message_area.setFont(self.font)

    def make_italic(self):
        self.font.setItalic(True)
        self.message_area.setFont(self.font)

    def make_underline(self):
        self.font.setUnderline(True)
        self.message_area.setFont(self.font)

    def dialog_save(self):
        fname = QFileDialog.getSaveFileName(
            self, 'Save as', os.path.expanduser(''),
            'Html (*.html);;Txt (*.txt);;All files (*.*)')[0]
        if fname:
            self.statusBar().showMessage('Saved file as: {}'.format(fname))
            print(fname)
            html_str = self.chat_area.toHtml()
            Html_file = open(fname, "w")
            Html_file.write(html_str)
            Html_file.close()

    def edit_profile(self):
        dlg = ProfileWindow()
        dlg.exec_()
Exemple #12
0
def error_message(message):
    error_dialog = QErrorMessage()
    error_dialog.setWindowModality(Qt.WindowModal)
    error_dialog.showMessage(message)
    error_dialog.exec_()
Exemple #13
0
class MyMainWindow(QMainWindow, Ui_MainWindow):

    closing = pyqtSignal()

    fileOptions = QFileDialog.Options() | QFileDialog.DontUseNativeDialog

    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)
        qApp.installEventFilter(self)
        self.setupUi(self)

        self.data = ImageData()

        self.errorMsg = QErrorMessage(self)
        self.errorMsg.setWindowModality(Qt.WindowModal)

        self.pushButtonLoadWL.clicked.connect(self.loadWhite)
        self.pushButtonLoadSpectra.clicked.connect(self.loadSpectra)
        self.pushButtonLoad.clicked.connect(self.loadAnnotations)
        self.pushButtonSave.clicked.connect(self.saveAnnotations)

        self.pushButtonClear.clicked.connect(self.plot_visual.clearSelected)

        #        self.comboBoxMethod.currentIndexChanged.connect()
        self.horizontalSliderWn.valueChanged.connect(self.wavenumberSlide)
        self.lineEditWn.editingFinished.connect(self.wavenumberEdit)

        self.comboBoxVisual.currentIndexChanged.connect(self.imageProjection)
        self.comboBoxCmaps.currentTextChanged.connect(self.plot_visual.setCmap)

        self.lineEditWn.setFormat("%.2f")
        #        self.lineEditWavenumber.setRange(min=wmin, max=wmax, default=.5*(wmin+wmax))

        self.comboBoxAnnotation.currentIndexChanged.connect(
            self.plot_visual.setAnnotation)

        self.plot_visual.addedPoint.connect(self.plot_whitelight.addPoint)
        self.plot_visual.removedPoint.connect(self.plot_whitelight.removePoint)
        self.plot_visual.alteredCounts.connect(self.updateCounts)
        self.plot_whitelight.clickedPoint.connect(
            self.plot_visual.clickOnePoint)

    def closeEvent(self, event):
        self.closing.emit()
        plt.close('all')
        self.deleteLater()
        qApp.quit()

    def loadSpectra(self):
        "Load a file or return (error message, traceback) from trying"
        fileName, _ = QFileDialog.getOpenFileName(
            self,
            "Load FTIR image",
            "",
            "Matlab files (*.mat);;All files (*)",
            options=MyMainWindow.fileOptions)
        self.data.readmat(fileName)
        self.plot_visual.setData(self.data.wavenumber, self.data.raw,
                                 self.data.wh)
        self.horizontalSliderWn.setMaximum(len(self.data.wavenumber) - 1)
        self.lineEditWn.setRange(self.data.wmin, self.data.wmax)
        self.imageProjection()
        self.plot_whitelight.load(os.path.splitext(fileName)[0] + '.jpg')

    def loadAnnotations(self):
        fileName, _ = QFileDialog.getOpenFileName(
            self,
            "Load annotation map",
            "",
            "Matlab files (*.mat);;All files (*)",
            options=MyMainWindow.fileOptions)
        if fileName:
            s = scipy.io.loadmat(fileName)['annotations']
            self.plot_visual.setAnnotations(s)

    def saveAnnotations(self):
        fileName, _ = QFileDialog.getSaveFileName(
            self,
            "Save annotation map",
            "",
            "Matlab files (*.mat);;All files (*)",
            options=MyMainWindow.fileOptions)
        if fileName:
            scipy.io.savemat(
                fileName, {'annotations': self.plot_visual.getAnnotations()})

    # Image visualization
    def loadWhite(self):
        fileName, _ = QFileDialog.getOpenFileName(
            self,
            "Load white light image",
            "",
            "Image files (*.jpg *.png);;All files (*)",
            options=MyMainWindow.fileOptions)
        if fileName:
            self.plot_whitelight.load(fileName)

    def imageProjection(self):
        meth = self.comboBoxVisual.currentIndex()
        iswn = meth == 2
        self.lineEditWn.setEnabled(iswn)
        self.horizontalSliderWn.setEnabled(iswn)
        self.plot_visual.setProjection(meth,
                                       -1 - self.horizontalSliderWn.value())

    def updateCounts(self, counts):
        for i in range(self.comboBoxAnnotation.count()):
            t = self.comboBoxAnnotation.itemText(i)
            p = t.rfind('(')
            t = t[:p] if p >= 0 else t + ' '
            self.comboBoxAnnotation.setItemText(i, t + '(%d)' % counts[i])

    def sliderToBox(self, slider, box, ixfinder):
        wn = self.plot_visual.getWavenumbers()
        if wn is not None:
            if (not box.hasAcceptableInput()
                    or slider.value() != ixfinder(wn, box.value())):
                box.setValue(wn[-1 - slider.value()])
        elif (not box.hasAcceptableInput() or slider.value() != int(
                round(slider.maximum() * (box.value() - self.wmin) /
                      (self.wmax - self.wmin)))):
            box.setValue(self.wmin + (self.wmax - self.wmin) * slider.value() /
                         slider.maximum())

    def boxToSlider(self, slider, box, ixfinder):
        wn = self.plot_visual.getWavenumbers()
        if wn is not None:
            if box.hasAcceptableInput():
                slider.setValue(ixfinder(wn, box.value()))
            else:
                box.setValue(wn[-1 - slider.value()])
        else:
            slider.setValue(
                int(
                    round(slider.maximum() * (box.value() - self.wmin) /
                          (self.wmax - self.wmin))))

    def wavenumberSlide(self):
        self.sliderToBox(
            self.horizontalSliderWn, self.lineEditWn,
            lambda wn, val: len(wn) - 1 - (np.abs(wn - val)).argmin())
        self.plot_visual.setProjection(2, -1 - self.horizontalSliderWn.value())

    def wavenumberEdit(self):
        self.boxToSlider(
            self.horizontalSliderWn, self.lineEditWn,
            lambda wn, val: len(wn) - 1 - (np.abs(wn - val)).argmin())
Exemple #14
0
class OctavvsMainWindow(QMainWindow):

    fileOptions = QFileDialog.Options() | QFileDialog.DontUseNativeDialog

    octavvs_version = 'v0.1.2'

    def __init__(self, parent=None):
        super().__init__(parent)
        self.settings = QSettings('MICCS', 'OCTAVVS ' + self.program_name())
        qApp.installEventFilter(self)
        self.setupUi(self)

        self.errorMsg = QErrorMessage(self)
        self.errorMsg.setWindowModality(Qt.WindowModal)

    def post_setup(self):
        "Called by children after setting up UI but before loading data etc"
        self.setWindowTitle('OCTAVVS %s %s' %
                            (self.program_name(), self.octavvs_version))
        ExceptionDialog.install(self)

    @classmethod
    def program_name(cls):
        "Return the name of the program that this main window represents"
        return cls.__name__

    def showDetailedErrorMessage(self, err, details):
        "Show an error dialog with details"
        q = QMessageBox(self)
        q.setIcon(QMessageBox.Critical)
        q.setWindowTitle("Error")
        q.setText(err)
        q.setTextFormat(Qt.PlainText)
        q.setDetailedText(details)
        q.addButton('OK', QMessageBox.AcceptRole)
        return q.exec()

    def loadErrorBox(self, file, err):
        "Prepare an error dialog for failure to load a file"
        q = QMessageBox(self)
        q.setIcon(QMessageBox.Warning)
        q.setWindowTitle("Error loading file")
        q.setText("Failed to load '" + file + "':\n" + err[0])
        q.setTextFormat(Qt.PlainText)
        q.setDetailedText(err[1])
        return q

    def getLoadSaveFileName(self,
                            title,
                            filter=None,
                            settingname=None,
                            savesuffix=None,
                            multiple=False,
                            settingdefault=None):
        "Show a file dialog and select one or more files"
        setting = self.settings.value(
            settingname, settingdefault) if settingname is not None else None
        directory = setting if type(setting) is str else None
        dialog = QFileDialog(parent=self,
                             caption=title,
                             directory=directory,
                             filter=filter)
        #        if setting and type(setting) is not str:
        #            dialog.restoreState(setting)
        dialog.setOption(QFileDialog.DontUseNativeDialog, True)
        if savesuffix is not None:
            dialog.setAcceptMode(QFileDialog.AcceptSave)
            dialog.setDefaultSuffix(savesuffix)
        elif multiple:
            dialog.setFileMode(QFileDialog.ExistingFiles)
        else:
            dialog.setFileMode(QFileDialog.ExistingFile)
        dialog.exec()
        files = dialog.selectedFiles()
        if not dialog.result() or not files:
            return None
        self.settings.setValue(settingname, os.path.dirname(files[0]))
        return files if multiple else files[0]

    def getSaveFileName(self, title, suffix='', **kwargs):
        "Show a file dialog and select an output file"
        return self.getLoadSaveFileName(title=title,
                                        savesuffix=suffix,
                                        **kwargs)

    def getLoadFileName(self, title, **kwargs):
        "Show a file dialog and select an input file"
        return self.getLoadSaveFileName(title=title, **kwargs)

    def getLoadFileNames(self, title, **kwargs):
        "Show a file dialog and select an input file"
        return self.getLoadSaveFileName(title=title, multiple=True, **kwargs)

    def getImageFileName(self, title, **kwargs):
        "Show a file dialog and select a single image file"
        return self.getLoadSaveFileName(
            title=title,
            filter=
            "Image files (*.jpg *.jpeg *.png *.tif *.tiff *.bmp *.gif);;All files (*)",
            **kwargs)

    def getDirectoryName(self, title, settingname=None, savesetting=True):
        "Show a file dialog and select a directory"
        directory = self.settings.value(
            settingname, None) if settingname is not None else None
        dialog = QFileDialog(parent=self, caption=title, directory=directory)

        dialog.setAcceptMode(QFileDialog.AcceptSave)
        dialog.setFileMode(QFileDialog.Directory)
        dialog.setOption(QFileDialog.DontUseNativeDialog, True)
        dialog.setOption(QFileDialog.ShowDirsOnly, True)
        dialog.exec()
        dirs = dialog.selectedFiles()
        if not dialog.result() or not dirs:
            return None
        if savesetting:
            self.settings.setValue(settingname, dirs[0])
        return dirs[0]

    def updateWavenumberRange(self):
        "For derived classes, when the wn range is updated"
        pass

    def updateDimensions(self, wh):
        "For derived classes, when image width/height is updated"
        pass

    @classmethod
    def run_octavvs_application(windowclass,
                                parser=None,
                                parameters=[],
                                isChild=False):
        res = 1
        try:
            progver = 'OCTAVVS %s %s' % (windowclass.program_name(),
                                         OctavvsMainWindow.octavvs_version)
            windowparams = {}
            if parser is not None:
                parser.add_argument('--version',
                                    action='version',
                                    version=progver)
                parser.add_argument('--mpmethod')
                args = parser.parse_args()
                windowparams = {k: args.__dict__[k] for k in parameters}
                if args.mpmethod and multiprocessing.get_start_method(
                        allow_none=True) is None:
                    multiprocessing.set_start_method(args.mpmethod)

            app = QApplication.instance()
            if not app:
                app = QApplication(sys.argv)
            add_clipboard_to_figures()
            window = windowclass(**windowparams)
            window.show()
            if not isChild:
                qApp.lastWindowClosed.connect(qApp.quit)
                res = app.exec_()

        except Exception:
            traceback.print_exc()
            print('Press some key to quit')
            input()
        if not isChild:
            sys.exit(res)
Exemple #15
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        loadUi("./ui/main_window.ui",self)

        self.setWindowIcon(QIcon('./ui/icon.png'))

        self.emsg = QErrorMessage()
        self.emsg.setWindowModality(QtCore.Qt.WindowModal)
        self.emsg.setWindowFlag(QtCore.Qt.WindowStaysOnTopHint)

        self.initUI()
        self.char_window = CharWindow()
        self.selector_window = SelectorWindow()

        self.b_Edit_Characters.clicked.connect(self.edit_chars)
        self.b_clear_dyn.clicked.connect(self.clear_dynamics)
        self.b_rand_dyn_miss.clicked.connect(self.rand_dynamics_missing)
        self.b_rand_dyn_all.clicked.connect(self.rand_dynamics_all)   

        self.b_rand_thr_all.clicked.connect(self.rand_throughlines_all) 
        self.b_rand_abs_all.clicked.connect(self.rand_abstracts_all)
        self.b_rand_acts_all.clicked.connect(self.rand_acts_all)

        self.b_rand_EVERYTHING.clicked.connect(self.rand_EVERYTHING)

        self.b_Crucial_Element.clicked.connect(self.b_crucial_clicked)
        self.b_clear_thro.clicked.connect(self.b_clear_throughlines)
        self.b_clear_abstracts.clicked.connect(lambda: self.reset_button_layout(self.grid_abstracts))
        self.b_clear_acts.clicked.connect(lambda: self.reset_button_layout(self.grid_acts))

    def initUI(self):

        # Load colors from config file
        for branch in root.children:
            change_style_color(branch.text, BG_COLOURS[branch.text], TEXT_COLOURS[branch.text])
        refresh_styles()

        self.actionNew.setShortcut('Ctrl+N')
        self.actionNew.setStatusTip('New document')
        self.actionNew.triggered.connect(self.newCall)

        self.actionOpen.setShortcut('Ctrl+O')
        self.actionOpen.setStatusTip('Open document')
        self.actionOpen.triggered.connect(self.openCall)

        self.actionSave.setShortcut('Ctrl+S')
        self.actionSave.setStatusTip('Save document')
        self.actionSave.triggered.connect(self.saveCall)

        self.actionExport_pdf.setShortcut('Ctrl+E')
        self.actionExport_pdf.setStatusTip('Export to .pdf')
        self.actionExport_pdf.triggered.connect(self.exportCall)

        self.update_char_layout()

        # Fill Throughline and Acts grids with buttons
        positions = [(i, j) for i in range(4) for j in range(4)]
        for i, pos in enumerate(positions):
            widget = QPushButton('th_'+str(i),self)
            widget.clicked.connect(self.b_throughline_clicked)
            widget.setText(BLANK_TEXT)
            self.grid_throughlines.addWidget(widget, *pos)

            widget = QPushButton('ac_'+str(i),self)
            widget.clicked.connect(self.b_acts_clicked)
            widget.setText(BLANK_TEXT)
            self.grid_acts.addWidget(widget,*pos)

        for i, button in enumerate(self.grid_abstracts.parentWidget().findChildren(QPushButton)):
            button.setText(BLANK_TEXT)
            button.clicked.connect(self.b_abstract_clicked)

        # GENERATE THE DYNAMICS TABLES WITH BUTTONS N SHIT
        positions = [(i, j) for i in range(8) for j in range(3)]
        for x in range(3):
            self.grid_c_dynamics.setColumnMinimumWidth(x,150)
            self.grid_p_dynamics.setColumnMinimumWidth(x,150)

        for position, name in zip (positions, c_dynamics):
            if name in dynamic_headings:
                widget = QLabel(name,self)
                widget.setFont(HEADING_FONT)
                widget.setAlignment(Qt.AlignCenter)
            else:
                widget = QPushButton(name,self)
                widget.setCheckable(True)
                widget.clicked.connect(self.opt_flip)
                widget.setObjectName(name)
            self.grid_c_dynamics.addWidget(widget, *position)
            
        for position, name in zip (positions, p_dynamics):
            if name in dynamic_headings:
                widget = QLabel(name,self)
                widget.setFont(HEADING_FONT)

                widget.setAlignment(Qt.AlignCenter)
            else:
                widget = QPushButton(name,self)
                widget.setCheckable(True)
                widget.clicked.connect(self.opt_flip)
                if self.findChild(QPushButton, name) == None:
                    widget.setObjectName(name)
                else:
                    widget.setObjectName(name+str(1))

            self.grid_p_dynamics.addWidget(widget, *position)

    def set_node_button(self, button, text, style):
        button.setText(text)
        button.setStyleSheet(style)
        button.setToolTip(get_tooltip(text))

    def b_clear_throughlines(self):
        self.reset_button_layout(self.grid_throughlines)
        self.set_node_button(self.b_Crucial_Element,BLANK_TEXT, "")

    def copy_throughline_extras(self):
        crucial_text = self.grid_throughlines.itemAtPosition(1,3).widget().text()

        if crucial_text != BLANK_TEXT:
            crucial_node = self.find_node(crucial_text, self.grid_throughlines.itemAtPosition(1,0).widget().text())
            
            self.set_node_button(self.b_Crucial_Element,crucial_node.text, crucial_node.style)
            self.set_node_button(self.grid_throughlines.itemAtPosition(0,3).widget(),crucial_node.text, crucial_node.style)

    def b_throughline_clicked(self, button=None):
        if not button:
            button = self.sender()
        else:
            print('prog exec')

        layout = button.parent().layout()
        idx = layout.indexOf(button)
        pos = layout.getItemPosition(idx)

        siblings = []
        cousins = []

        if pos[1] == 0: # if its a root branch
            cousins = root.children
            selected = []

            for y in range(4):
                text = layout.itemAtPosition(y,0).widget().text()
                if self.find_node(text): selected.append(self.find_node(text))

            for branch in cousins:
                if branch not in selected:
                    siblings.append(branch)

        else:
            branch_text = layout.itemAtPosition(pos[0],0).widget().text()
            parent_text = layout.itemAtPosition(pos[0],pos[1]-1).widget().text()

            try:
                parent_node = self.find_node(parent_text, branch_text, pos[1]-1)
                siblings.extend(parent_node.children)
                cousins = self.find_cousins(parent_node)
            except:
                print('exception')
                return
        try:
            self.toggle_selector(button, layout, idx, pos, cousins, siblings, self.chk_Legal_Only_Thro.checkState())
        except:
            return

    def b_crucial_clicked(self):
        layout = self.grid_throughlines

        branch_node_text = layout.itemAtPosition(1,0).widget().text()
        branch_node = self.find_node(branch_node_text)

        mc_problem_text = layout.itemAtPosition(1,3).widget().text()
        mc_problem_node = self.find_node(mc_problem_text, branch_node_text, 3)

        parent_node_text = layout.itemAtPosition(1,2).widget().text()
        if parent_node_text != BLANK_TEXT:
            try:
                siblings = []
                cousins = []
                parent_node = self.find_node(parent_node_text, branch_node_text, 2)

                siblings.append(mc_problem_node)

                cousins = self.find_cousins(parent_node)
                self.toggle_selector(self.sender(), layout, None, None, cousins, siblings, self.chk_Legal_Only_Thro.checkState())
            except:
                print('exception')
                return

    def b_abstract_clicked(self):
        button = self.sender()
        options_all = copy.copy(types)
        selected = []

        for b in self.grid_abstracts.parentWidget().findChildren(QPushButton):
            if b.text() != BLANK_TEXT:
                selected.append(self.find_node(b.text()))

        legal_options = []

        for o in options_all:
            if o in selected:
                continue
            legal_options.append(o)

        self.toggle_selector(button, self.grid_abstracts, None, None, options_all, legal_options, self.chk_Legal_Only_Abstracts.checkState())

    def b_acts_clicked(self, button=None):
        if not button:
            button = self.sender()
        else:
            print('prog exec')

        layout = button.parent().layout()
        idx = layout.indexOf(button)
        pos = layout.getItemPosition(idx)

        branch_text = self.grid_throughlines.itemAtPosition(pos[0],0).widget().text()
        branch = self.find_node(branch_text)

        if not branch:
            return

        acts_all = []
        acts_all.extend(branch.children)

        acts_picked = []
        for x in range(4):
            if x == pos[1]:
                continue
            picked_text = layout.itemAtPosition(pos[0],x).widget().text()
            if picked_text != BLANK_TEXT:
                acts_picked.append(self.find_node(picked_text))

        acts_legal = []
        for act in acts_all:
            if act not in acts_picked: 
                acts_legal.append(act)

        try:
            self.toggle_selector(button, layout, idx, pos, acts_all, acts_legal, self.chk_Legal_Only_Acts.checkState())
        except:
            return

    def toggle_selector(self, button=None, layout=None, idx=None, pos=None, cousins=None, siblings=None, bool_legal_only=False):
        global selector_node
        global selector_button

        if self.selector_window.isVisible():

            self.set_node_button(selector_button, selector_node.text, selector_node.style)
            self.selector_window.hide()
            self.setEnabled(True)
                 
            selector_button_layout = selector_button.parent().layout()
            
            if selector_button_layout == self.grid_throughlines:
                idx = selector_button_layout.indexOf(selector_button)
                pos = selector_button_layout.getItemPosition(idx)
                for x in range(pos[1]+1,4):
                    b = selector_button_layout.itemAtPosition(pos[0],x).widget()
                    self.set_node_button(b, BLANK_TEXT, "")

        else:
            selector_button = button
            self.selector_window.show() 
            self.selector_window.update_buttons(cousins, siblings, bool_legal_only)
            self.setEnabled(False)

    def find_cousins(self, parent_node):
        cousins = []

        if parent_node.level == 0:
            cousins.extend(parent_node.children)

        else:
            for gp_node in node_levels[parent_node.level-1]:
                for p_node in gp_node.children:
                    if p_node == parent_node:
                        grandparent = gp_node
                        break

            for au in grandparent.children:
                for c in au.children:
                    cousins.append(c)
        return cousins

    def find_chars(self, trait):
        data = []
        for c in characters:
            if trait in c.traits:
                data.append(c.name)
        return data

    def edit_chars(self):
        if self.char_window.isVisible():
            self.char_window.hide()
            self.show()
            self.setEnabled(True)
            
        else:
            self.char_window.show() 
            self.hide()
            self.setEnabled(False)
            self.char_window.update_char_list()

    def update_char_layout(self):
        global characters
        # Clear the layouts of any existing labels
        for label in self.layout_chars.parentWidget().findChildren(QLabel):
            label.setParent(None)

        for label in self.layout_traits.parentWidget().findChildren(QLabel):
            label.setParent(None)

        max_col = 3
        cur_col = 0
        cur_row = 0

        c_names = []
        c_traits = []

        for c in characters: 
            c_names.append(c.name)
            c_traits.append((", ").join(c.traits))

        for i, c in enumerate(c_names):
            c_widget = QLabel(c,self)
            c_widget.setFont(HEADING_FONT)
            c_widget.setAlignment(Qt.AlignCenter)
            c_widget.setText(c)
            c_widget.setFrameStyle(1)
            c_widget.setFrameShape(QFrame.Box)
            c_widget.setFrameShadow(QFrame.Plain)
            c_widget.setStyleSheet(COLOR_HEADING_BACKGROUND)
            
            
            t_widget = QLabel(c_traits[i],self)
            t_widget.setAlignment(Qt.AlignCenter)
            t_widget.setText(c_traits[i])
            t_widget.setFrameStyle(1)
            t_widget.setFrameShape(QFrame.Box)
            t_widget.setFrameShadow(QFrame.Plain)
            t_widget.setWordWrap(True)
            # t_widget.setMinimumSize(t_widget.sizeHint())

            self.layout_chars.addWidget(c_widget,cur_row,cur_col)
            self.layout_chars.addWidget(t_widget,cur_row+1,cur_col)
            
            if cur_col < max_col:
                cur_col += 1
            else:
                cur_col = 0
                cur_row += 2

        cur_col = 0
        cur_row = 0

        for trait in traits:
            t_widget = QLabel(trait,self)
            t_widget.setFont(HEADING_FONT)
            t_widget.setAlignment(Qt.AlignCenter)
            t_widget.setText(trait)
            t_widget.setFrameStyle(1)
            t_widget.setFrameShape(QFrame.Box)
            t_widget.setFrameShadow(QFrame.Plain)
            t_widget.setStyleSheet(COLOR_HEADING_BACKGROUND)
            t_widget.setToolTip(get_tooltip(trait))

            chars_w_trait = []
            for c in characters:
                if trait in c.traits:
                    chars_w_trait.append(c.name)
                    continue

            c_widget = QLabel(trait,self)
            c_widget.setAlignment(Qt.AlignCenter)
            c_widget.setText((", ").join(chars_w_trait))
            c_widget.setFrameStyle(1)
            c_widget.setFrameShape(QFrame.Box)
            c_widget.setFrameShadow(QFrame.Plain)
            c_widget.setWordWrap(True)

            self.layout_traits.addWidget(t_widget,cur_row,cur_col)
            self.layout_traits.addWidget(c_widget,cur_row+1,cur_col)

            if cur_col < max_col:
                cur_col += 1
            else:
                cur_col = 0
                cur_row += 2

    def rand_EVERYTHING(self):
        self.char_window.Randomize_Traits()
        self.update_char_layout()

        self.rand_dynamics_all()
        self.rand_throughlines_all()
        self.rand_abstracts_all()
        self.rand_acts_all()

# ========================== DYNAMICS ============================
    def opt_flip(self): #TURN THE OPPOSITE CLICKED BUTTON OFF.
        selected = self.sender().objectName()
        for options in dynamics.values():
            if selected in options:
                if options[0] == selected:
                    opposite = options[1]
                else:
                    opposite = options[0]
        self.findChild(QPushButton, opposite).setChecked(False)

    def get_dyn_buttons(self): #GET LIST OF BUTTONS IN THE DYNAMICS GRID FORMS
        c = []
        p = []
        for i in range(self.grid_c_dynamics.count()):
            if isinstance(self.grid_c_dynamics.itemAt(i).widget(), QPushButton):
                c.append(self.grid_c_dynamics.itemAt(i).widget())
            if isinstance(self.grid_p_dynamics.itemAt(i).widget(), QPushButton):
                p.append(self.grid_p_dynamics.itemAt(i).widget())
        c.extend(p)
        return c

    def read_dynamics_state(self):
        dyn_buttons = self.get_dyn_buttons()

        selections = dict.fromkeys(dynamics.keys()) 
        for key, options in dynamics.items():
            for item in dyn_buttons:
                if item.objectName() in options and item.isChecked():
                    selections[key] = item.objectName()
        return selections

    def clear_dynamics(self):
        dyn_buttons = self.get_dyn_buttons()
        for b in dyn_buttons:
            b.setChecked(False)

    def rand_dynamics_missing(self):
        selections = self.read_dynamics_state()
        dyn_buttons = self.get_dyn_buttons()

        for key, options in dynamics.items():
            if selections[key] == None:
                self.findChild(QPushButton, random.choice(options)).setChecked(True)

    def rand_dynamics_all(self):
        dyn_buttons = self.get_dyn_buttons()

        for b in dyn_buttons:
            b.setChecked(False)

        for key, options in dynamics.items():
            self.findChild(QPushButton, random.choice(options)).setChecked(True)

# ======================== THROUGHLINES ========================== 
    def gen_throughline(self, start_node):
        thro = []
        thro.append(random.choice(start_node.children))
        for i in range(1,3):
            pick = random.choice(thro[i-1].children)
            thro.append(pick)
        return thro
    
    def rand_throughlines_all(self):
        roots = []
        choices = list(root.children)

        # Initialize the throughline classes
        roots.append(random.choice(choices))
        roots.append(self.find_node(opposites[roots[0].text]))
        choices.remove(roots[0])
        choices.remove(roots[1])

        random.shuffle(choices)
        roots.extend(choices)
        
        # Reorder classes as Subjective Story needs to be last 
        roots.append(roots.pop(roots.index(roots[1])))

        # Generate 4 random throughlines with the above as seeds
        cb_throughlines = []
        for x in range(4):
            cb_throughlines.append(roots[x])
            cb_throughlines.extend(self.gen_throughline(roots[x]))

        # Overall Story problem is supposed to match the Main Character Problem
        try:
            cb_throughlines[3] = self.find_node(cb_throughlines[7].text, cb_throughlines[0].text)
        except:
            cb_throughlines[3] = cb_throughlines[7]


        for i, button in enumerate(self.grid_throughlines.parentWidget()
                                    .findChildren(QPushButton)):
            self.set_node_button(button, cb_throughlines[i].text, cb_throughlines[i].style)
        self.set_node_button(self.b_Crucial_Element,cb_throughlines[7].text, cb_throughlines[7].style)
        
    def find_node(self, string, branch=None, level=None):
        found = []
        if string == BLANK_TEXT: return

        for cl in root.children:
            if branch:
                if cl.text != branch:
                    continue
            if cl.text == string: 
                found.append(cl)
            for ty in cl.children:
                if ty.text == string: 
                    found.append(ty)
                for var in ty.children:
                    if var.text == string: 
                        found.append(var)
                    for elem in var.children:
                        if elem.text == string: 
                            found.append(elem)

        # error checking for multiple results. 
        if level and found:
            for node in found:
                if node.level == level:
                    found = [node]

        return found[0]

    def read_throughlines_state(self):
        content = []

        for i, button in enumerate(self.grid_throughlines.parentWidget()
                                    .findChildren(QPushButton)):
            content.append(button.text())

        return content

# ========================= ABSTRACTS ============================
    def rand_abstracts_all(self):
        choices = list(types)
        random.shuffle(choices)
        for i, button in enumerate(self.grid_abstracts.parentWidget()
                                    .findChildren(QPushButton)):
            self.set_node_button(button, choices[i].text, choices[i].style)

    def read_abstracts_state(self):
        content = []

        for i, button in enumerate(self.grid_abstracts.parentWidget()
                                    .findChildren(QPushButton)):
            content.append(button.text())

        return content

# =========================== ACTS ===============================
    def update_acts(self):
        thro = self.read_throughlines_state()
        branches = thro[0::4]

    def rand_acts_all(self):
        try:
            thro = self.read_throughlines_state()
            branch_names = thro[0::4]

            branch_nodes = []
            for name in branch_names:
                branch_nodes.append(self.find_node(name))

            acts = []
            for b in branch_nodes:
                choices = []
                for child in b.children:
                    choices.append(child)
                random.shuffle(choices)
                acts.extend(choices)

            for i, button in enumerate(self.grid_acts.parentWidget()
                                    .findChildren(QPushButton)):
                self.set_node_button(button, acts[i].text, acts[i].style)

        except:
            return

    def read_acts_state(self):
        content = []

        for i, button in enumerate(self.grid_acts.parentWidget()
                                    .findChildren(QPushButton)):
            content.append(button.text())

        return content
# =========================== SAVE ===============================
    def read_total_state(self):
        _data = {}
        _data['characters'] = []
        _data['character_names'] = []
        _data['character_traits'] = []
        _data['dynamics'] = []
        _data['throughlines'] = []
        _data['abstracts'] = []
        _data['acts'] = []        

        try:
            for c in characters:
                _data['characters'].append(jsonpickle.encode(c))
                _data['character_names'].append(c.name)
                _data['character_traits'].append(c.traits)

            for k, dyn in self.read_dynamics_state().items():
                _data['dynamics'].append(dyn)

            _data['throughlines'] = self.read_throughlines_state()

            _data['abstracts'] = self.read_abstracts_state()

            _data['acts'] = self.read_acts_state()

            return _data

        except:
            pass

    def saveCall(self):
        global FILE_PATH
        save_data = self.read_total_state()   

        if FILE_PATH == "":
            save_path = QFileDialog.getSaveFileName(self, 'Save File', "","JSON (*.json)")
        else:
            save_path = QFileDialog.getSaveFileName(self, 'Save File', FILE_PATH,"JSON (*.json)")
        
        if save_path[0]:
            with open(save_path[0], 'w') as outfile:
                json.dump(save_data,outfile)

            FILE_PATH = save_path[0]
            self.setWindowTitle("Story Randomizer - " + FILE_PATH)
        else:
            return

    def reset_button_layout(self,layout):
        try:
            for i, button in enumerate(layout.parentWidget()
                                        .findChildren(QPushButton)):
                    self.set_node_button(button,BLANK_TEXT, "")   
        except:
            return


    def newCall(self):
        global characters

        characters = []
        self.update_char_layout()

        for button in self.layout_dynamics.parentWidget().findChildren(QPushButton):
            button.setChecked(False)

        layouts = [self.grid_throughlines, self.grid_abstracts, self.grid_acts]

        for layout in layouts:
            self.reset_button_layout(layout)
            
        self.set_node_button(self.b_Crucial_Element, BLANK_TEXT, "")

    def openCall(self):
        global characters
        global FILE_PATH
        open_path = QFileDialog.getOpenFileName(self, 'Open File', '',"JSON (*.json)")
        
        if open_path[0]:
            with open(open_path[0]) as json_file:
                in_data = json.load(json_file)
        else:
            return 
        
        FILE_PATH = open_path[0]
        self.setWindowTitle("Story Randomizer - " + FILE_PATH)

        characters = []
        for c in in_data['characters']:
            characters.append(jsonpickle.decode(c))

        self.update_char_layout()

        for button in self.layout_dynamics.parentWidget().findChildren(QPushButton):
            button.setChecked(False)
            if button.text() in in_data['dynamics']: button.setChecked(True)

        for i, button in enumerate(self.grid_throughlines.parentWidget()
                                    .findChildren(QPushButton)):
            try:
                if i % 4 == 0:
                    branch = self.find_node(in_data['throughlines'][i])
                node = self.find_node(in_data['throughlines'][i],branch.text)
                self.set_node_button(button, node.text, node.style)
                if i == 7:
                    self.set_node_button(self.b_Crucial_Element, node.text, node.style)
            except:
                print("Error loading throughlines, save may be corrupted")

        for i, button in enumerate(self.grid_abstracts.parentWidget()
                                    .findChildren(QPushButton)):
            try:
                node = self.find_node(in_data['abstracts'][i])
                self.set_node_button(button, node.text, node.style)
            except:
                print("Error loading abstracts, save may be corrupted")

        for i, button in enumerate(self.grid_acts.parentWidget()
                                    .findChildren(QPushButton)):
            try:
                node = self.find_node(in_data['acts'][i])
                self.set_node_button(button, node.text, node.style)
            except:
                print("Error loading acts, save may be corrupted")

    def exportCall(self):
        try:
            out_data = self.read_total_state()

            save_path = QFileDialog.getSaveFileName(self, 'Save File', "","PDF (*.pdf)")

            if not save_path[0]:
                return

            with open('dump.json', 'w') as outfile:
                json.dump(out_data,outfile)

            pdf_report.save('dump.json', save_path)
        except:
            self.error_message('Export to pdf failed.')

    def error_message(self, text):
        self.emsg.setWindowTitle('Error')
        self.emsg.showMessage(text)
Exemple #16
0
class TestDialog(QDialog):
    def __init__(self, nlist_0, nlist_1):

        super(TestDialog, self).__init__()

        self.emsg = QErrorMessage(self)
        self.emsg.setWindowModality(Qt.WindowModal)
        self.emsg.children()[2].setVisible(False)

        data0 = self.dir_scan(nlist_0, "*.f90")
        nl_1 = deepcopy(data0)
        data1 = self.namelist_scan(nlist_1, nl_1)
        # TODO: need to make sure that all nam_block entries have same nam_items

        cols = ["col0", "col1"]

        # Copy input data for manipulation
        self.data = {}
        self.data["col0"] = deepcopy(data0)
        self.data["col1"] = deepcopy(data1)

        # TreeView for each data set
        self.tree = {}
        for col in cols:
            self.tree[col] = QTreeView()

        # Layout
        btOk = QPushButton("OK")
        btCancel = QPushButton("Cancel")
        btSearch = QPushButton("Search")
        self.searchInput = QLineEdit()

        # Search Box
        self.searchInput.setText(
            QCoreApplication.translate("Find a nam_block or nam_item",
                                       "Enter search text here"))
        btSearch.setToolTip(
            QCoreApplication.translate(
                "Find a nam_block or nam_item",
                "Enter search text here, press ENTER again to go to next match!",
            ))
        btSearch.clicked.connect(self.searchItem)

        # First row
        hbox1 = QHBoxLayout()
        hbox1.addStretch(1)
        hbox1.addWidget(btSearch, 100)
        # self.connect(self.btSearch, SIGNAL("returnPressed()"), self.searchItem)

        hbox1.addWidget(self.searchInput)
        # hbox1.addWidget(btSearch)
        hbox1.addWidget(btOk)
        hbox1.addWidget(btCancel)

        # self.button.clicked.connect(self.handleButton)

        # Second row
        hbox2 = QHBoxLayout()
        # hbox2.addStretch(1)
        for col in ["col0", "col1"]:
            hbox2.addWidget(self.tree[col])

        # Wrap both columns in a container
        vbox = QVBoxLayout()
        vbox.addLayout(hbox1)
        vbox.addLayout(hbox2)
        # vbox.addWidget(self.tree1)
        self.setLayout(vbox)
        self.setGeometry(300, 300, 800, 400)

        # Button signals
        btCancel.clicked.connect(self.reject)
        btOk.clicked.connect(self.accept)

        # Tree view

        for col in cols:
            col_next = cols[1 - cols.index(col)]
            self.tree[col].setModel(QStandardItemModel())
            self.tree[col].setAlternatingRowColors(True)
            # self.tree[col].model().setData(self.tree[col].model().index(1, 1),
            # QtGui.QBrush(QtGui.QColor(255, 0, 0)), QtCore.Qt.BackgroundRole)
            self.tree[col].setSortingEnabled(True)
            self.tree[col].setHeaderHidden(False)
            # self.tree[col].setColumnWidth(2, 30) # This does work!
            self.tree[col].setSelectionBehavior(QAbstractItemView.SelectItems)
            self.tree[col].model().setHorizontalHeaderLabels(
                ["Parameter", "Value", "Update"])
            self.tree[col].header().setSectionResizeMode(
                0, QHeaderView.Stretch)
            self.tree[col].header().setSectionResizeMode(
                1, QHeaderView.Stretch)
            self.tree[col].header().resizeSection(2, 50)
            self.tree[col].header().setSectionResizeMode(2, QHeaderView.Fixed)
            self.tree[col].header().setStretchLastSection(False)
            if col != "col0":
                #   self.tree[col].model().setHorizontalHeaderLabels(['Update', i
                #                                          'Parameter', 'Value'])
                self.tree[col].header().moveSection(2, 0)
            # self.tree1.model().horizontalHeader().setSectionResizeMode(2,
            # QHeaderView.ResizeToContents)

        for col in cols:
            for x in self.data[col]:
                if not self.data[col][x]:  # why did I write this in??
                    continue
                self._add_nam_block(x, col)

        for col in cols:
            col_next = cols[1 - cols.index(col)]
            # self.tree[col].model().itemChanged.connect(partial(self.handleItemChanged
            # , col))
            self.tree[col].expanded.connect(
                partial(self.handleExpanded, col, col_next))
            self.tree[col].collapsed.connect(
                partial(self.handleCollapsed, col, col_next))

        self.tree["col0"].verticalScrollBar().valueChanged.connect(
            self.tree["col1"].verticalScrollBar().setValue)
        self.tree["col1"].verticalScrollBar().valueChanged.connect(
            self.tree["col0"].verticalScrollBar().setValue)

        for col in cols:
            self.tree[col].model().itemChanged.connect(
                partial(self.handleItemChanged, col))

        # for col in cols:
        #    other = self.tree[col].model().findItems('nambdy',
        #    QtCore.Qt.MatchFixedString)
        #    newIndex=self.tree[col].model().indexFromItem(other[0])
        #    self.tree[col].scrollTo(newIndex, 3) # Centre

        # msg = QtWidgets.QMessageBox()
        # msg.setWindowModality(QtCore.Qt.WindowModal)
        # msg.setIcon(QtWidgets.QMessageBox.Critical)
        # msg.setText("Error")
        # msg.setInformativeText('More information')
        # msg.setWindowTitle("Error")
        # msg.exec_()

        # emsg.showMessage('Message: ')

    def _add_nam_block(self, nam_block, col):

        # TODO: run initial check on all the data to see if correct type

        # Set constants
        color_diff = QColor(205, 92, 92)
        color_miss = QColor(92, 92, 205)

        # Which namelist are we dealing with
        if col == "col0":
            col_alt = "col1"
            arrow = 4
        else:
            col_alt = "col0"
            arrow = 3

        # Local variables
        tree = self.tree[col]
        model = tree.model()
        nambk0 = self.data[col][nam_block]
        if nam_block in self.data[col_alt]:
            nambk1 = self.data[col_alt][nam_block]
        else:
            nambk1 = False

        # Add namelist block
        parent0 = QStandardItem(nam_block)  # Namelist header
        parent1 = QStandardItem()  # Not used
        parent2 = QStandardItem()  # Placeholder for QToolButton

        parent0.setFlags(Qt.NoItemFlags)
        parent1.setFlags(Qt.NoItemFlags)

        # Add row
        model.appendRow([parent0, parent1, parent2])

        parind0 = parent0.index()
        parind2 = parent2.index()

        # Add widget to handle the transfer of nam_block
        if not nambk1 or (nambk0 != nambk1):
            q_btn = QToolButton()
            # q_btn.setText(arrow)
            q_btn.setArrowType(arrow)
            q_btn.clicked.connect(partial(self.pass_block, parind0, col))
            tree.setIndexWidget(parind2, q_btn)

        if not nambk1:
            model.setData(parind0, color_miss, Qt.ForegroundRole)
        elif nambk0 != nambk1:
            model.setData(parind0, color_diff, Qt.ForegroundRole)

        # parent_item.setData("this is a parent", QtCore.Qt.ToolTipRole)

        # Add namelist item within block
        for nam_item in nambk0:

            # Retrieve value of namelist variable
            nam_val0 = nambk0[nam_item]

            # If nam_block exists in other namelist
            if nambk1:
                # TODO: temp fix is item isn't present
                if nam_item in nambk1:
                    nam_val1 = nambk1[nam_item]
                else:
                    nam_val1 = None

            child0 = QStandardItem(nam_item)
            child1 = QStandardItem(str(nam_val0))  # TODO: rm quotes from cn_
            child2 = QStandardItem()

            # child0.setFlags(QtCore.Qt.NoItemFlags |
            #                 QtCore.Qt.ItemIsEnabled)
            child0.setFlags(Qt.NoItemFlags)

            child1.setFlags(Qt.ItemIsEnabled | Qt.ItemIsEditable
                            | ~Qt.ItemIsSelectable)

            parent0.appendRow([child0, child1, child2])

            chiind0 = child0.index()
            chiind1 = child1.index()
            chiind2 = child2.index()

            # If bool then replace box with drop down
            if nam_item[0:2] == "ln":
                c_box = QComboBox()
                c_box.addItem("None")
                c_box.addItem(".true.")
                c_box.addItem(".false.")
                ind = c_box.findText(str(nam_val0), Qt.MatchFixedString)
                c_box.setCurrentIndex(ind)
                tree.setIndexWidget(chiind1, c_box)

            # TODO: maybe add arrows to all, just toggle visibility
            if nambk1 and (nam_val0 != nam_val1):
                q_btn = QToolButton()

                # q_btn.setText(arrow)
                q_btn.clicked.connect(
                    partial(self.pass_entry, col, nam_block, nam_item))
                q_btn.setArrowType(arrow)
                # q_btn.setEnabled(True)
                tree.setIndexWidget(chiind2, q_btn)

                model.setData(chiind0, color_diff, Qt.ForegroundRole)
            elif not nambk1:
                model.setData(chiind0, color_miss, Qt.ForegroundRole)
            else:
                q_btn = QToolButton()

                # q_btn.setText(arrow)
                q_btn.clicked.connect(
                    partial(self.pass_entry, col, nam_block, nam_item))
                q_btn.setArrowType(0)
                q_btn.setEnabled(False)
                tree.setIndexWidget(chiind2, q_btn)

    def handleItemChanged(self, col, item):

        # if col == "col0":
        #    col_alt = "col1"
        # else:
        #   col_alt = "col0"

        # tree = self.tree[col]
        # model = tree.model()
        # print(help(model.match))
        print("here****************** item changed")
        if item.parent() is None:
            print("Parent")
        else:
            # parent = self.data[col][item.parent().text()]
            # p2 = self.data[col_alt][item.parent().text()]

            nam_block = item.parent().text()

            nam_item = item.parent().child(item.row(), 0).text()

            # item_type = nam_block[0:2]
            item_val = item.text()  # same a nam_item!

            print(type(item_val))

            # app = QtWidgets.QApplication([])

            # error_dialog = QtWidgets.QErrorMessage()
            # error_dialog.showMessage('Oh no!')

            # app.exec_()
            print("headline   " + nam_block + " " + nam_item + " " + item_val +
                  " ")

            # TODO check item.text is right type or None
            # if it is do
            # parent[nam_block] = type(item.text())(item.text())

            # else keep the original value and flag somehow
            # color_chng = QColor(0, 0, 0)
            # color_chn1 = QColor(176, 176, 176)
            # Index = model.indexFromItem(item)

            # color_diff = QColor(205, 92, 92)
            # color_test = QColor(100, 0, 92)

            print("whos the daddy" + item.parent().text())

            if (nam_item != item_val
                ):  # need to work out how/why to stop the itemchanged call
                # when colors are updated

                self.data[col][nam_block][nam_item] = item_val

                self.color_update(col, nam_block, nam_item)

            # TODO error check with pop up if the value isn't compatible with the key

    def handleExpanded(self, col, col_next, idx):

        item = self.tree[col].model().itemFromIndex(idx)
        text = item.text()
        for other in self.tree[col_next].model().findItems(
                text, Qt.MatchFixedString):
            newIndex = self.tree[col_next].model().indexFromItem(other)
            self.tree[col_next].setExpanded(newIndex, True)

    def handleCollapsed(self, col, col_next, idx):
        item = self.tree[col].model().itemFromIndex(idx)
        text = item.text()
        for other in self.tree[col_next].model().findItems(
                text, Qt.MatchFixedString):
            newIndex = self.tree[col_next].model().indexFromItem(other)
            self.tree[col_next].setExpanded(newIndex, False)

    def get_data1(self):
        return self.data1

    def get_data2(self):
        return self.data2

    def pass_block(self, ind, col, idx):

        if col == "col0":
            col_alt = "col1"
        else:
            col_alt = "col0"

        tree = self.tree[col]
        model = tree.model()

        # item = model.itemFromIndex(idx)

        # nam_block = item.parent().child(item.row(), 0).text()

        # print(ind, idx)
        nam_block = model.itemFromIndex(ind).text()
        print(model.itemFromIndex(ind).text())

        # Map changes in the data
        self.data[col_alt][nam_block] = self.data[col][nam_block]

        for other in (self.tree[col_alt].model().findItems(
                nam_block, Qt.MatchFixedString | Qt.MatchRecursive)):
            for cnt in range(other.rowCount()):
                nam_item = other.child(cnt, 0).text()
                other.child(cnt, 1).setText(
                    str(self.data[col][nam_block][nam_item]))

        # self.secondColumn.parameter.setText(self)
        # item = self.tree['col0'].model().itemFromIndex(idx)
        # text = item.text()
        # for other in self.tree2.model().findItems(text, QtCore.Qt.MatchFixedString):
        #     newIndex=self.tree2.model().indexFromItem(other)
        # code to update value in column two

    def pass_entry(self, col, nam_block, nam_item, idx):

        # buttonClicked = self.sender()
        # postitionOfWidget = buttonClicked.pos()
        # print(postitionOfWidget)
        # print(dir(sending_button))
        # print(str(sending_button.objectName()))

        if col == "col0":
            col_alt = "col1"
        else:
            col_alt = "col0"

        # tree = self.tree[col]
        # model = tree.model()
        # parent = self.data[col][nam_block]
        # p2 = self.data[col_alt][nam_block]

        # nam_block = item.parent().text()

        # nam_item  = item.parent().child(item.row(), 0).text()

        # item_type = nam_block[0:2]
        # item_val  = item.text() # same a nam_item!

        # sender = self.sender()
        print(col, nam_block, nam_item, idx)
        print(self.data[col_alt][nam_block][nam_item])
        print(self.data[col][nam_block][nam_item])
        self.data[col_alt][nam_block][nam_item] = self.data[col][nam_block][
            nam_item]

        for other in (self.tree[col_alt].model().findItems(
                nam_block, Qt.MatchFixedString | Qt.MatchRecursive)):
            print("$$$$$$$$$$$$$$$$$$")
            print(other.child(0, 0).text())
            print(range(other.rowCount()))
            print(nam_item)
            for cnt in range(other.rowCount()):
                blk = other.child(cnt, 0).text()
                if blk == nam_item:
                    print("@@@@@@@@@@@@@@@@@ found it")
                    print(other.child(cnt, 0))
                    print(other.child(cnt, 1))
                    other.child(cnt, 1).setText(
                        str(self.data[col][nam_block][nam_item]))

            # item.parent().child(item.row(), 0).text()

        # print(other.takeRow(0)[0].text()    )

        # print(dir(other.takeRow(0)[0]))

        # TODO: do I need this as handleItemChanged maybe triggered anyway?
        self.color_update(col, nam_block, nam_item)

    def color_update(self, col, nam_block, nam_item):

        color_chng = QColor(0, 0, 0)
        color_chn1 = QColor(176, 176, 176)

        color_diff = QColor(205, 92, 92)
        # color_test = QColor(100, 0, 92)

        if col == "col0":
            col_alt = "col1"
        else:
            col_alt = "col0"

        # tree = self.tree[col]
        # model = tree.model()
        parent = self.data[col][nam_block]
        p2 = self.data[col_alt][nam_block]

        for other in (self.tree[col_alt].model().findItems(
                nam_item, Qt.MatchFixedString | Qt.MatchRecursive)):
            newIndex = self.tree[col_alt].model().indexFromItem(other)
            #    print('NEWINDEX')
            #     print(newIndex.row())
            #    print(newIndex.column())
            #     print(dir(newIndex))
            # TODO: sort out indexing
            #     self.tree[col_alt].model().setData(newIndex,str(self.data[col]
            # [nam_block][nam_item]),Qt.DisplayRole)
            if p2[nam_item] != parent[nam_item]:
                print("tttttttttttttttesting1a")
                print(p2[nam_item])
                print(parent[nam_item])
                print("****************Not equal1")
                self.tree[col_alt].model().setData(newIndex, color_diff,
                                                   Qt.ForegroundRole)
                # TODO: insert arrow
            else:
                print("tttttttttttttttesting1b")
                print(p2[nam_item])
                print(parent[nam_item])
                print("****************equal1")
                self.tree[col_alt].model().setData(newIndex, color_chng,
                                                   Qt.ForegroundRole)
                # TODO: remove arrow

        #              q_btn = item.parent().child(item.row(), 2)
        #              print(dir(item.parent()))
        #             print(item.parent().text())
        #             print(dir(item.parent().child(item.row(), 2)))
        #             print(item.parent().child(item.row(), 2).hasChildren())
        # q_btn = item.parent().child(item.row(), 2)
        # q_btn.data.setArrowType(0)
        # q_btn.setEnabled(False)

        # TODO: probably don't need the following for block
        for other in (self.tree[col].model().findItems(
                nam_item, Qt.MatchFixedString | Qt.MatchRecursive)):

            newI = self.tree[col].model().indexFromItem(other)
            print("We're in Row")
            print(newI.row())
            # other = self.tree['col1'].model().findItems(p2[nam_item],
            # Qt.MatchFixedString)
            # print(item)
            # newIndex=self.tree['col1'].model().indexFromItem(item)

            # if p2[nam_item] != parent[nam_item]:
            if p2[nam_item] != parent[nam_item]:
                print("tttttttttttttttesting2a")
                print(p2[nam_item])
                print(parent[nam_item])
                print("****************Not equal0")
                self.tree[col].model().setData(newI, color_diff,
                                               Qt.ForegroundRole)
            else:
                print("tttttttttttttttesting2b")
                print(p2[nam_item])
                print(parent[nam_item])
                print("****************equal0")
                self.tree[col].model().setData(newI, color_chng,
                                               Qt.ForegroundRole)
                # TODO: insert arrow

        pi0 = self.tree[col].model().findItems(nam_block, Qt.MatchFixedString)
        pi1 = self.tree[col_alt].model().findItems(nam_block,
                                                   Qt.MatchFixedString)
        in0 = self.tree[col].model().indexFromItem(pi0[0])
        in1 = self.tree[col_alt].model().indexFromItem(pi1[0])

        if self.data[col][nam_block] != self.data[col_alt][nam_block]:

            self.tree[col].model().setData(in0, color_diff, Qt.ForegroundRole)
            self.tree[col_alt].model().setData(in1, color_diff,
                                               Qt.ForegroundRole)
        else:
            self.tree[col].model().setData(in0, color_chn1, Qt.ForegroundRole)
            self.tree[col_alt].model().setData(in1, color_chn1,
                                               Qt.ForegroundRole)

        # TODO check item.text is right type or None
        # if it is do
        # parent[key] = type(item.text())(item.text())
        # self.secondColumn.parameter().setText(self)
        # item = self.tree1.model().itemFromIndex(idx)
        # text = item.text()
        # for other in self.tree2.model().findItems(text, QtCore.Qt.MatchFixedString):
        #    newIndex=self.tree2.model().indexFromItem(other)
        # code to update value in column two

    def searchItem(self):
        """execute the search and highlight the (next) result"""
        txt = str(self.searchInput.text())
        if txt != self.searchText:
            self.searchText = txt
            tmp = self.model.findItems(
                txt,
                Qt.MatchFixedString
                | Qt.MatchContains
                | Qt.MatchWildcard
                | Qt.MatchRecursive,
            )
            self.searchList = [i.index() for i in tmp]
        if self.searchList:
            mi = self.searchList.pop()
            self.treeView.setCurrentIndex(mi)
            self.treeView.expand(mi)
            self.treeView.scrollTo(mi)
        else:
            QMessageBox.information(
                self,
                QCoreApplication.translate("DataStorageBrowser",
                                           "No (more) matches!"),
                QCoreApplication.translate(
                    "DataStorageBrowser",
                    "No (more) matches found! Change you search text and try again!",
                ),
            )
            self.searchText = ""

    """
    file picker call back for output file input field
    """

    @pyqtSlot()
    def get_fname(self):
        # When you call getOpenFileName, a file picker dialog is created
        # and if the user selects a file, it's path is returned, and if not
        # (ie, the user cancels the operation) None is returned
        fname = QFileDialog.getSaveFileName(self,
                                            "Select output file",
                                            "",
                                            selectedFilter="*.ncml")[0]
        if fname:
            self.filename = fname  # returns a QString
            self.top_outfile_name.setText(str(fname))
            # print 'the output file is set to : ' + self.filename

    def dir_scan(self, bld_dir, ftype):

        pattern = re.compile("NAMELIST")
        nl = {}

        f90_files = glob.glob(bld_dir +
                              "*.f90")  # need to update to make recursive

        f90_files = glob.glob(bld_dir + "/**/*.[F,f]90", recursive=True)

        for fname in f90_files:
            for i, line in enumerate(open(fname, errors="ignore")):
                if re.search(pattern,
                             line):  # replace as NAMELIST only occurs once
                    # print(pattern, line)
                    nl_sub = collections.OrderedDict()
                    if "&" in line:
                        ln = line.rsplit("&")[0].strip()
                        count = i + 1
                        while ln[-1] == ",":
                            nxt = [
                                x for j, x in enumerate(open(fname)) if j in [
                                    count,
                                ]
                            ][0]
                            if nxt.count("&") >= 1 and nxt.strip()[0] == "&":
                                ln = ln + " " + nxt.rsplit("&")[1].strip()
                            elif nxt.count("&") == 1:
                                ln = ln + " " + nxt.rsplit("&")[0].strip()
                            else:
                                ln = ln + " " + nxt.strip()
                                # need to print('error') # proper handling of
                                # errors please
                            count += 1
                    nam_name = ln.rsplit("/")[1].strip()
                    nam_items = ln.rsplit("/")[2].strip()
                    nam_items = nam_items.rsplit("!")[0].strip()
                    nam_items = nam_items.rsplit(",")

                    for nam in range(len(nam_items)):
                        nl_sub[nam_items[nam].strip()] = None

                    # need a check in here to make sure that all identical i
                    # namelists have the same number of entries?
                    nl[nam_name] = nl_sub

        return nl

    def namelist_scan(self, namelist_in, nl_dict):

        nam_name = None
        nam_item = None

        pattern = re.compile("&nam")

        for i, line in enumerate(open(namelist_in)):
            if len(line.strip()) > 0:
                if re.search(pattern, line):
                    nam_name = line.rsplit()[0].strip()[1:]
                elif line.strip()[0] != "!" and line.strip()[0] != "/":
                    nam_item = line.rsplit()[0].strip()
                    nam_val = line.rsplit()[2].strip()
                if nl_dict.get(nam_name) is not None and nam_item is not None:
                    nl_dict[nam_name][
                        nam_item] = nam_val  # remember we may be adding new nam_vals here
                    # so need to check later
                    # print 'A:'+nam_name+': '+nam_item+' : '+nam_val
                elif nam_name is not None and nam_item is not None:
                    nl_dict[nam_name] = {}
                    nl_dict[nam_name][nam_item] = nam_val
                    # print 'B:'+nam_name+': '+nam_item+' : '+nam_val
                nam_item = None

        return nl_dict

    #   def namelist_write(self, namelist_out):

    def item_type(self, i):

        item_switcher = {
            "ln": bool,
            "nn": int,
            "rn": float,
            "cn": str,
            "sn": str
        }

        return item_switcher.get(i, self.emsg.showMessage("Message:"))
Exemple #17
0
class Example(QWidget):
    def __init__(self):
        super().__init__()

        self.error_message = QErrorMessage(self)
        self.error_message.setWindowModality(Qt.WindowModal)

        self.gui_dictionary = {key: None for key in default_values}
        self.initUI()

    def initUI(self):

        self.grid = QGridLayout()
        self.grid.setSpacing(10)

        for idx, (key, value) in enumerate(sorted(default_values.items())):
            text_label = key.replace('_', ' ').title()
            value_str = str(value)
            label = QLabel(text_label)
            text_box = QLineEdit(value_str)
            text_box.setAlignment(Qt.AlignCenter)
            self.grid.addWidget(label, idx + 1, 0)
            self.grid.addWidget(text_box, idx + 1, 1)
            self.gui_dictionary[key] = text_box

        self.create_video_button = QPushButton('Create Simulation')
        self.add_molecule_type = QPushButton('Add Molecule')

        self.molecules_table_model = MoleculeTableModel()
        self.molecules_table = self.molecules_table_model.table_widget

        self.create_video_button.clicked.connect(self.run_button_clicked)
        self.add_molecule_type.clicked.connect(self.add_molecule)

        self.grid.addWidget(self.add_molecule_type,
                            len(default_values) + 1, 0, 2, 1)
        self.grid.addWidget(self.molecules_table,
                            len(default_values) + 1, 1, 2, 1)
        self.grid.addWidget(self.create_video_button,
                            len(default_values) + 3, 0, 1, 2)

        self.progress_bar = QProgressBar()
        self.status_label = QLabel(STATUS_LABEL_INITAL_VALUE)
        self.grid.addWidget(self.progress_bar,
                            len(default_values) + 4, 1, 1, 1)
        self.grid.addWidget(self.status_label,
                            len(default_values) + 4, 0, 1, 1)

        self.setLayout(self.grid)
        self.setGeometry(ORIGINAL_POSITION_X, ORIGINAL_POSITION_Y,
                         WINDOW_WIDTH, WINDOW_HEIGHT)
        self.setWindowTitle('Molecule Magic Mixer')
        self.show()

    @pyqtSlot()
    def add_molecule(self):
        self.molecules_table_model.add_row()

    @pyqtSlot()
    def run_button_clicked(self):
        self.molecules_table_model.update_data()

        molecules_dictionaries = self.molecules_table_model.get_molecules(self)
        if molecules_dictionaries is None:
            """
                An Error Occurred
            """
            return

        setup_dictionary = self.create_setup_dictionary()
        if setup_dictionary is None:
            return

        options = QFileDialog.Options()
        filename, _ = QFileDialog.getSaveFileName(
            self,
            "QFileDialog.getSaveFileName()",
            "",
            "Tiff Files (*.tif)",
            options=options)

        if not filename:
            return

        self.init_progress_bar(len(molecules_dictionaries))
        self.set_enable_status(False)

        def worker():
            self.set_status_label("Running Simulations")
            simulations = []
            for index, molecule_dict in enumerate(molecules_dictionaries):
                simulation_dictionary = dict(setup_dictionary)
                simulation_dictionary.update(molecule_dict)
                simulation = Simulation(simulation_dictionary)
                simulation.run()
                simulations.append(simulation)
                self.progress_bar.setValue(index)

            multispecies_simulation = MultiSpeciesSimulation(*simulations)

            self.progress_bar.setValue(self.progress_bar.value() + 1)
            self.set_status_label("Creating Frames")
            multispecies_simulation.create_frames()

            self.progress_bar.setValue(self.progress_bar.value() + 1)
            self.set_status_label("Saving to file")
            multispecies_simulation.save_frames_to_file(filename)
            self.save_setup(setup_dictionary, molecules_dictionaries,
                            simulations, filename)

            # self.progress_bar.setValue(self.progress_bar.value() + 1)
            self.finish_progress_bar()
            self.set_status_label("Done!")
            self.set_enable_status(True)

        t = threading.Thread(target=worker)
        treads.append(t)
        t.start()

    def show_error(self, message):
        self.error_message.showMessage(message)
        self.set_enable_status(True)

    def set_enable_status(self, status):
        # for value in
        for widget in self.gui_dictionary.values():
            widget.setEnabled(status)
        self.molecules_table_model.table_widget.setEnabled(status)
        self.create_video_button.setEnabled(status)
        self.add_molecule_type.setEnabled(status)

    def init_progress_bar(self, number_of_molecules):
        self.progress_bar.setValue(0)
        self.progress_bar.setMinimum(0)
        self.progress_bar.setMaximum(number_of_molecules +
                                     NUMBER_OF_ANIMATION_STEPS)

    def finish_progress_bar(self):
        self.progress_bar.setMinimum(0)
        self.progress_bar.setMaximum(1)
        self.progress_bar.setValue(1)

    def set_status_label(self, text):
        self.status_label.setText(text)

    def create_setup_dictionary(self):
        setup_dictionary = {}
        for key, text_box in self.gui_dictionary.items():
            try:
                setup_dictionary[key] = types_dictionary[key](text_box.text())

            except Exception as e:
                self.show_error("Problem parsing {}, \n {}".format(
                    key, str(e)))
                return

        return setup_dictionary

    def create_simulation_dictionary(self, setup_dictionary,
                                     molecules_dictionaries, simulations):
        d = dict(setup_dictionary)
        molecules_dictionaries = list(
            [dict(d) for d in molecules_dictionaries])
        for molecule_dict, simulation in zip(molecules_dictionaries,
                                             simulations):
            molecule_dict[MOLECULES_KEY] = simulation.to_dict()[MOLECULES_KEY]
        d[MOLECULES_DICTIONARY_KEY] = molecules_dictionaries
        return d

    def save_setup(self, setup_dictionary, molecules_dictionaries, simulations,
                   filename):
        with open(filename + ".json", "w") as f:
            f.write(
                json.dumps(
                    self.create_simulation_dictionary(setup_dictionary,
                                                      molecules_dictionaries,
                                                      simulations)))