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)
class ViewWidget(QWidget): exercise_name_label = None exercise_name_line = None scroll_area = None base_widget = None exercises_widget = None return_button = None add_button = None def __init__(self): QWidget.__init__(self) self.file = "" self.setup_widget() def setup_widget(self): self.exercise_name_label = QLabel("Exercise name:", self) self.exercise_name_label.move(5, 5) self.exercise_name_label.resize(125, 25) self.add_button = QPushButton("Add", self) self.add_button.resize(75, 25) self.add_button.clicked.connect(self.add_line) self.exercise_name_line = QLineEdit(self) self.exercise_name_line.move(135, 5) self.exercise_name_line.resize(125, 25) self.scroll_area = QScrollArea(self) self.base_widget = QWidget(self) self.scroll_area.setWidget(self.base_widget) self.exercises_widget = QVBoxLayout() self.exercises_widget.setAlignment(Qt.AlignTop) self.base_widget.setLayout(self.exercises_widget) self.return_button = QPushButton("Return wo save", self) def resizeEvent(self, event): self.scroll_area.move(5, 35) self.scroll_area.resize(self.width() - 165, self.height() - 40) self.add_button.move(self.width() - 160 - 75, 5) self.return_button.move(self.width() - 155, 5) self.return_button.resize(150, 40) self.base_widget.resize(self.scroll_area.width() - 25, self.exercises_widget.count() * 25) def clear_widget(self): while self.exercises_widget.count() > 0: self.exercises_widget.takeAt(0) def open_exercise_file(self, file: str): self.file = file with open(self.file, "r") as json_file: json_data = json.load(json_file) name = json_data['name'] for data in json_data['exercise']: movement = data['name'] description = data['description'] time = data['time'] widget = PanelWidget() widget.set_data(movement, description, time) widget.remove_signal.connect(self.remove_panel_item) widget.move_down_signal.connect(self.move_widget_down) widget.move_up_signal.connect(self.move_widget_up) self.exercises_widget.addWidget(widget) json_file.close() self.base_widget.resize(self.scroll_area.width() - 25, self.exercises_widget.count() * 25) self.exercise_name_line.setText(name) @Slot() def add_line(self): widget = PanelWidget() self.exercises_widget.addWidget(widget) self.base_widget.resize(self.scroll_area.width() - 25, self.exercises_widget.count() * 25) @Slot(QWidget) def move_widget_down(self, widget: QWidget): ind = self.exercises_widget.indexOf(widget) self.exercises_widget.removeWidget(widget) self.exercises_widget.insertWidget((ind + 1), widget) @Slot(QWidget) def move_widget_up(self, widget: QWidget): ind = self.exercises_widget.indexOf(widget) self.exercises_widget.removeWidget(widget) self.exercises_widget.insertWidget((ind - 1), widget) @Slot(QWidget) def remove_panel_item(self, widget: QWidget): self.exercises_widget.removeWidget(widget) self.base_widget.resize(self.scroll_area.width() - 25, self.exercises_widget.count() * 25)