示例#1
0
def wrap_image(image: ImageView) -> QScrollArea:
    """Wraps an image in a scrollable and resizable container."""
    area = QScrollArea()
    area.setWidget(image)
    area.setWidgetResizable(True)
    area.setAlignment(Qt.AlignCenter)
    return area
示例#2
0
    def _widgets(self):
        splitter = QSplitter()

        # -- Left
        left_splitter = QSplitter(Qt.Vertical)
        splitter.addWidget(left_splitter)

        # Top left
        filetree = FileTree(self._root_path,
                            self._on_filetree_selection_changed)
        left_splitter.addWidget(filetree)

        # Bottom left
        tagging = Tagging()
        left_splitter.addWidget(tagging)

        # -- Right
        image = ImageDisplay()
        # Wrap it in a resizable scroll area
        area = QScrollArea()
        area.setWidget(image)
        area.setWidgetResizable(True)
        area.setAlignment(Qt.AlignCenter)
        splitter.addWidget(area)

        # A slight hack to split width equally
        splitter.setSizes([100000, 100000])

        return splitter, filetree, tagging, image
class ResultBlockViewWidget(QMainWindow):
    # Конструктор
    def __init__(self, block_name, result, parent=None):
        super(ResultBlockViewWidget, self).__init__(parent)
        self.setWindowTitle(f'Результаты выполнения блока \'{block_name}\'')
        self.setWindowModality(Qt.ApplicationModal)
        self.setMinimumWidth(640)
        self.central_widget = QWidget()
        self.scroll = QScrollArea()
        self.layout = QVBoxLayout(self.central_widget)
        self.init_ui(result)

    # Метод инициализации UI
    def init_ui(self, result):
        self.scroll.setAlignment(Qt.AlignCenter)
        self.layout.setAlignment(Qt.AlignTop)
        self.setCentralWidget(self.central_widget)
        self.layout.addWidget(self.scroll)
        widget = QWidget()
        layout = QVBoxLayout(widget)
        for i in range(0, len(result)):
            result_label = QLabel(f'Команда №{i + 1}: \'{result[i][1].strip()}\'.')
            result_label.setAlignment(Qt.AlignCenter)
            if result[i][0] == 0:
                result_label.setStyleSheet('background-color: ForestGreen;')
            else:
                result_label.setStyleSheet('background-color: OrangeRed;')
            layout.addWidget(result_label)
        self.scroll.setWidget(widget)
示例#4
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        openmc.capi.init(['-c'])

        self.setWindowTitle('OpenMC Plot Explorer')

        self.restored = False
        self.pixmap = None
        self.zoom = 100

        self.model = PlotModel()
        self.updateRelativeBases()
        self.restoreModelSettings()

        self.cellsModel = DomainTableModel(self.model.activeView.cells)
        self.materialsModel = DomainTableModel(self.model.activeView.materials)

        # Create viewing area
        self.frame = QScrollArea(self)
        self.frame.setAlignment(QtCore.Qt.AlignCenter)
        self.frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.setCentralWidget(self.frame)

        # Create plot image
        self.plotIm = PlotImage(self.model, self.frame, self)
        self.frame.setWidget(self.plotIm)

        # Dock
        self.dock = OptionsDock(self.model, FM, self)
        self.dock.setObjectName("OptionsDock")
        self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.dock)

        # Color Dialog
        self.colorDialog = ColorDialog(self.model, FM, self)
        self.colorDialog.hide()

        # Restore Window Settings
        self.restoreWindowSettings()

        # Create menubar
        self.createMenuBar()

        # Status Bar
        self.coord_label = QLabel()
        self.statusBar().addPermanentWidget(self.coord_label)
        self.coord_label.hide()

        # Load Plot
        self.statusBar().showMessage('Generating Plot...')
        self.dock.updateDock()
        self.colorDialog.updateDialogValues()

        if self.restored:
            self.showCurrentView()
        else:
            # Timer allows GUI to render before plot finishes loading
            QtCore.QTimer.singleShot(0, self.model.generatePlot)
            QtCore.QTimer.singleShot(0, self.showCurrentView)

    # Create and update menus:
    def createMenuBar(self):
        self.mainMenu = self.menuBar()

        # File Menu
        self.saveImageAction = QAction("&Save Image As...", self)
        self.saveImageAction.setShortcut("Ctrl+Shift+S")
        self.saveImageAction.setToolTip('Save plot image')
        self.saveImageAction.setStatusTip('Save plot image')
        self.saveImageAction.triggered.connect(self.saveImage)

        self.saveViewAction = QAction("Save &View Settings...", self)
        self.saveViewAction.setShortcut(QtGui.QKeySequence.Save)
        self.saveViewAction.setStatusTip('Save current view settings')
        self.saveViewAction.triggered.connect(self.saveView)

        self.openAction = QAction("&Open View Settings...", self)
        self.openAction.setShortcut(QtGui.QKeySequence.Open)
        self.openAction.setToolTip('Open saved view settings')
        self.openAction.setStatusTip('Open saved view settings')
        self.openAction.triggered.connect(self.openView)

        self.quitAction = QAction("&Quit", self)
        self.quitAction.setShortcut(QtGui.QKeySequence.Quit)
        self.quitAction.setToolTip('Quit OpenMC Plot Explorer')
        self.quitAction.setStatusTip('Quit OpenMC Plot Explorer')
        self.quitAction.triggered.connect(self.close)

        self.fileMenu = self.mainMenu.addMenu('&File')
        self.fileMenu.addAction(self.saveImageAction)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.saveViewAction)
        self.fileMenu.addAction(self.openAction)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.quitAction)

        # Edit Menu
        self.applyAction = QAction("&Apply Changes", self)
        self.applyAction.setShortcut("Shift+Return")
        self.applyAction.setToolTip('Generate new view with changes applied')
        self.applyAction.setStatusTip('Generate new view with changes applied')
        self.applyAction.triggered.connect(self.applyChanges)

        self.undoAction = QAction('&Undo', self)
        self.undoAction.setShortcut(QtGui.QKeySequence.Undo)
        self.undoAction.setToolTip('Undo')
        self.undoAction.setStatusTip('Undo last plot view change')
        self.undoAction.setDisabled(True)
        self.undoAction.triggered.connect(self.undo)

        self.redoAction = QAction('&Redo', self)
        self.redoAction.setDisabled(True)
        self.redoAction.setToolTip('Redo')
        self.redoAction.setStatusTip('Redo last plot view change')
        self.redoAction.setShortcut(QtGui.QKeySequence.Redo)
        self.redoAction.triggered.connect(self.redo)

        self.restoreAction = QAction("&Restore Default Plot", self)
        self.restoreAction.setShortcut("Ctrl+R")
        self.restoreAction.setToolTip('Restore to default plot view')
        self.restoreAction.setStatusTip('Restore to default plot view')
        self.restoreAction.triggered.connect(self.restoreDefault)

        self.editMenu = self.mainMenu.addMenu('&Edit')
        self.editMenu.addAction(self.applyAction)
        self.editMenu.addSeparator()
        self.editMenu.addAction(self.undoAction)
        self.editMenu.addAction(self.redoAction)
        self.editMenu.addSeparator()
        self.editMenu.addAction(self.restoreAction)
        self.editMenu.addSeparator()
        self.editMenu.aboutToShow.connect(self.updateEditMenu)

        # Edit -> Basis Menu
        self.xyAction = QAction('&xy  ', self)
        self.xyAction.setCheckable(True)
        self.xyAction.setShortcut('Alt+X')
        self.xyAction.setToolTip('Change to xy basis')
        self.xyAction.setStatusTip('Change to xy basis')
        self.xyAction.triggered.connect(
            lambda: self.editBasis('xy', apply=True))

        self.xzAction = QAction('x&z  ', self)
        self.xzAction.setCheckable(True)
        self.xzAction.setShortcut('Alt+Z')
        self.xzAction.setToolTip('Change to xz basis')
        self.xzAction.setStatusTip('Change to xz basis')
        self.xzAction.triggered.connect(
            lambda: self.editBasis('xz', apply=True))

        self.yzAction = QAction('&yz  ', self)
        self.yzAction.setCheckable(True)
        self.yzAction.setShortcut('Alt+Y')
        self.yzAction.setToolTip('Change to yz basis')
        self.yzAction.setStatusTip('Change to yz basis')
        self.yzAction.triggered.connect(
            lambda: self.editBasis('yz', apply=True))

        self.basisMenu = self.editMenu.addMenu('&Basis')
        self.basisMenu.addAction(self.xyAction)
        self.basisMenu.addAction(self.xzAction)
        self.basisMenu.addAction(self.yzAction)
        self.basisMenu.aboutToShow.connect(self.updateBasisMenu)

        # Edit -> Color By Menu
        self.cellAction = QAction('&Cell', self)
        self.cellAction.setCheckable(True)
        self.cellAction.setShortcut('Alt+C')
        self.cellAction.setToolTip('Color by cell')
        self.cellAction.setStatusTip('Color plot by cell')
        self.cellAction.triggered.connect(
            lambda: self.editColorBy('cell', apply=True))

        self.materialAction = QAction('&Material', self)
        self.materialAction.setCheckable(True)
        self.materialAction.setShortcut('Alt+M')
        self.materialAction.setToolTip('Color by material')
        self.materialAction.setStatusTip('Color plot by material')
        self.materialAction.triggered.connect(
            lambda: self.editColorBy('material', apply=True))

        self.colorbyMenu = self.editMenu.addMenu('&Color By')
        self.colorbyMenu.addAction(self.cellAction)
        self.colorbyMenu.addAction(self.materialAction)
        self.colorbyMenu.aboutToShow.connect(self.updateColorbyMenu)

        self.editMenu.addSeparator()

        self.maskingAction = QAction('Enable &Masking', self)
        self.maskingAction.setShortcut('Ctrl+M')
        self.maskingAction.setCheckable(True)
        self.maskingAction.setToolTip('Toggle masking')
        self.maskingAction.setStatusTip('Toggle whether masking is enabled')
        self.maskingAction.triggered[bool].connect(
            lambda bool=bool: self.toggleMasking(bool, apply=True))
        self.editMenu.addAction(self.maskingAction)

        self.highlightingAct = QAction('Enable High&lighting', self)
        self.highlightingAct.setShortcut('Ctrl+L')
        self.highlightingAct.setCheckable(True)
        self.highlightingAct.setToolTip('Toggle highlighting')
        self.highlightingAct.setStatusTip(
            'Toggle whether highlighting is enabled')
        self.highlightingAct.triggered[bool].connect(
            lambda bool=bool: self.toggleHighlighting(bool, apply=True))
        self.editMenu.addAction(self.highlightingAct)

        # View Menu
        self.dockAction = QAction('Hide &Dock', self)
        self.dockAction.setShortcut("Ctrl+D")
        self.dockAction.setToolTip('Toggle dock visibility')
        self.dockAction.setStatusTip('Toggle dock visibility')
        self.dockAction.triggered.connect(self.toggleDockView)

        self.zoomAction = QAction('&Zoom...', self)
        self.zoomAction.setShortcut('Alt+Shift+Z')
        self.zoomAction.setToolTip('Edit zoom factor')
        self.zoomAction.setStatusTip('Edit zoom factor')
        self.zoomAction.triggered.connect(self.editZoomAct)

        self.viewMenu = self.mainMenu.addMenu('&View')
        self.viewMenu.addAction(self.dockAction)
        self.viewMenu.addSeparator()
        self.viewMenu.addAction(self.zoomAction)
        self.viewMenu.aboutToShow.connect(self.updateViewMenu)

        # Window Menu
        self.mainWindowAction = QAction('&Main Window', self)
        self.mainWindowAction.setShortcut('Alt+W')
        self.mainWindowAction.setCheckable(True)
        self.mainWindowAction.setToolTip('Bring main window to front')
        self.mainWindowAction.setStatusTip('Bring main window to front')
        self.mainWindowAction.triggered.connect(self.showMainWindow)

        self.colorDialogAction = QAction('Color &Options', self)
        self.colorDialogAction.setShortcut('Alt+D')
        self.colorDialogAction.setCheckable(True)
        self.colorDialogAction.setToolTip('Bring Color Dialog to front')
        self.colorDialogAction.setStatusTip('Bring Color Dialog to front')
        self.colorDialogAction.triggered.connect(self.showColorDialog)

        self.windowMenu = self.mainMenu.addMenu('&Window')
        self.windowMenu.addAction(self.mainWindowAction)
        self.windowMenu.addAction(self.colorDialogAction)
        self.windowMenu.aboutToShow.connect(self.updateWindowMenu)

    def updateEditMenu(self):
        changed = self.model.currentView != self.model.defaultView
        self.restoreAction.setDisabled(not changed)

        self.maskingAction.setChecked(self.model.currentView.masking)
        self.highlightingAct.setChecked(self.model.currentView.highlighting)

        self.undoAction.setText(f'&Undo ({len(self.model.previousViews)})')
        self.redoAction.setText(f'&Redo ({len(self.model.subsequentViews)})')

    def updateBasisMenu(self):
        self.xyAction.setChecked(self.model.currentView.basis == 'xy')
        self.xzAction.setChecked(self.model.currentView.basis == 'xz')
        self.yzAction.setChecked(self.model.currentView.basis == 'yz')

    def updateColorbyMenu(self):
        cv = self.model.currentView
        self.cellAction.setChecked(cv.colorby == 'cell')
        self.materialAction.setChecked(cv.colorby == 'material')

    def updateViewMenu(self):
        if self.dock.isVisible():
            self.dockAction.setText('Hide &Dock')
        else:
            self.dockAction.setText('Show &Dock')

    def updateWindowMenu(self):
        self.colorDialogAction.setChecked(self.colorDialog.isActiveWindow())
        self.mainWindowAction.setChecked(self.isActiveWindow())

    # Menu and shared methods:

    def saveImage(self):
        filename, ext = QFileDialog.getSaveFileName(self, "Save Plot Image",
                                                    "untitled",
                                                    "Images (*.png *.ppm)")
        if filename:
            if "." not in filename:
                filename += ".png"
            self.plotIm.figure.savefig(filename, transparent=True)
            self.statusBar().showMessage('Plot Image Saved', 5000)

    def saveView(self):
        filename, ext = QFileDialog.getSaveFileName(self, "Save View Settings",
                                                    "untitled",
                                                    "View Settings (*.pltvw)")
        if filename:
            if "." not in filename:
                filename += ".pltvw"

            saved = {
                'default': self.model.defaultView,
                'current': self.model.currentView
            }

            with open(filename, 'wb') as file:
                pickle.dump(saved, file)

    def openView(self):
        filename, ext = QFileDialog.getOpenFileName(self, "Open View Settings",
                                                    ".", "*.pltvw")
        if filename:
            try:
                with open(filename, 'rb') as file:
                    saved = pickle.load(file)
            except Exception:
                message = 'Error loading plot settings'
                saved = {'default': None, 'current': None}
            if saved['default'] == self.model.defaultView:
                self.model.activeView = saved['current']
                self.dock.updateDock()
                self.applyChanges()
                message = f'{filename} settings loaded'
            else:
                message = 'Error loading plot settings. Incompatible model.'
            self.statusBar().showMessage(message, 5000)

    def applyChanges(self):
        if self.model.activeView != self.model.currentView:
            self.statusBar().showMessage('Generating Plot...')
            QApplication.processEvents()

            self.model.storeCurrent()
            self.model.subsequentViews = []
            self.model.generatePlot()
            self.resetModels()
            self.showCurrentView()

        else:
            self.statusBar().showMessage('No changes to apply.', 3000)

    def undo(self):
        self.statusBar().showMessage('Generating Plot...')
        QApplication.processEvents()

        self.model.undo()
        self.resetModels()
        self.showCurrentView()
        self.dock.updateDock()
        self.colorDialog.updateDialogValues()

        if not self.model.previousViews:
            self.undoAction.setDisabled(True)
        self.redoAction.setDisabled(False)

    def redo(self):
        self.statusBar().showMessage('Generating Plot...')
        QApplication.processEvents()

        self.model.redo()
        self.resetModels()
        self.showCurrentView()
        self.dock.updateDock()
        self.colorDialog.updateDialogValues()

        if not self.model.subsequentViews:
            self.redoAction.setDisabled(True)
        self.undoAction.setDisabled(False)

    def restoreDefault(self):
        if self.model.currentView != self.model.defaultView:

            self.statusBar().showMessage('Generating Plot...')
            QApplication.processEvents()

            self.model.storeCurrent()
            self.model.activeView = copy.deepcopy(self.model.defaultView)
            self.model.generatePlot()
            self.resetModels()
            self.showCurrentView()
            self.dock.updateDock()
            self.colorDialog.updateDialogValues()

            self.model.subsequentViews = []

    def editBasis(self, basis, apply=False):
        self.model.activeView.basis = basis
        self.dock.updateBasis()
        if apply:
            self.applyChanges()

    def editColorBy(self, domain_kind, apply=False):
        self.model.activeView.colorby = domain_kind
        self.dock.updateColorBy()
        self.colorDialog.updateColorBy()
        if apply:
            self.applyChanges()

    def toggleMasking(self, state, apply=False):
        self.model.activeView.masking = bool(state)
        self.colorDialog.updateMasking()
        if apply:
            self.applyChanges()

    def toggleHighlighting(self, state, apply=False):
        self.model.activeView.highlighting = bool(state)
        self.colorDialog.updateHighlighting()
        if apply:
            self.applyChanges()

    def toggleDockView(self):
        if self.dock.isVisible():
            self.dock.hide()
            if not self.isMaximized() and not self.dock.isFloating():
                self.resize(self.width() - self.dock.width(), self.height())
        else:
            self.dock.setVisible(True)
            if not self.isMaximized() and not self.dock.isFloating():
                self.resize(self.width() + self.dock.width(), self.height())
        self.resizePixmap()
        self.showMainWindow()

    def editZoomAct(self):
        percent, ok = QInputDialog.getInt(self, "Edit Zoom", "Zoom Percent:",
                                          self.dock.zoomBox.value(), 25, 2000)
        if ok:
            self.dock.zoomBox.setValue(percent)

    def editZoom(self, value):
        self.zoom = value
        self.resizePixmap()
        self.dock.zoomBox.setValue(value)

    def showMainWindow(self):
        self.raise_()
        self.activateWindow()

    def showColorDialog(self):
        self.colorDialog.show()
        self.colorDialog.raise_()
        self.colorDialog.activateWindow()

    # Dock methods:

    def editSingleOrigin(self, value, dimension):
        self.model.activeView.origin[dimension] = value

    def editPlotAlpha(self, value):
        self.model.activeView.plotAlpha = value
        self.dock.updatePlotAlpha()

    def editWidth(self, value):
        self.model.activeView.width = value
        self.onRatioChange()
        self.dock.updateWidth()

    def editHeight(self, value):
        self.model.activeView.height = value
        self.onRatioChange()
        self.dock.updateHeight()

    def toggleAspectLock(self, state):
        self.model.activeView.aspectLock = bool(state)
        self.onRatioChange()
        self.dock.updateAspectLock()

    def editVRes(self, value):
        self.model.activeView.v_res = value
        self.dock.updateVRes()

    def editHRes(self, value):
        self.model.activeView.h_res = value
        self.onRatioChange()
        self.dock.updateHRes()

    # Color dialog methods:

    def editMaskingColor(self):
        current_color = self.model.activeView.maskBackground
        dlg = QColorDialog(self)

        dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color))
        if dlg.exec_():
            new_color = dlg.currentColor().getRgb()[:3]
            self.model.activeView.maskBackground = new_color
            self.colorDialog.updateMaskingColor()

    def editHighlightColor(self):
        current_color = self.model.activeView.highlightBackground
        dlg = QColorDialog(self)

        dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color))
        if dlg.exec_():
            new_color = dlg.currentColor().getRgb()[:3]
            self.model.activeView.highlightBackground = new_color
            self.colorDialog.updateHighlightColor()

    def editAlpha(self, value):
        self.model.activeView.highlightAlpha = value

    def editSeed(self, value):
        self.model.activeView.highlightSeed = value

    def editBackgroundColor(self, apply=False):
        current_color = self.model.activeView.plotBackground
        dlg = QColorDialog(self)

        dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color))
        if dlg.exec_():
            new_color = dlg.currentColor().getRgb()[:3]
            self.model.activeView.plotBackground = new_color
            self.colorDialog.updateBackgroundColor()

        if apply:
            self.applyChanges()

    # Plot image methods

    def editPlotOrigin(self, xOr, yOr, zOr=None, apply=False):
        if zOr != None:
            self.model.activeView.origin = [xOr, yOr, zOr]
        else:
            origin = [None, None, None]
            origin[self.xBasis] = xOr
            origin[self.yBasis] = yOr
            origin[self.zBasis] = self.model.activeView.origin[self.zBasis]
            self.model.activeView.origin = origin

        self.dock.updateOrigin()

        if apply:
            self.applyChanges()

    def revertDockControls(self):
        self.dock.revertToCurrent()

    def editDomainColor(self, kind, id):
        if kind == 'Cell':
            domain = self.model.activeView.cells
        else:
            domain = self.model.activeView.materials

        current_color = domain[id].color
        dlg = QColorDialog(self)

        if isinstance(current_color, tuple):
            dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color))
        elif isinstance(current_color, str):
            current_color = openmc.plots._SVG_COLORS[current_color]
            dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color))
        if dlg.exec_():
            new_color = dlg.currentColor().getRgb()[:3]
            domain[id].color = new_color

        self.applyChanges()

    def toggleDomainMask(self, state, kind, id):
        if kind == 'Cell':
            domain = self.model.activeView.cells
        else:
            domain = self.model.activeView.materials

        domain[id].masked = bool(state)
        self.applyChanges()

    def toggleDomainHighlight(self, state, kind, id):
        if kind == 'Cell':
            domain = self.model.activeView.cells
        else:
            domain = self.model.activeView.materials

        domain[id].highlighted = bool(state)
        self.applyChanges()

    # Helper methods:

    def restoreWindowSettings(self):
        settings = QtCore.QSettings()

        self.resize(settings.value("mainWindow/Size", QtCore.QSize(800, 600)))
        self.move(
            settings.value("mainWindow/Position", QtCore.QPoint(100, 100)))
        self.restoreState(settings.value("mainWindow/State"))

        self.colorDialog.resize(
            settings.value("colorDialog/Size", QtCore.QSize(400, 500)))
        self.colorDialog.move(
            settings.value("colorDialog/Position", QtCore.QPoint(600, 200)))
        self.colorDialog.setVisible(
            bool(settings.value("colorDialog/Visible", 0)))

    def restoreModelSettings(self):
        if os.path.isfile("plot_settings.pkl"):

            with open('plot_settings.pkl', 'rb') as file:
                model = pickle.load(file)

            if model.defaultView == self.model.defaultView:
                self.model.currentView = model.currentView
                self.model.activeView = copy.deepcopy(model.currentView)
                self.model.previousViews = model.previousViews
                self.model.subsequentViews = model.subsequentViews
                if os.path.isfile('plot_ids.binary') \
                    and os.path.isfile('plot.ppm'):
                    self.restored = True

    def resetModels(self):
        self.cellsModel = DomainTableModel(self.model.activeView.cells)
        self.materialsModel = DomainTableModel(self.model.activeView.materials)
        self.cellsModel.beginResetModel()
        self.cellsModel.endResetModel()
        self.materialsModel.beginResetModel()
        self.materialsModel.endResetModel()
        self.colorDialog.updateDomainTabs()

    def showCurrentView(self):
        self.resizePixmap()
        self.updateScale()
        self.updateRelativeBases()

        if self.model.previousViews:
            self.undoAction.setDisabled(False)
        if self.model.subsequentViews:
            self.redoAction.setDisabled(False)
        else:
            self.redoAction.setDisabled(True)

        self.statusBar().showMessage('Done', 1000)
        self.adjustWindow()

    def updateScale(self):
        cv = self.model.currentView
        self.scale = (cv.h_res / cv.width, cv.v_res / cv.height)

    def updateRelativeBases(self):
        cv = self.model.currentView
        self.xBasis = 0 if cv.basis[0] == 'x' else 1
        self.yBasis = 1 if cv.basis[1] == 'y' else 2
        self.zBasis = 3 - (self.xBasis + self.yBasis)

    def adjustWindow(self):
        self.screen = app.desktop().screenGeometry()
        self.setMaximumSize(self.screen.width(), self.screen.height())

    def onRatioChange(self):
        av = self.model.activeView
        if av.aspectLock:
            ratio = av.width / max(av.height, .001)
            av.v_res = int(av.h_res / ratio)
            self.dock.updateVRes()

    def showCoords(self, xPlotPos, yPlotPos):
        cv = self.model.currentView
        if cv.basis == 'xy':
            coords = (f"({round(xPlotPos, 2)}, {round(yPlotPos, 2)}, "
                      f"{round(cv.origin[2], 2)})")
        elif cv.basis == 'xz':
            coords = (f"({round(xPlotPos, 2)}, {round(cv.origin[1], 2)}, "
                      f"{round(yPlotPos, 2)})")
        else:
            coords = (f"({round(cv.origin[0], 2)}, {round(xPlotPos, 2)}, "
                      f"{round(yPlotPos, 2)})")
        self.coord_label.setText(f'{coords}')

    def resizePixmap(self):
        z = self.zoom / 100.
        self.plotIm.setPixmap(self.frame.width() * z, self.frame.height() * z)
        self.plotIm.adjustSize()

    def moveEvent(self, event):
        self.adjustWindow()

    def resizeEvent(self, event):
        if self.pixmap:
            self.adjustWindow()
            self.updateScale()

    def closeEvent(self, event):
        settings = QtCore.QSettings()
        settings.setValue("mainWindow/Size", self.size())
        settings.setValue("mainWindow/Position", self.pos())
        settings.setValue("mainWindow/State", self.saveState())

        settings.setValue("colorDialog/Size", self.colorDialog.size())
        settings.setValue("colorDialog/Position", self.colorDialog.pos())
        visible = int(self.colorDialog.isVisible())
        settings.setValue("colorDialog/Visible", visible)

        if len(self.model.previousViews) > 10:
            self.model.previousViews = self.model.previousViews[-10:]
        if len(self.model.subsequentViews) > 10:
            self.model.subsequentViews = self.model.subsequentViews[-10:]

        with open('plot_settings.pkl', 'wb') as file:
            pickle.dump(self.model, file)
示例#5
0
class SlideShow(QObject):
    current_index_changed = Signal(int)
    clicked = Signal()
    key_pressed = Signal(QEvent)

    def __init__(self, parent, base_widget, is_animated=True):
        QObject.__init__(self, parent)
        self._base_widget = base_widget  # has to be stacked widget
        self._is_animated = is_animated
        self._slides_count = self._base_widget.count()

        self._scroll_area = QScrollArea(parent)
        self._scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self._scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self._scroll_area.setFrameShape(QFrame.NoFrame)
        self._scroll_area.mouseReleaseEvent = self._on_mouse_release_event
        self._scroll_area.keyPressEvent = self._on_key_pressed

        self._slide_width = None
        self._current_index = 0
        self._is_moving = False
        self._orig_resize_event = self._base_widget.resizeEvent
        self._base_widget.resizeEvent = self._resizeEvent

        self._animation_time = 300
        self._animation_steps = 50

    def _construct_ribbon(self):
        self._ribbon = QWidget()

        self._layout = QHBoxLayout()
        self._ribbon.setLayout(self._layout)

        x = 0
        for i in range(self._slides_count):
            self._base_widget.setCurrentIndex(i)
            widget = self._base_widget.currentWidget()
            if widget:
                new_widget = self._grab(widget)
                self._layout.addWidget(new_widget)
                x += self._slide_width

    def _grab(self, widget):
        new_widget = QLabel()
        pixmap = widget.grab()
        new_widget.setPixmap(pixmap)
        return new_widget

    def _resizeEvent(self, *args, **kwargs):
        self._orig_resize_event(*args, **kwargs)
        if not self._slide_width:
            self._scroll_area.setGeometry(self._base_widget.geometry())
            self._slide_width = self._base_widget.widget(0).width()
            QTimer.singleShot(50, self._show)

    def _show(self):
        self._construct_ribbon()
        self._scroll_area.setWidget(self._ribbon)
        self._scroll_area.setAlignment(Qt.AlignCenter)
        self._scroll_area.show()
        self._scroll_area.setFocus()

    def _on_mouse_release_event(self, ev):
        self.clicked.emit()

    def _on_key_pressed(self, ev):
        self.key_pressed.emit(ev)

    def setAnimated(self,
                    is_animated,
                    animation_time=None,
                    animation_steps=None):
        self._is_animated = is_animated
        if animation_time:
            self._animation_time = animation_time
        if animation_steps:
            self._animation_steps = animation_steps

    def is_moving(self):
        return self._is_moving

    def setCurrentIndex(self, new_index):
        new_index = max(new_index, 0)
        new_index = min(new_index, self._slides_count - 1)

        if new_index == self._current_index or self._is_moving:
            return

        is_animated = self._is_animated and \
                      abs(self._current_index - new_index) == 1
        self._move(new_index, is_animated)

    def _move(self, new_index, is_animated):
        self._is_moving = True
        source_x = self._current_index * self._slide_width
        target_x = new_index * self._slide_width

        if not is_animated:
            dx = target_x - source_x
            self._ribbon.scroll(-dx, 0)
            self._finish_moving(new_index)
        else:
            animation_interval = self._animation_time // self._animation_steps
            dx = (target_x - source_x) // self._animation_steps
            self._move_animated(source_x, target_x, dx, animation_interval,
                                new_index)

    def _move_animated(self, source_x, target_x, dx, animation_interval,
                       new_index):
        if target_x == source_x:
            self._finish_moving(new_index)
            return

        if target_x > source_x:
            dx = min(dx, target_x - source_x)
        else:
            dx = -min(-dx, source_x - target_x)
        self._ribbon.scroll(-dx, 0)
        source_x += dx
        QTimer.singleShot(
            animation_interval, lambda: self._move_animated(
                source_x, target_x, dx, animation_interval, new_index))

    def _finish_moving(self, new_index):
        self._current_index = new_index
        self.current_index_changed.emit(self._current_index)
        self._is_moving = False

    def widget(self):
        return self._scroll_area
class LayersSelectorWidget(QWidget):
    '''
    LayerChoiceWidget class provide widget plugin for picking layers.
    '''
    def __init__(self):
        super().__init__()
        filter = QLineEdit()
        filter.setPlaceholderText("filter")
        names = [
            'Core Layers', 'Convolution Layers', 'Pooling Layers',
            'Recurrent Layers', 'Preprocessing Layers', 'Attention Layers',
            'Reshaping Layers', 'Locally-Connected Layers'
        ]
        layers = [['Dense', 'Activation', 'Embedding', 'Masking', 'Lambda'],
                  [
                      'Conv1D', 'Conv2D', 'Conv3D', 'SeparableConv1D',
                      'SeparableConv2D', 'DepthwiseConv2D', 'Conv2DTranspose',
                      'Conv3Dtranspose'
                  ],
                  [
                      'MaxPooling1D', 'MaxPooling2D', 'MaxPooling3D',
                      'AveragePooling1D', 'AveragePooling2D',
                      'AveragePooling3D', 'GlobalMaxPooling1D',
                      'GlobalMaxPooling2D', 'GlobalMaxPooling3D',
                      'GlobalAveragePooling1D', 'GlobalAveragePooling2D',
                      'GlobalAveragePooling3D'
                  ],
                  [
                      'LSTM', 'GRU', 'SimpleRNN', 'TimeDistributed',
                      'BiDirectional', 'ConvLSTM2D'
                  ], ['TextToVector', 'Normalization'],
                  ['Attention', 'AdditiveAttention'],
                  [
                      'Reshape', 'Flatten', 'Cropping1D', 'Cropping2D',
                      'Cropping3D', 'UpSampling1D', 'UpSampling2D',
                      'UpSampling3D', 'ZeroPadding1D', 'ZeroPadding2D',
                      'ZeroPadding3D'
                  ], ['LocallyConnected1D', 'LocallyConnected2D']]
        self.main_layout = QVBoxLayout()
        self.main_layout.addWidget(filter)
        for i in range(len(names)):
            if i > 1:
                self.main_layout.addWidget(
                    LayersList(names[i], layers[i], filter, False))
            else:
                self.main_layout.addWidget(
                    LayersList(names[i], layers[i], filter))
        self.main_layout.setSpacing(0)
        self.main_layout.setAlignment(Qt.AlignTop)
        self.main_layout.setMargin(0)
        self.main_layout.setSpacing(0)
        self.main_layout.setContentsMargins(0, 0, 0, 0)
        self.scroll_area = QScrollArea()
        names = QWidget()
        names.setLayout(self.main_layout)
        self.scroll_area.setWidget(names)
        self.main_layout = QVBoxLayout()
        self.main_layout.setAlignment(Qt.AlignTop)
        self.main_layout.setMargin(0)
        self.main_layout.setSpacing(0)
        self.main_layout.setContentsMargins(0, 0, 0, 0)
        self.scroll_area.setAlignment(Qt.AlignTop)
        self.scroll_area.setWidgetResizable(True)
        self.main_layout.addWidget(self.scroll_area)
        self.setLayout(self.main_layout)
        del (names)
        del (layers)

        self.set_styling()

    def set_styling(self):
        self.setStyleSheet('''
                           background-color:aliceblue;
                           ''')
示例#7
0
class Window(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle('image-viewer')
        self.setGeometry(200, 200, 800, 600)
        self.image_path = 'F:\\browser-test\\test-dir\\4.jpg'
        self.cont_zoomed = None

        self.create_layout()
        self.show()

    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_BracketLeft:
            self.zoom('in')
            event.accept()
        elif event.key() == QtCore.Qt.Key_BracketRight:
            self.zoom('out')
            event.accept()
        elif event.key() == QtCore.Qt.Key_Q:
            self.close()
        return super().keyPressEvent(event)

    def zoom(self, dire):
        if self.cont_zoomed:
            ratio = self.cont_zoomed
        else:
            ratio = self.content
        if dire == 'in':
            self.cont_zoomed = self.content.scaled(ratio.width() * 0.75,
                                                   ratio.height() * 0.75,
                                                   QtCore.Qt.KeepAspectRatio)
        elif dire == 'out':
            self.cont_zoomed = self.content.scaled(ratio.width() * 1.25,
                                                   ratio.height() * 1.25,
                                                   QtCore.Qt.KeepAspectRatio)
        self.image.setPixmap(self.cont_zoomed)

    def create_layout(self):
        self.image = QLabel()
        self.content = QPixmap(self.image_path)
        # FIXME: reset on resize
        self.image.setPixmap(self.content)

        self.label = QLabel()
        self.label.setText('image name probably')

        self.image_scroll = QScrollArea()
        self.image_scroll.setWidget(self.image)
        self.image_scroll.setAlignment(QtCore.Qt.AlignCenter)

        vbox = QVBoxLayout()

        vbox.addWidget(self.image_scroll)
        vbox.addWidget(self.label)

        # remove padding & margin
        vbox.setSpacing(0)
        vbox.setContentsMargins(0, 0, 0, 0)
        self.setLayout(vbox)

    def set_image(self, img):
        '''Change displayed image with the input from the main window.
		
		img -- path to image file
		'''
        self.image_path = img
        self.content = QPixmap(self.image_path)
        self.image.setPixmap(self.content)

        self.label.setText(self.image_path)

    def resizeEvent(self, event):
        # increase image width in scroll area to fit into it
        self.image.setFixedWidth(
            self.width() - self.image_scroll.verticalScrollBar().width() - 2)
        return super().resizeEvent(event)
示例#8
0
class ResultModuleViewWidget(QMainWindow):
    # Конструктор
    def __init__(self,
                 module_name,
                 result,
                 module_success,
                 message,
                 parent=None):
        super(ResultModuleViewWidget, self).__init__(parent)
        self.module_name = module_name
        self.module_success = module_success
        self.result = result
        self.message = message
        self.setWindowTitle(f'Результаты выполнения модуля \'{module_name}\'')
        self.setWindowModality(Qt.ApplicationModal)
        self.setMinimumWidth(640)
        self.central_widget = QWidget()
        self.scroll = QScrollArea()
        self.layout = QVBoxLayout(self.central_widget)
        self.init_ui(result, module_success)

    # Метод инициализации UI
    def init_ui(self, result, module_success):
        self.layout.addWidget(self.scroll)
        self.layout.setAlignment(Qt.AlignTop)
        self.setCentralWidget(self.central_widget)
        widget = QWidget()
        main_layout = QVBoxLayout(widget)
        for i in range(0, len(result)):
            block_widget = QWidget()
            layout = QVBoxLayout(block_widget)
            block_name_label = QLabel(result[i][-1])
            block_name_label.setAlignment(Qt.AlignCenter)
            block_name_label.setStyleSheet('font-weight: bold;')
            layout.addWidget(block_name_label)
            for j in range(0, len(result[i]) - 1):
                command_result_label = QLabel(
                    f'Команда №{j + 1}: \'{result[i][j][1].strip()}\'.')
                command_result_label.setAlignment(Qt.AlignCenter)
                if result[i][j][0] == 0:
                    command_result_label.setStyleSheet(
                        'background-color: ForestGreen;')
                else:
                    command_result_label.setStyleSheet(
                        'background-color: OrangeRed;')
                layout.addWidget(command_result_label)
            main_layout.addWidget(block_widget)
        module_result_label = QLabel()
        if module_success == 'True':
            module_result_label.setText('Модуль выполнился успешно.')
            module_result_label.setStyleSheet(
                'color: ForestGreen; font-weight: bold;')
        elif module_success == 'False':
            module_result_label.setText('Модуль завершился неудачно.')
            module_result_label.setStyleSheet('color: Red; font-weight: bold;')
        elif module_success == 'Error':
            module_result_label.setText('В проверочном выражении ошибка.')
            module_result_label.setStyleSheet(
                'color: OrangeRed; font-weight: bold;')
        module_result_label.setAlignment(Qt.AlignCenter)
        main_layout.addWidget(module_result_label)
        button = QPushButton('Сгенерировать отчёт')
        button.clicked.connect(self.start_report_generation)
        main_layout.addWidget(button)
        self.scroll.setAlignment(Qt.AlignCenter)
        self.scroll.setWidget(widget)

    # Слот, обрабатывающий запрос пользователя на генерацию отчёта (нажатие соответствующей кнопки)
    @Slot()
    def start_report_generation(self):
        generate_report(self.module_name, self.module_success, self.result,
                        self.message, self)
示例#9
0
class MainWindow(QMainWindow):
    def __init__(self,
                 font=QtGui.QFontMetrics(QtGui.QFont()),
                 screen_size=QtCore.QSize()):
        super().__init__()

        self.screen = screen_size
        self.font_metric = font
        self.setWindowTitle('OpenMC Plot Explorer')

    def loadGui(self):

        self.pixmap = None
        self.zoom = 100

        self.loadModel()

        # Create viewing area
        self.frame = QScrollArea(self)
        cw = QWidget()
        self.frame.setCornerWidget(cw)
        self.frame.setAlignment(QtCore.Qt.AlignCenter)
        self.frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.setCentralWidget(self.frame)

        # connect pinch gesture (OSX)
        self.grabGesture(QtCore.Qt.PinchGesture)

        # Create plot image
        self.plotIm = PlotImage(self.model, self.frame, self)
        self.frame.setWidget(self.plotIm)

        # Dock
        self.dock = DomainDock(self.model, self.font_metric, self)
        self.dock.setObjectName("Domain Options Dock")
        self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.dock)

        # Tally Dock
        self.tallyDock = TallyDock(self.model, self.font_metric, self)
        self.tallyDock.update()
        self.tallyDock.setObjectName("Tally Options Dock")
        self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.tallyDock)

        # Color DialogtallyDock
        self.colorDialog = ColorDialog(self.model, self.font_metric, self)
        self.colorDialog.hide()

        # Tools
        self.exportDataDialog = ExportDataDialog(self.model, self.font_metric,
                                                 self)

        # Restore Window Settings
        self.restoreWindowSettings()

        # Create menubar
        self.createMenuBar()
        self.updateEditMenu()

        # Status Bar
        self.coord_label = QLabel()
        self.statusBar().addPermanentWidget(self.coord_label)
        self.coord_label.hide()

        # Keyboard overlay
        self.shortcutOverlay = ShortcutsOverlay(self)
        self.shortcutOverlay.hide()

        # Load Plot
        self.statusBar().showMessage('Generating Plot...')
        self.dock.updateDock()
        self.tallyDock.update()
        self.colorDialog.updateDialogValues()
        self.statusBar().showMessage('')

        # Timer allows GUI to render before plot finishes loading
        QtCore.QTimer.singleShot(0, self.plotIm.generatePixmap)
        QtCore.QTimer.singleShot(0, self.showCurrentView)

    def event(self, event):
        # use pinch event to update zoom
        if isinstance(event, QGestureEvent):
            pinch = event.gesture(QtCore.Qt.PinchGesture)
            self.editZoom(self.zoom * pinch.scaleFactor())
        if isinstance(event, QKeyEvent) and hasattr(self, "shortcutOverlay"):
            self.shortcutOverlay.event(event)
        return super().event(event)

    def show(self):
        super().show()
        self.plotIm._resize()

    def toggleShortcuts(self):
        if self.shortcutOverlay.isVisible():
            self.shortcutOverlay.close()
        else:
            self.shortcutOverlay.move(0, 0)
            self.shortcutOverlay.resize(self.width(), self.height())
            self.shortcutOverlay.show()

    # Create and update menus:
    def createMenuBar(self):
        self.mainMenu = self.menuBar()

        # File Menu
        self.reloadModelAction = QAction("&Reload model...", self)
        self.reloadModelAction.setShortcut("Ctrl+Shift+R")
        self.reloadModelAction.setToolTip("Reload current model")
        self.reloadModelAction.setStatusTip("Reload current model")
        reload_connector = partial(self.loadModel, reload=True)
        self.reloadModelAction.triggered.connect(reload_connector)

        self.saveImageAction = QAction("&Save Image As...", self)
        self.saveImageAction.setShortcut("Ctrl+Shift+S")
        self.saveImageAction.setToolTip('Save plot image')
        self.saveImageAction.setStatusTip('Save plot image')
        self.saveImageAction.triggered.connect(self.saveImage)

        self.saveViewAction = QAction("Save &View...", self)
        self.saveViewAction.setShortcut(QtGui.QKeySequence.Save)
        self.saveViewAction.setStatusTip('Save current view settings')
        self.saveViewAction.triggered.connect(self.saveView)

        self.openAction = QAction("&Open View...", self)
        self.openAction.setShortcut(QtGui.QKeySequence.Open)
        self.openAction.setToolTip('Open saved view settings')
        self.openAction.setStatusTip('Open saved view settings')
        self.openAction.triggered.connect(self.openView)

        self.quitAction = QAction("&Quit", self)
        self.quitAction.setShortcut(QtGui.QKeySequence.Quit)
        self.quitAction.setToolTip('Quit OpenMC Plot Explorer')
        self.quitAction.setStatusTip('Quit OpenMC Plot Explorer')
        self.quitAction.triggered.connect(self.close)

        self.exportDataAction = QAction('E&xport...', self)
        self.exportDataAction.setToolTip('Export model and tally data VTK')
        self.setStatusTip('Export current model and tally data to VTK')
        self.exportDataAction.triggered.connect(self.exportTallyData)
        if not _HAVE_VTK:
            self.exportDataAction.setEnabled(False)
            self.exportDataAction.setToolTip(
                "Disabled: VTK Python module is not installed")

        self.fileMenu = self.mainMenu.addMenu('&File')
        self.fileMenu.addAction(self.reloadModelAction)
        self.fileMenu.addAction(self.saveImageAction)
        self.fileMenu.addAction(self.exportDataAction)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.saveViewAction)
        self.fileMenu.addAction(self.openAction)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.quitAction)

        # Data Menu
        self.openStatePointAction = QAction("&Open statepoint...", self)
        self.openStatePointAction.setToolTip('Open statepoint file')
        self.openStatePointAction.triggered.connect(self.openStatePoint)

        self.dataMenu = self.mainMenu.addMenu('D&ata')
        self.dataMenu.addAction(self.openStatePointAction)
        self.updateDataMenu()

        # Edit Menu
        self.applyAction = QAction("&Apply Changes", self)
        self.applyAction.setShortcut("Ctrl+Return")
        self.applyAction.setToolTip('Generate new view with changes applied')
        self.applyAction.setStatusTip('Generate new view with changes applied')
        self.applyAction.triggered.connect(self.applyChanges)

        self.undoAction = QAction('&Undo', self)
        self.undoAction.setShortcut(QtGui.QKeySequence.Undo)
        self.undoAction.setToolTip('Undo')
        self.undoAction.setStatusTip('Undo last plot view change')
        self.undoAction.setDisabled(True)
        self.undoAction.triggered.connect(self.undo)

        self.redoAction = QAction('&Redo', self)
        self.redoAction.setDisabled(True)
        self.redoAction.setToolTip('Redo')
        self.redoAction.setStatusTip('Redo last plot view change')
        self.redoAction.setShortcut(QtGui.QKeySequence.Redo)
        self.redoAction.triggered.connect(self.redo)

        self.restoreAction = QAction("&Restore Default Plot", self)
        self.restoreAction.setShortcut("Ctrl+R")
        self.restoreAction.setToolTip('Restore to default plot view')
        self.restoreAction.setStatusTip('Restore to default plot view')
        self.restoreAction.triggered.connect(self.restoreDefault)

        self.editMenu = self.mainMenu.addMenu('&Edit')
        self.editMenu.addAction(self.applyAction)
        self.editMenu.addSeparator()
        self.editMenu.addAction(self.undoAction)
        self.editMenu.addAction(self.redoAction)
        self.editMenu.addSeparator()
        self.editMenu.addAction(self.restoreAction)
        self.editMenu.addSeparator()
        self.editMenu.aboutToShow.connect(self.updateEditMenu)

        # Edit -> Basis Menu
        self.xyAction = QAction('&xy  ', self)
        self.xyAction.setCheckable(True)
        self.xyAction.setShortcut('Alt+X')
        self.xyAction.setToolTip('Change to xy basis')
        self.xyAction.setStatusTip('Change to xy basis')
        xy_connector = partial(self.editBasis, 'xy', apply=True)
        self.xyAction.triggered.connect(xy_connector)

        self.xzAction = QAction('x&z  ', self)
        self.xzAction.setCheckable(True)
        self.xzAction.setShortcut('Alt+Z')
        self.xzAction.setToolTip('Change to xz basis')
        self.xzAction.setStatusTip('Change to xz basis')
        xz_connector = partial(self.editBasis, 'xz', apply=True)
        self.xzAction.triggered.connect(xz_connector)

        self.yzAction = QAction('&yz  ', self)
        self.yzAction.setCheckable(True)
        self.yzAction.setShortcut('Alt+Y')
        self.yzAction.setToolTip('Change to yz basis')
        self.yzAction.setStatusTip('Change to yz basis')
        yz_connector = partial(self.editBasis, 'yz', apply=True)
        self.yzAction.triggered.connect(yz_connector)

        self.basisMenu = self.editMenu.addMenu('&Basis')
        self.basisMenu.addAction(self.xyAction)
        self.basisMenu.addAction(self.xzAction)
        self.basisMenu.addAction(self.yzAction)
        self.basisMenu.aboutToShow.connect(self.updateBasisMenu)

        # Edit -> Color By Menu
        self.cellAction = QAction('&Cell', self)
        self.cellAction.setCheckable(True)
        self.cellAction.setShortcut('Alt+C')
        self.cellAction.setToolTip('Color by cell')
        self.cellAction.setStatusTip('Color plot by cell')
        cell_connector = partial(self.editColorBy, 'cell', apply=True)
        self.cellAction.triggered.connect(cell_connector)

        self.materialAction = QAction('&Material', self)
        self.materialAction.setCheckable(True)
        self.materialAction.setShortcut('Alt+M')
        self.materialAction.setToolTip('Color by material')
        self.materialAction.setStatusTip('Color plot by material')
        material_connector = partial(self.editColorBy, 'material', apply=True)
        self.materialAction.triggered.connect(material_connector)

        self.temperatureAction = QAction('&Temperature', self)
        self.temperatureAction.setCheckable(True)
        self.temperatureAction.setShortcut('Alt+T')
        self.temperatureAction.setToolTip('Color by temperature')
        self.temperatureAction.setStatusTip('Color plot by temperature')
        temp_connector = partial(self.editColorBy, 'temperature', apply=True)
        self.temperatureAction.triggered.connect(temp_connector)

        self.densityAction = QAction('&Density', self)
        self.densityAction.setCheckable(True)
        self.densityAction.setShortcut('Alt+D')
        self.densityAction.setToolTip('Color by density')
        self.densityAction.setStatusTip('Color plot by density')
        density_connector = partial(self.editColorBy, 'density', apply=True)
        self.densityAction.triggered.connect(density_connector)

        self.colorbyMenu = self.editMenu.addMenu('&Color By')
        self.colorbyMenu.addAction(self.cellAction)
        self.colorbyMenu.addAction(self.materialAction)
        self.colorbyMenu.addAction(self.temperatureAction)
        self.colorbyMenu.addAction(self.densityAction)

        self.colorbyMenu.aboutToShow.connect(self.updateColorbyMenu)

        self.editMenu.addSeparator()

        # Edit -> Other Options
        self.maskingAction = QAction('Enable &Masking', self)
        self.maskingAction.setShortcut('Ctrl+M')
        self.maskingAction.setCheckable(True)
        self.maskingAction.setToolTip('Toggle masking')
        self.maskingAction.setStatusTip('Toggle whether masking is enabled')
        masking_connector = partial(self.toggleMasking, apply=True)
        self.maskingAction.toggled.connect(masking_connector)
        self.editMenu.addAction(self.maskingAction)

        self.highlightingAct = QAction('Enable High&lighting', self)
        self.highlightingAct.setShortcut('Ctrl+L')
        self.highlightingAct.setCheckable(True)
        self.highlightingAct.setToolTip('Toggle highlighting')
        self.highlightingAct.setStatusTip('Toggle whether '
                                          'highlighting is enabled')
        highlight_connector = partial(self.toggleHighlighting, apply=True)
        self.highlightingAct.toggled.connect(highlight_connector)
        self.editMenu.addAction(self.highlightingAct)

        self.overlapAct = QAction('Enable Overlap Coloring', self)
        self.overlapAct.setShortcut('Ctrl+P')
        self.overlapAct.setCheckable(True)
        self.overlapAct.setToolTip('Toggle overlapping regions')
        self.overlapAct.setStatusTip('Toggle display of overlapping '
                                     'regions when enabled')
        overlap_connector = partial(self.toggleOverlaps, apply=True)
        self.overlapAct.toggled.connect(overlap_connector)
        self.editMenu.addAction(self.overlapAct)

        self.outlineAct = QAction('Enable Domain Outlines', self)
        self.outlineAct.setShortcut('Ctrl+U')
        self.outlineAct.setCheckable(True)
        self.outlineAct.setToolTip('Display Cell/Material Boundaries')
        self.outlineAct.setStatusTip('Toggle display of domain '
                                     'outlines when enabled')
        outline_connector = partial(self.toggleOutlines, apply=True)
        self.outlineAct.toggled.connect(outline_connector)
        self.editMenu.addAction(self.outlineAct)

        # View Menu
        self.dockAction = QAction('Hide &Dock', self)
        self.dockAction.setShortcut("Ctrl+D")
        self.dockAction.setToolTip('Toggle dock visibility')
        self.dockAction.setStatusTip('Toggle dock visibility')
        self.dockAction.triggered.connect(self.toggleDockView)

        self.tallyDockAction = QAction('Tally &Dock', self)
        self.tallyDockAction.setShortcut("Ctrl+T")
        self.tallyDockAction.setToolTip('Toggle tally dock visibility')
        self.tallyDockAction.setStatusTip('Toggle tally dock visibility')
        self.tallyDockAction.triggered.connect(self.toggleTallyDockView)

        self.zoomAction = QAction('&Zoom...', self)
        self.zoomAction.setShortcut('Alt+Shift+Z')
        self.zoomAction.setToolTip('Edit zoom factor')
        self.zoomAction.setStatusTip('Edit zoom factor')
        self.zoomAction.triggered.connect(self.editZoomAct)

        self.viewMenu = self.mainMenu.addMenu('&View')
        self.viewMenu.addAction(self.dockAction)
        self.viewMenu.addAction(self.tallyDockAction)
        self.viewMenu.addSeparator()
        self.viewMenu.addAction(self.zoomAction)
        self.viewMenu.aboutToShow.connect(self.updateViewMenu)

        # Window Menu
        self.mainWindowAction = QAction('&Main Window', self)
        self.mainWindowAction.setCheckable(True)
        self.mainWindowAction.setToolTip('Bring main window to front')
        self.mainWindowAction.setStatusTip('Bring main window to front')
        self.mainWindowAction.triggered.connect(self.showMainWindow)

        self.colorDialogAction = QAction('Color &Options', self)
        self.colorDialogAction.setCheckable(True)
        self.colorDialogAction.setToolTip('Bring Color Dialog to front')
        self.colorDialogAction.setStatusTip('Bring Color Dialog to front')
        self.colorDialogAction.triggered.connect(self.showColorDialog)

        # Keyboard Shortcuts Overlay
        self.keyboardShortcutsAction = QAction("&Keyboard Shortcuts...", self)
        self.keyboardShortcutsAction.setShortcut("?")
        self.keyboardShortcutsAction.setToolTip("Display Keyboard Shortcuts")
        self.keyboardShortcutsAction.setStatusTip("Display Keyboard Shortcuts")
        self.keyboardShortcutsAction.triggered.connect(self.toggleShortcuts)

        self.windowMenu = self.mainMenu.addMenu('&Window')
        self.windowMenu.addAction(self.mainWindowAction)
        self.windowMenu.addAction(self.colorDialogAction)
        self.windowMenu.addAction(self.keyboardShortcutsAction)
        self.windowMenu.aboutToShow.connect(self.updateWindowMenu)

    def updateEditMenu(self):
        changed = self.model.currentView != self.model.defaultView
        self.restoreAction.setDisabled(not changed)

        self.maskingAction.setChecked(self.model.currentView.masking)
        self.highlightingAct.setChecked(self.model.currentView.highlighting)
        self.outlineAct.setChecked(self.model.currentView.outlines)

        num_previous_views = len(self.model.previousViews)
        self.undoAction.setText('&Undo ({})'.format(num_previous_views))
        num_subsequent_views = len(self.model.subsequentViews)
        self.redoAction.setText('&Redo ({})'.format(num_subsequent_views))

    def updateBasisMenu(self):
        self.xyAction.setChecked(self.model.currentView.basis == 'xy')
        self.xzAction.setChecked(self.model.currentView.basis == 'xz')
        self.yzAction.setChecked(self.model.currentView.basis == 'yz')

    def updateColorbyMenu(self):
        cv = self.model.currentView
        self.cellAction.setChecked(cv.colorby == 'cell')
        self.materialAction.setChecked(cv.colorby == 'material')
        self.temperatureAction.setChecked(cv.colorby == 'temperature')
        self.densityAction.setChecked(cv.colorby == 'density')

    def updateViewMenu(self):
        if self.dock.isVisible():
            self.dockAction.setText('Hide &Dock')
        else:
            self.dockAction.setText('Show &Dock')

    def updateWindowMenu(self):
        self.colorDialogAction.setChecked(self.colorDialog.isActiveWindow())
        self.mainWindowAction.setChecked(self.isActiveWindow())

    # Menu and shared methods
    def loadModel(self, reload=False):
        if reload:
            self.resetModels()
        else:
            # create new plot model
            self.model = PlotModel()
            self.restoreModelSettings()
            # update plot and model settings
            self.updateRelativeBases()

        self.cellsModel = DomainTableModel(self.model.activeView.cells)
        self.materialsModel = DomainTableModel(self.model.activeView.materials)

        if reload:
            loader_thread = Thread(target=_openmcReload)
            loader_thread.start()
            while loader_thread.is_alive():
                self.statusBar().showMessage("Reloading model...")
                QApplication.processEvents()

            self.plotIm.model = self.model
            self.applyChanges()

    def saveImage(self):
        filename, ext = QFileDialog.getSaveFileName(self, "Save Plot Image",
                                                    "untitled",
                                                    "Images (*.png)")
        if filename:
            if "." not in filename:
                filename += ".png"
            self.plotIm.figure.savefig(filename, transparent=True)
            self.statusBar().showMessage('Plot Image Saved', 5000)

    def saveView(self):
        filename, ext = QFileDialog.getSaveFileName(self, "Save View Settings",
                                                    "untitled",
                                                    "View Settings (*.pltvw)")
        if filename:
            if "." not in filename:
                filename += ".pltvw"

            saved = {
                'version': self.model.version,
                'current': self.model.currentView
            }
            with open(filename, 'wb') as file:
                pickle.dump(saved, file)

    def openView(self):
        filename, ext = QFileDialog.getOpenFileName(self, "Open View Settings",
                                                    ".", "*.pltvw")
        if filename:
            try:
                with open(filename, 'rb') as file:
                    saved = pickle.load(file)
            except Exception:
                message = 'Error loading plot settings'
                saved = {'version': None, 'current': None}
            if saved['version'] == self.model.version:
                self.model.activeView = saved['current']
                self.dock.updateDock()
                self.colorDialog.updateDialogValues()
                self.applyChanges()
                message = '{} settings loaded'.format(filename)
            else:
                message = 'Error loading plot settings. Incompatible model.'
            self.statusBar().showMessage(message, 5000)

    def openStatePoint(self):
        # check for an alread-open statepoint
        if self.model.statepoint:
            msg_box = QMessageBox()
            msg_box.setText("Please close the current statepoint file before "
                            "opening a new one.")
            msg_box.setIcon(QMessageBox.Information)
            msg_box.setStandardButtons(QMessageBox.Ok)
            msg_box.exec_()
            return
        filename, ext = QFileDialog.getOpenFileName(self, "Open StatePoint",
                                                    ".", "statepoint*.h5")
        if filename:
            try:
                self.model.openStatePoint(filename)
                message = 'Opened statepoint file: {}'
            except (FileNotFoundError, OSError):
                message = 'Error opening statepoint file: {}'
                msg_box = QMessageBox()
                msg = "Could open statepoint file: \n\n {} \n"
                msg_box.setText(msg.format(filename))
                msg_box.setIcon(QMessageBox.Warning)
                msg_box.setStandardButtons(QMessageBox.Ok)
                msg_box.exec_()
            finally:
                self.statusBar().showMessage(message.format(filename), 5000)
            self.updateDataMenu()
            self.tallyDock.update()

    def closeStatePoint(self):
        # remove the statepoint object and update the data menu
        filename = self.model.statepoint.filename
        self.model.statepoint = None
        self.model.currentView.selectedTally = None
        self.model.activeView.selectedTally = None

        msg = "Closed statepoint file {}".format(filename)
        self.statusBar().showMessage(msg)
        self.updateDataMenu()
        self.tallyDock.selectTally()
        self.tallyDock.update()
        self.plotIm.updatePixmap()

    def updateDataMenu(self):
        if self.model.statepoint:
            self.closeStatePointAction = QAction("&Close statepoint", self)
            self.closeStatePointAction.setToolTip("Close current statepoint")
            self.closeStatePointAction.triggered.connect(self.closeStatePoint)
            self.dataMenu.addAction(self.closeStatePointAction)
        elif hasattr(self, "closeStatePointAction"):
            self.dataMenu.removeAction(self.closeStatePointAction)

    def applyChanges(self):
        if self.model.activeView != self.model.currentView:
            self.statusBar().showMessage('Generating Plot...')
            QApplication.processEvents()
            self.model.storeCurrent()
            self.model.subsequentViews = []
            self.plotIm.generatePixmap()
            self.resetModels()
            self.showCurrentView()
            self.statusBar().showMessage('')
        else:
            self.statusBar().showMessage('No changes to apply.', 3000)

    def undo(self):
        self.statusBar().showMessage('Generating Plot...')
        QApplication.processEvents()

        self.model.undo()
        self.resetModels()
        self.showCurrentView()
        self.dock.updateDock()
        self.colorDialog.updateDialogValues()

        if not self.model.previousViews:
            self.undoAction.setDisabled(True)
        self.redoAction.setDisabled(False)
        self.statusBar().showMessage('')

    def redo(self):
        self.statusBar().showMessage('Generating Plot...')
        QApplication.processEvents()

        self.model.redo()
        self.resetModels()
        self.showCurrentView()
        self.dock.updateDock()
        self.colorDialog.updateDialogValues()

        if not self.model.subsequentViews:
            self.redoAction.setDisabled(True)
        self.undoAction.setDisabled(False)
        self.statusBar().showMessage('')

    def restoreDefault(self):
        if self.model.currentView != self.model.defaultView:

            self.statusBar().showMessage('Generating Plot...')
            QApplication.processEvents()

            self.model.storeCurrent()
            self.model.activeView.adopt_plotbase(self.model.defaultView)
            self.plotIm.generatePixmap()
            self.resetModels()
            self.showCurrentView()
            self.dock.updateDock()
            self.colorDialog.updateDialogValues()

            self.model.subsequentViews = []
            self.statusBar().showMessage('')

    def editBasis(self, basis, apply=False):
        self.model.activeView.basis = basis
        self.dock.updateBasis()
        if apply:
            self.applyChanges()

    def editColorBy(self, domain_kind, apply=False):
        self.model.activeView.colorby = domain_kind
        self.dock.updateColorBy()
        self.colorDialog.updateColorBy()
        if apply:
            self.applyChanges()

    def toggleOverlaps(self, state, apply=False):
        self.model.activeView.color_overlaps = bool(state)
        self.colorDialog.updateOverlap()
        if apply:
            self.applyChanges()

    def editColorMap(self, colormap_name, property_type, apply=False):
        self.model.activeView.colormaps[property_type] = colormap_name
        self.plotIm.updateColorMap(colormap_name, property_type)
        self.colorDialog.updateColorMaps()
        if apply:
            self.applyChanges()

    def editColorbarMin(self, min_val, property_type, apply=False):
        av = self.model.activeView
        current = av.user_minmax[property_type]
        av.user_minmax[property_type] = (min_val, current[1])
        self.colorDialog.updateColorMinMax()
        self.plotIm.updateColorMinMax(property_type)
        if apply:
            self.applyChanges()

    def editColorbarMax(self, max_val, property_type, apply=False):
        av = self.model.activeView
        current = av.user_minmax[property_type]
        av.user_minmax[property_type] = (current[0], max_val)
        self.colorDialog.updateColorMinMax()
        self.plotIm.updateColorMinMax(property_type)
        if apply:
            self.applyChanges()

    def toggleColorbarScale(self, state, property, apply=False):
        av = self.model.activeView
        av.color_scale_log[property] = bool(state)
        # temporary, should be resolved diferently in the future
        cv = self.model.currentView
        cv.color_scale_log[property] = bool(state)
        self.plotIm.updateColorbarScale()
        if apply:
            self.applyChanges()

    def toggleUserMinMax(self, state, property):
        av = self.model.activeView
        av.use_custom_minmax[property] = bool(state)
        if av.user_minmax[property] == (0.0, 0.0):
            av.user_minmax[property] = copy.copy(av.data_minmax[property])
        self.plotIm.updateColorMinMax('temperature')
        self.plotIm.updateColorMinMax('density')
        self.colorDialog.updateColorMinMax()

    def toggleDataIndicatorCheckBox(self, state, property, apply=False):
        av = self.model.activeView
        av.data_indicator_enabled[property] = bool(state)

        cv = self.model.currentView
        cv.data_indicator_enabled[property] = bool(state)

        self.plotIm.updateDataIndicatorVisibility()
        if apply:
            self.applyChanges()

    def toggleMasking(self, state, apply=False):
        self.model.activeView.masking = bool(state)
        self.colorDialog.updateMasking()
        if apply:
            self.applyChanges()

    def toggleHighlighting(self, state, apply=False):
        self.model.activeView.highlighting = bool(state)
        self.colorDialog.updateHighlighting()
        if apply:
            self.applyChanges()

    def toggleDockView(self):
        if self.dock.isVisible():
            self.dock.hide()
            if not self.isMaximized() and not self.dock.isFloating():
                self.resize(self.width() - self.dock.width(), self.height())
        else:
            self.dock.setVisible(True)
            if not self.isMaximized() and not self.dock.isFloating():
                self.resize(self.width() + self.dock.width(), self.height())
        self.resizePixmap()
        self.showMainWindow()

    def toggleTallyDockView(self):
        if self.tallyDock.isVisible():
            self.tallyDock.hide()
            if not self.isMaximized() and not self.tallyDock.isFloating():
                self.resize(self.width() - self.tallyDock.width(),
                            self.height())
        else:
            self.tallyDock.setVisible(True)
            if not self.isMaximized() and not self.tallyDock.isFloating():
                self.resize(self.width() + self.tallyDock.width(),
                            self.height())
        self.resizePixmap()
        self.showMainWindow()

    def editZoomAct(self):
        percent, ok = QInputDialog.getInt(self, "Edit Zoom", "Zoom Percent:",
                                          self.dock.zoomBox.value(), 25, 2000)
        if ok:
            self.dock.zoomBox.setValue(percent)

    def editZoom(self, value):
        self.zoom = value
        self.resizePixmap()
        self.dock.zoomBox.setValue(value)

    def showMainWindow(self):
        self.raise_()
        self.activateWindow()

    def showColorDialog(self):
        self.colorDialog.show()
        self.colorDialog.raise_()
        self.colorDialog.activateWindow()

    def showExportDialog(self):
        self.exportDataDialog.show()
        self.exportDataDialog.raise_()
        self.exportDataDialog.activateWindow()

    # Dock methods:

    def editSingleOrigin(self, value, dimension):
        self.model.activeView.origin[dimension] = value

    def editPlotAlpha(self, value):
        self.model.activeView.domainAlpha = value

    def editPlotVisibility(self, value):
        self.model.activeView.domainVisible = bool(value)

    def toggleOutlines(self, value, apply=False):
        self.model.activeView.outlines = bool(value)
        self.dock.updateOutlines()

        if apply:
            self.applyChanges()

    def editWidth(self, value):
        self.model.activeView.width = value
        self.onRatioChange()
        self.dock.updateWidth()

    def editHeight(self, value):
        self.model.activeView.height = value
        self.onRatioChange()
        self.dock.updateHeight()

    def toggleAspectLock(self, state):
        self.model.activeView.aspectLock = bool(state)
        self.onRatioChange()
        self.dock.updateAspectLock()

    def editVRes(self, value):
        self.model.activeView.v_res = value
        self.dock.updateVRes()

    def editHRes(self, value):
        self.model.activeView.h_res = value
        self.onRatioChange()
        self.dock.updateHRes()

    # Color dialog methods:

    def editMaskingColor(self):
        current_color = self.model.activeView.maskBackground
        dlg = QColorDialog(self)

        dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color))
        if dlg.exec_():
            new_color = dlg.currentColor().getRgb()[:3]
            self.model.activeView.maskBackground = new_color
            self.colorDialog.updateMaskingColor()

    def editHighlightColor(self):
        current_color = self.model.activeView.highlightBackground
        dlg = QColorDialog(self)

        dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color))
        if dlg.exec_():
            new_color = dlg.currentColor().getRgb()[:3]
            self.model.activeView.highlightBackground = new_color
            self.colorDialog.updateHighlightColor()

    def editAlpha(self, value):
        self.model.activeView.highlightAlpha = value

    def editSeed(self, value):
        self.model.activeView.highlightSeed = value

    def editOverlapColor(self, apply=False):
        current_color = self.model.activeView.overlap_color
        dlg = QColorDialog(self)
        dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color))
        if dlg.exec_():
            new_color = dlg.currentColor().getRgb()[:3]
            self.model.activeView.overlap_color = new_color
            self.colorDialog.updateOverlapColor()

        if apply:
            self.applyChanges()

    def editBackgroundColor(self, apply=False):
        current_color = self.model.activeView.domainBackground
        dlg = QColorDialog(self)

        dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color))
        if dlg.exec_():
            new_color = dlg.currentColor().getRgb()[:3]
            self.model.activeView.domainBackground = new_color
            self.colorDialog.updateBackgroundColor()

        if apply:
            self.applyChanges()

    def resetColors(self):
        self.model.resetColors()
        self.colorDialog.updateDialogValues()
        self.applyChanges()

    # Tally dock methods

    def editSelectedTally(self, event):
        av = self.model.activeView

        if event is None or event == "None" or event == "":
            av.selectedTally = None
        else:
            av.selectedTally = int(event.split()[1])
        self.tallyDock.selectTally(event)

    def editTallyValue(self, event):
        av = self.model.activeView
        av.tallyValue = event

    def updateScores(self, state):
        self.tallyDock.updateScores()

    def updateNuclides(self, state):
        self.tallyDock.updateNuclides()

    def toggleTallyVisibility(self, state, apply=False):
        av = self.model.activeView
        av.tallyDataVisible = bool(state)
        if apply:
            self.applyChanges()

    def toggleTallyLogScale(self, state, apply=False):
        av = self.model.activeView
        av.tallyDataLogScale = bool(state)
        if apply:
            self.applyChanges()

    def toggleTallyMaskZero(self, state):
        av = self.model.activeView
        av.tallyMaskZeroValues = bool(state)

    def editTallyAlpha(self, value, apply=False):
        av = self.model.activeView
        av.tallyDataAlpha = value
        if apply:
            self.applyChanges()

    def toggleTallyContours(self, state):
        av = self.model.activeView
        av.tallyContours = bool(state)

    def editTallyContourLevels(self, value):
        av = self.model.activeView
        av.tallyContourLevels = value

    def toggleTallyDataIndicator(self, state, apply=False):
        av = self.model.activeView
        av.tallyDataIndicator = bool(state)
        if apply:
            self.applyChanges()

    def toggleTallyDataClip(self, state):
        av = self.model.activeView
        av.clipTallyData = bool(state)

    def toggleTallyDataUserMinMax(self, state, apply=False):
        av = self.model.activeView
        av.tallyDataUserMinMax = bool(state)
        self.tallyDock.tallyColorForm.setMinMaxEnabled(bool(state))
        if apply:
            self.applyChanges()

    def editTallyDataMin(self, value, apply=False):
        av = self.model.activeView
        av.tallyDataMin = value
        if apply:
            self.applyChanges()

    def editTallyDataMax(self, value, apply=False):
        av = self.model.activeView
        av.tallyDataMax = value
        if apply:
            self.applyChanges()

    def editTallyDataColormap(self, cmap, apply=False):
        av = self.model.activeView
        av.tallyDataColormap = cmap
        if apply:
            self.applyChanges()

    def updateTallyMinMax(self):
        self.tallyDock.updateMinMax()

    # Plot image methods
    def editPlotOrigin(self, xOr, yOr, zOr=None, apply=False):
        if zOr is not None:
            self.model.activeView.origin = [xOr, yOr, zOr]
        else:
            origin = [None, None, None]
            origin[self.xBasis] = xOr
            origin[self.yBasis] = yOr
            origin[self.zBasis] = self.model.activeView.origin[self.zBasis]
            self.model.activeView.origin = origin

        self.dock.updateOrigin()

        if apply:
            self.applyChanges()

    def revertDockControls(self):
        self.dock.revertToCurrent()

    def editDomainColor(self, kind, id):
        if kind == 'Cell':
            domain = self.model.activeView.cells
        else:
            domain = self.model.activeView.materials

        current_color = domain[id].color
        dlg = QColorDialog(self)

        if isinstance(current_color, tuple):
            dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color))
        elif isinstance(current_color, str):
            current_color = openmc.plots._SVG_COLORS[current_color]
            dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color))
        if dlg.exec_():
            new_color = dlg.currentColor().getRgb()[:3]
            domain[id].color = new_color

        self.applyChanges()

    def toggleDomainMask(self, state, kind, id):
        if kind == 'Cell':
            domain = self.model.activeView.cells
        else:
            domain = self.model.activeView.materials

        domain[id].masked = bool(state)
        self.applyChanges()

    def toggleDomainHighlight(self, state, kind, id):
        if kind == 'Cell':
            domain = self.model.activeView.cells
        else:
            domain = self.model.activeView.materials

        domain[id].highlight = bool(state)
        self.applyChanges()

    # Helper methods:

    def restoreWindowSettings(self):
        settings = QtCore.QSettings()

        self.resize(settings.value("mainWindow/Size", QtCore.QSize(800, 600)))
        self.move(
            settings.value("mainWindow/Position", QtCore.QPoint(100, 100)))
        self.restoreState(settings.value("mainWindow/State"))

        self.colorDialog.resize(
            settings.value("colorDialog/Size", QtCore.QSize(400, 500)))
        self.colorDialog.move(
            settings.value("colorDialog/Position", QtCore.QPoint(600, 200)))
        is_visible = settings.value("colorDialog/Visible", 0)
        # some versions of PySide will return None rather than the default value
        if is_visible is None:
            is_visible = False
        else:
            is_visible = bool(int(is_visible))

        self.colorDialog.setVisible(is_visible)

    def restoreModelSettings(self):
        if os.path.isfile("plot_settings.pkl"):

            with open('plot_settings.pkl', 'rb') as file:
                model = pickle.load(file)

            # do not replace model if the version is out of date
            if model.version != self.model.version:
                print("WARNING: previous plot settings are for a different "
                      "version of the GUI. They will be ignored.")
                wrn_msg = "Existing version: {}, Current GUI version: {}"
                print(wrn_msg.format(model.version, self.model.version))
                return

            try:
                self.model.statepoint = model.statepoint
            except OSError:
                msg_box = QMessageBox()
                msg = "Could open statepoint file: \n\n {} \n"
                msg_box.setText(msg.format(self.model.statepoint.filename))
                msg_box.setIcon(QMessageBox.Warning)
                msg_box.setStandardButtons(QMessageBox.Ok)
                msg_box.exec_()
                self.model.statepoint = None

            self.model.currentView = model.currentView
            self.model.activeView = copy.deepcopy(model.currentView)
            self.model.previousViews = model.previousViews
            self.model.subsequentViews = model.subsequentViews

    def resetModels(self):
        self.cellsModel = DomainTableModel(self.model.activeView.cells)
        self.materialsModel = DomainTableModel(self.model.activeView.materials)
        self.cellsModel.beginResetModel()
        self.cellsModel.endResetModel()
        self.materialsModel.beginResetModel()
        self.materialsModel.endResetModel()
        self.colorDialog.updateDomainTabs()

    def showCurrentView(self):
        self.updateScale()
        self.updateRelativeBases()
        self.plotIm.updatePixmap()

        if self.model.previousViews:
            self.undoAction.setDisabled(False)
        if self.model.subsequentViews:
            self.redoAction.setDisabled(False)
        else:
            self.redoAction.setDisabled(True)

        self.adjustWindow()

    def updateScale(self):
        cv = self.model.currentView
        self.scale = (cv.h_res / cv.width, cv.v_res / cv.height)

    def updateRelativeBases(self):
        cv = self.model.currentView
        self.xBasis = 0 if cv.basis[0] == 'x' else 1
        self.yBasis = 1 if cv.basis[1] == 'y' else 2
        self.zBasis = 3 - (self.xBasis + self.yBasis)

    def adjustWindow(self):
        self.setMaximumSize(self.screen.width(), self.screen.height())

    def onRatioChange(self):
        av = self.model.activeView
        if av.aspectLock:
            ratio = av.width / max(av.height, .001)
            av.v_res = int(av.h_res / ratio)
            self.dock.updateVRes()

    def showCoords(self, xPlotPos, yPlotPos):
        cv = self.model.currentView
        if cv.basis == 'xy':
            coords = ("({}, {}, {})".format(round(xPlotPos, 2),
                                            round(yPlotPos, 2),
                                            round(cv.origin[2], 2)))
        elif cv.basis == 'xz':
            coords = ("({}, {}, {})".format(round(xPlotPos, 2),
                                            round(cv.origin[1], 2),
                                            round(yPlotPos, 2)))
        else:
            coords = ("({}, {}, {})".format(round(cv.origin[0], 2),
                                            round(xPlotPos, 2),
                                            round(yPlotPos, 2)))
        self.coord_label.setText('{}'.format(coords))

    def resizePixmap(self):
        self.plotIm._resize()
        self.plotIm.adjustSize()

    def moveEvent(self, event):
        self.adjustWindow()

    def resizeEvent(self, event):
        self.plotIm._resize()
        self.adjustWindow()
        self.updateScale()
        if self.shortcutOverlay.isVisible():
            self.shortcutOverlay.resize(self.width(), self.height())

    def closeEvent(self, event):
        settings = QtCore.QSettings()
        settings.setValue("mainWindow/Size", self.size())
        settings.setValue("mainWindow/Position", self.pos())
        settings.setValue("mainWindow/State", self.saveState())

        settings.setValue("colorDialog/Size", self.colorDialog.size())
        settings.setValue("colorDialog/Position", self.colorDialog.pos())
        visible = int(self.colorDialog.isVisible())
        settings.setValue("colorDialog/Visible", visible)

        openmc.lib.finalize()

        self.saveSettings()

    def saveSettings(self):

        if len(self.model.previousViews) > 10:
            self.model.previousViews = self.model.previousViews[-10:]
        if len(self.model.subsequentViews) > 10:
            self.model.subsequentViews = self.model.subsequentViews[-10:]

        with open('plot_settings.pkl', 'wb') as file:
            if self.model.statepoint:
                self.model.statepoint.close()
            pickle.dump(self.model, file)

    def exportTallyData(self):
        # show export tool dialog
        self.showExportDialog()
示例#10
0
class MainWindow(QMainWindow):
    # Конструктор
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setWindowTitle('ВКР')
        self.setMinimumSize(800, 600)
        self.central_widget = QWidget(self)
        self.layout = QVBoxLayout(self.central_widget)
        self.modules_cb = QComboBox()
        self.modules_cb.currentIndexChanged.connect(self.set_module)
        self.buttons_widget = QWidget()
        self.scroll = QScrollArea()
        self.modules = []
        self.init_ui()

    # Метод инициализации UI
    def init_ui(self):
        file_menu = self.menuBar().addMenu('Файл')
        help_menu = self.menuBar().addMenu('Помощь')
        create_module_action = file_menu.addAction('Создать новый модуль')
        create_module_action.triggered.connect(self.create_module)
        add_module_action = file_menu.addAction('Добавить существующий модуль')
        add_module_action.triggered.connect(self.add_module)
        close_action = file_menu.addAction('Закрыть программу')
        close_action.triggered.connect(self.close_program)
        about_action = help_menu.addAction('О программе')
        about_action.triggered.connect(self.show_about)
        system_name = QLabel(
            f'Операционная система: {QSysInfo.prettyProductName()}',
            self.central_widget)
        system_name.setMaximumHeight(self.central_widget.height() * 0.7)
        system_name.setAlignment(Qt.AlignRight)
        self.layout.addWidget(system_name)
        self.layout.addWidget(self.modules_cb)
        bw_layout = QHBoxLayout(self.buttons_widget)
        edit_button = QPushButton('Редактировать модуль')
        edit_button.clicked.connect(self.edit_module)
        bw_layout.addWidget(edit_button)
        delete_button = QPushButton('Удалить модуль')
        delete_button.clicked.connect(self.delete_module)
        bw_layout.addWidget(delete_button)
        self.layout.addWidget(self.buttons_widget)
        self.layout.addWidget(self.scroll)
        self.setCentralWidget(self.central_widget)
        self.load_modules()
        if self.scroll.widget() is None:
            self.modules_cb.setVisible(False)
            self.buttons_widget.setVisible(False)
        self.scroll.setAlignment(Qt.AlignCenter)

    # Слот, обрабатывающий запрос пользователя на создание нового модуля (нажатие соответствующей кнопки)
    @Slot()
    def create_module(self):
        cmw = CreateModuleWidget(self)
        cmw.module_created.connect(self.add_created_module)
        cmw.show()

    @Slot(str)
    def add_created_module(self, module_full_name):
        if module_full_name != '':
            idx = module_full_name.rfind('/')
            if idx != -1:
                module_short_name = module_full_name[idx + 1:]
                self.check_module(module_full_name, module_short_name)

    # Слот, обрабатывающий событие изменения модуля
    @Slot(int)
    def update_edited_module(self, idx):
        self.show_module(self.modules[idx])

    # Слот, обрабатывающий запрос пользователя на изменение модуля (нажатие соответствующей кнопки)
    @Slot()
    def edit_module(self):
        password, ok = QInputDialog().getText(
            self, 'Ввод пароля', 'Введите пароль для редактирования модуля:',
            QLineEdit.Password)
        if ok:
            module = self.modules[self.modules_cb.currentIndex()]
            try:
                with open(module['full_name'], 'rb') as module_file:
                    crypto_type = module_file.read(3)
                    password_hash = module_file.read(md5().digest_size)
                    if password_hash != md5(password.encode('utf-8')).digest():
                        raise RuntimeError('Введён неправильный пароль.')
                    if crypto_type == b'aes':
                        content = aes_decrypt(module_file.read())
                    elif crypto_type == b'xor':
                        content = xor_str(module_file.read())
                    else:
                        raise RuntimeError(
                            'Неизвестный тип шифрования файла модуля.')
                emw = EditModuleWidget(module, crypto_type,
                                       password_hash, content,
                                       self.modules_cb.currentIndex(), self)
                emw.edited.connect(self.update_edited_module)
                emw.show()
            except IOError:
                mb = QMessageBox(self)
                mb.setWindowTitle('Ошибка')
                mb.setText('При открытии файла модуля возникла ошибка.')
                mb.show()
            except (SyntaxError, RuntimeError) as error:
                mb = QMessageBox(self)
                mb.setWindowTitle('Ошибка')
                mb.setText(str(error))
                mb.show()

    # Слот, обрабатывающий запрос пользователя на удаление модуля (нажатие соответствующей кнопки)
    @Slot()
    def delete_module(self):
        try:
            with open(argv[0].replace('main.py', 'data'), 'rb') as file:
                data = file.read()
            data_list = data.decode('utf-8').split('\n')[:-1]
            for i in range(0, len(data_list)):
                if data_list[i].find(self.modules[
                        self.modules_cb.currentIndex()]['full_name']) > -1:
                    data_list[i] = ''.encode('utf-8')
                else:
                    data_list[i] = (data_list[i] + '\n').encode('utf-8')
            with open(argv[0].replace('main.py', 'data'), 'wb') as file:
                file.writelines(data_list)
        except IOError:
            mb = QMessageBox(self)
            mb.setWindowTitle('Ошибка')
            mb.setText('Не удалось удалить данные о модуле.')
            mb.show()
        del self.modules[self.modules_cb.currentIndex()]
        self.modules_cb.removeItem(self.modules_cb.currentIndex())

    # Слот, обрабатывающий изменение выбранного модуля
    @Slot(int)
    def set_module(self, index):
        if index > -1:
            self.show_module(self.modules[index])
        else:
            self.scroll.widget().setParent(None)
            self.modules_cb.setVisible(False)
            self.buttons_widget.setVisible(False)

    # Слот, обрабатывающий запрос пользователя на показ информации о программе (нажатие соответствующей кнопки)
    @Slot()
    def show_about(self):
        mb = QMessageBox(self)
        mb.setWindowTitle('О программе')
        mb.setText(
            'Данная программа предназначена для создания, редактирования и выполнения модулей, '
            + 'взаимодействующих с операционной системой.')
        mb.show()

    # Слот, обрабатывающий запрос пользователя на закрытие программы (выбора соответствующего пункта меню)
    @Slot()
    def close_program(self):
        self.close()

    # Слот, обрабатывающий запрос пользователя на добавление существующего модуля (выбора соответствующего пункта меню)
    @Slot()
    def add_module(self):
        module_full_name = QFileDialog.getOpenFileName(self, 'Выберите модуль',
                                                       QDir.homePath(),
                                                       '*.module')[0]
        if module_full_name != '':
            idx = module_full_name.rfind('/')
            if idx != -1:
                module_short_name = module_full_name[idx + 1:]
                self.check_module(module_full_name, module_short_name)

    # Метод для проверки модуля на корректность перед добавлением
    def check_module(self, module_full_name, module_short_name):
        if len(self.modules) == 0:
            self.modules.append({
                'full_name': module_full_name,
                'short_name': module_short_name
            })
            self.modules_cb.setVisible(True)
            self.buttons_widget.setVisible(True)
            self.modules_cb.addItem(module_short_name)
            mb = QMessageBox(self)
            mb.setWindowTitle('Успешно')
            mb.setText('Модуль успешно добавлен.')
            mb.show()
            self.save_module_data(module_full_name, module_short_name)
        else:
            for m in self.modules:
                if m['full_name'] == module_full_name:
                    mb = QMessageBox(self)
                    mb.setWindowTitle('Ошибка')
                    mb.setText(f"Модуль '{module_short_name}' уже добавлен.")
                    mb.show()
                    return
            self.modules.append({
                'full_name': module_full_name,
                'short_name': module_short_name
            })
            self.modules_cb.addItem(module_short_name)
            mb = QMessageBox(self)
            mb.setWindowTitle('Успешно')
            mb.setText('Модуль успешно добавлен.')
            mb.show()
            self.save_module_data(module_full_name, module_short_name)

    # Метод сохранения данных о добавленных модулях
    def save_module_data(self, module_full_name, module_short_name):
        try:
            with open(argv[0].replace('main.py', 'data'), 'ab') as file:
                file.write((module_full_name + '|').encode('utf-8'))
                file.write((module_short_name + '\n').encode('utf-8'))
        except IOError:
            mb = QMessageBox(self)
            mb.setWindowTitle('Ошибка')
            mb.setText('Не удалось сохранить информацию о добавленном модуле.')
            mb.show()

    # Метод отображения модуля
    def show_module(self, module):
        try:
            with open(module['full_name'], 'rb') as module_file:
                crypto_type = module_file.read(3)
                password_hash = module_file.read(md5().digest_size)
                if crypto_type == b'aes':
                    content = aes_decrypt(module_file.read())
                elif crypto_type == b'xor':
                    content = xor_str(module_file.read())
                else:
                    raise RuntimeError(
                        'Неизвестный тип шифрования файла модуля.')
            parsed_data = parse(content)
            parsed_data['module_name'] = module['short_name']
            w = self.scroll.widget()
            if w is not None:
                w.setParent(None)
            mc = ModuleContentWidget(parsed_data, self.scroll)
            self.scroll.setWidget(mc)
        except IOError:
            mb = QMessageBox(self)
            mb.setWindowTitle('Ошибка')
            mb.setText('При открытии файла модуля возникла ошибка.')
            mb.show()
        except (SyntaxError, RuntimeError) as error:
            mb = QMessageBox(self)
            mb.setWindowTitle('Ошибка')
            mb.setText(str(error))
            mb.show()

    # Метод загрузки данных о добавленных ранее модулях
    def load_modules(self):
        try:
            with open(argv[0].replace('main.py', 'data'), 'rb') as file:
                data = file.read()
                str_data_list = data.decode('utf-8').split('\n')[:-1]
                for m in str_data_list:
                    self.modules.append({
                        'full_name': m.split('|')[0],
                        'short_name': m.split('|')[1]
                    })
                    self.modules_cb.addItem(self.modules[-1]['short_name'])
        except IOError:
            mb = QMessageBox(self)
            mb.setWindowTitle('Ошибка')
            mb.setText(
                'Не удалось получить информацию о добавленных ранее модулях.')
            mb.show()