def setupUi(self, *args): # called in MainWindowBase.__init__() # put the log widget at the bottom self.addDockWidget(Qt.BottomDockWidgetArea, self._setupLogWidget()) # file widget at the top self.toolbox = ToolBox(self) self._addToolboxItem(self._setupFileWidget()) self._addToolboxItem(self._setupDataWidget()) self._addToolboxItem(self._setupOptimWidget()) self._addToolboxItem(self._setupModelWidget()) self._addToolboxItem(self._setupStatsWidget()) # set up central widget of the main window self.centralLayout = QVBoxLayout() # put buttons in central widget self.centralLayout.addWidget(self.toolbox) self.centralLayout.addWidget(self._setupStartButton()) centralWidget = QWidget(self) centralWidget.setLayout(self.centralLayout) centralWidget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) self.setCentralWidget(centralWidget) self.onStartupSignal.connect(self.initUi) # set program icon, same for Win+Lin icopath = "resources/icon/mcsas.ico" if isMac(): icopath = "resources/icon/mcsas.icns" icopath = QFileInfo(makeAbsolutePath(icopath)).absoluteFilePath() self.setWindowIcon(QIcon(icopath))
def _createEntries(self): entryWidget = QWidget(self) entryLayout = QHBoxLayout(entryWidget) inputWidgets = (self._createParamBox(), self._createAutoRange(), self._createLower(), self._createUpper(), self._createBins(), self._createXScale(), self._createYWeight()) self._labels = dict() # assumes same ordering of entryWidgets above and Histogram.displayData for col, inputWidget in zip(Histogram.displayData, inputWidgets): fieldWidget = QWidget(self) # combines label + input fieldLayout = QVBoxLayout(fieldWidget) fieldLayout.setContentsMargins(QMargins()) # create label, text is set in _selectParam() self._labels[col] = QLabel(self) self._labels[col].setAlignment(Qt.AlignHCenter) # stack label + input fieldLayout.addWidget(self._labels[col]) fieldLayout.addWidget(inputWidget) fieldWidget.setLayout(fieldLayout) # add field to row of inputs entryLayout.addWidget(fieldWidget) entryWidget.setLayout(entryLayout) self.pbox.setCurrentIndex(0) self.lentry.selectAll() # select the first input by default return entryWidget
def _createButtons(self): btnWidget = QWidget(self) btnLayout = QHBoxLayout(btnWidget) okBtn = QPushButton("add", self) okBtn.clicked.connect(self.accept) cancelBtn = QPushButton("cancel", self) cancelBtn.clicked.connect(self.reject) btnLayout.addWidget(okBtn) btnLayout.addWidget(cancelBtn) btnWidget.setLayout(btnLayout) return btnWidget
def _setupStartButton(self): """Set up "Start/Stop" - button.""" self.startStopBtn = QPushButton() self.startStopBtn.setCheckable(True) self.startStopBtn.clicked[bool].connect(self.onStartStopClick) btnLayout = QHBoxLayout() btnLayout.setContentsMargins(0, 0, 0, 0) self.startStopBtn.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) btnLayout.addWidget(self.startStopBtn) btnWidget = QWidget(self) btnWidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) btnWidget.setLayout(btnLayout) return btnWidget
def makeSetting(self, param, activeBtns=False): """Creates an input widget for the provided Parameter and configures it appropriately. """ if param is None: return None widget = QWidget(self) layout = QHBoxLayout(widget) layout.setContentsMargins(0, 0, 0, 0) layout.addStretch() try: suffix = param.suffix() except AttributeError: suffix = None if suffix is None: layout.addWidget(self._makeLabel(param.displayName())) else: layout.addWidget( self._makeLabel(u"{} ({})".format(param.displayName(), suffix))) widget.setLayout(layout) if isString(param.__doc__): # word wrapping by rich text: https://stackoverflow.com/a/4796057 txt = "<span>{0}</span>".format(param.__doc__) # add description as tooltip if available for parameter widget.setToolTip(txt) # create scalar value input widget with min/max limits minmaxValue, widgets = None, [] if isinstance(param, ParameterNumerical): minmaxValue = param.min(), param.max() if isinstance(param, ParameterFloat): minmaxValue = param.displayValueRange() decimals = None if hasattr(param, "decimals"): decimals = param.decimals() w = self._makeEntry(param.name(), param.dtype, param.displayValue(), decimals=decimals, minmax=minmaxValue, parent=widget) try: # set special value text for lower bound, simple solution special = param.displayValues(w.minimum()) w.setSpecialValueText(special) except: pass w.setFixedWidth(FIXEDWIDTH) widgets.insert(old_div(len(widgets), 2), w) # Special widget settings for active fitting parameters: activeBtns = activeBtns and isinstance(param, FitParameterBase) if activeBtns: # create input boxes for user specified active fitting range # within default upper/lower from class definition # minmaxValue = type(param).min(), type(param).max() activeRange = { "min": param.displayActiveRange()[0], "max": param.displayActiveRange()[1] } for bound in "min", "max": w = self._makeEntry(param.name() + bound, param.dtype, activeRange[bound], decimals=decimals, minmax=minmaxValue, parent=widget) w.setPrefix(bound + ": ") w.setFixedWidth(FIXEDWIDTH) widgets.append(w) # create *active* buttons for FitParameters only w = self._makeEntry(param.name() + "active", bool, param.isActive(), widgetType=QPushButton, parent=widget) w.setText("Active") w.setFixedWidth(FIXEDWIDTH * .5) widgets.append(w) # add input widgets to the layout for w in widgets: layout.addWidget(w) # store the parameter name w.parameterName = param.name() # configure UI accordingly (hide/show widgets) # no backend update, ui was just built, data is still in sync self.updateParam(param, emitBackendUpdated=False) return widget
class ModelWidget(AlgorithmWidget): _calculator = None _statsWidget = None # RangeList for (re-)storing histogram settings _models = None def __init__(self, parent, calculator, *args): super(ModelWidget, self).__init__(parent, None, *args) self._calculator = calculator self.title = TitleHandler.setup(self, "Model") # get all loadable ScatteringModels from model directory self._models = FindModels() layout = QVBoxLayout(self) layout.setObjectName("modelLayout") self.setLayout(layout) self.modelBox = QComboBox(self) self.modelBox.setFixedWidth(FIXEDWIDTH) layout.addWidget(self.modelBox) self.modelWidget = QWidget(self) paramLayout = QVBoxLayout(self.modelWidget) self.modelWidget.setLayout(paramLayout) layout.addWidget(self.modelWidget) def setStatsWidget(self, statsWidget): """Sets the statistics widget to use for updating ranges.""" assert (isinstance(statsWidget, AppSettings)) self._statsWidget = statsWidget def onDataSelected(self, dataobj): """Gets the data which is currently selected in the UI and rebuilds the model selection box based on compatible models.""" if not isinstance(dataobj, DataObj): return try: self.modelBox.currentIndexChanged[int].disconnect() except: pass self.modelBox.clear() # build selection list of available models for modelid in self._models: cls, dummy = self._models[modelid] if cls is None or not issubclass(cls, dataobj.modelType): continue category = modelid.split('.')[0:-2] if category[0] == self._models.rootName: del category[0] # set up the display name of the model, may contain category displayName = " / ".join(category + [ cls.name(), ]) # store the unique model identifier alongside its title self.modelBox.addItem(displayName, modelid) self.modelBox.setCurrentIndex(-1) # select none first self.modelBox.currentIndexChanged[int].connect(self._selectModelSlot) # trigger signal by switching from none -> 0 self.modelBox.setCurrentIndex(0) def storeSession(self, section=None): if self.appSettings is None or self.model is None: return model = self.model.name() self.setRootGroup() self.appSettings.beginGroup(self.objectName()) self.appSettings.setValue("model", model) super(ModelWidget, self).storeSession(model) self.appSettings.endGroup() self._statsWidget.storeSession() def restoreSession(self, model=None): """Load last known user settings from persistent app settings.""" if self.appSettings is None: return if model is None: # get the last model used and select it self.appSettings.beginGroup(self.objectName()) model = self.appSettings.value("model") self.appSettings.endGroup() # calls restoreSession(model) and storeSession() # mind the QSettings.group() if not isString(model): # default model if none set model = "Sphere" self.selectModel(model) else: self.appSettings.beginGroup(self.objectName()) super(ModelWidget, self).restoreSession(model) self.appSettings.endGroup() self._statsWidget.restoreSession() def _selectModelSlot(self, key=None): # get the model by its unique name in the items data field modelid = self.modelBox.itemData(key) if modelid not in self._models: return model, dummy = self._models[modelid] if model is None or not issubclass(model, ScatteringModel): return # store current settings before changing the model self.storeSession() self._calculator.model = model() # instantiate the model class # remove parameter widgets from layout layout = self.modelWidget.layout() self.removeWidgets(self.modelWidget) # create new parameter widget based on current selection for p in self.model.params(): try: widget = self.makeSetting(p, activeBtns=True) layout.addWidget(widget) except Exception as e: DisplayException(e, fmt=u"An error occurred on building a " "UI for parameter '{p}' in model '{m}'." "<p><nobr>{e}</nobr></p>" "Please see the log for a traceback.".format( p=p.name(), m=self.model.name(), e="{e}")) self.removeWidgets(self.modelWidget) self.modelBox.setCurrentIndex(0) return layout.addStretch() # restore user settings for this model self.restoreSession(self.model.name()) def selectModel(self, model): """*model*: string containing the name of the model to select. Calls _selectModelSlot() via signal.""" if not isString(model): return index = 0 # search the model with the provided name for i in range(0, self.modelBox.count()): if self.modelBox.itemText(i).lower() == model.lower().strip(): index = i break # set the index found or the first one otherwise self.modelBox.setCurrentIndex(index) @AlgorithmWidget.algorithm.getter def algorithm(self): if self._calculator is None: return None return self._calculator.model model = algorithm def setSphericalSizeRange(self, minVal, maxVal): key = "radius" # get parameter display order of magnitude: param = None for p in self.model.params(): if key in p.name().lower(): param = p break if param is None: logging.debug("No 'radius'-named parameter found, " "not setting spherical size range!") return False # nothing to do keymin, keymax = key + "min", key + "max" if self.get(keymin) is not None and self.get(keymax) is not None: self.set(keymin, param.toDisplay(minVal)) self.set(keymax, param.toDisplay(maxVal)) return True return False