class DefaultSelectParameterWidget(SelectParameterWidget): """Widget class for Default Select Parameter.""" def __init__(self, parameter, parent=None): """Constructor :param parameter: A DefaultSelectParameter object. :type parameter: DefaultSelectParameter """ super(DefaultSelectParameterWidget, self).__init__(parameter, parent) self.default_layout = QHBoxLayout() self.radio_button_layout = QHBoxLayout() self.radio_button_widget = QWidget() self.default_label = QLabel(tr('Default')) # Create radio button group self.default_input_button_group = QButtonGroup() # Define string enabler for radio button self.radio_button_enabler = self.input.itemData(0, Qt.UserRole) for i in range(len(self._parameter.default_labels)): if '%s' in self._parameter.default_labels[i]: label = ( self._parameter.default_labels[i] % self._parameter.default_values[i]) else: label = self._parameter.default_labels[i] radio_button = QRadioButton(label) self.radio_button_layout.addWidget(radio_button) self.default_input_button_group.addButton(radio_button, i) if self._parameter.default_value == \ self._parameter.default_values[i]: radio_button.setChecked(True) # Create double spin box for custom value self.custom_value = QDoubleSpinBox() if self._parameter.default_values[-1]: self.custom_value.setValue(self._parameter.default_values[-1]) has_min = False if self._parameter.minimum is not None: has_min = True self.custom_value.setMinimum(self._parameter.minimum) has_max = False if self._parameter.maximum is not None: has_max = True self.custom_value.setMaximum(self._parameter.maximum) if has_min and has_max: step = (self._parameter.maximum - self._parameter.minimum) / 100.0 self.custom_value.setSingleStep(step) self.radio_button_layout.addWidget(self.custom_value) self.toggle_custom_value() # Reset the layout self.input_layout.setParent(None) self.help_layout.setParent(None) self.label.setParent(None) self.inner_input_layout.setParent(None) self.input_layout = QGridLayout() self.input_layout.setSpacing(0) self.input_layout.addWidget(self.label, 0, 0) self.input_layout.addLayout(self.inner_input_layout, 0, 1) self.input_layout.addWidget(self.default_label, 1, 0) self.input_layout.addLayout(self.radio_button_layout, 1, 1) self.main_layout.addLayout(self.input_layout) self.main_layout.addLayout(self.help_layout) # check every added combobox, it could have been toggled by # the existing keyword self.toggle_input() # Connect # noinspection PyUnresolvedReferences self.input.currentIndexChanged.connect(self.toggle_input) self.default_input_button_group.buttonClicked.connect( self.toggle_custom_value) def raise_invalid_type_exception(self): message = 'Expecting element type of %s' % ( self._parameter.element_type.__name__) err = ValueError(message) return err def get_parameter(self): """Obtain list parameter object from the current widget state. :returns: A DefaultSelectParameter from the current state of widget. """ current_index = self.input.currentIndex() selected_value = self.input.itemData(current_index, Qt.UserRole) if hasattr(selected_value, 'toPyObject'): selected_value = selected_value.toPyObject() try: self._parameter.value = selected_value except ValueError: err = self.raise_invalid_type_exception() raise err radio_button_checked_id = self.default_input_button_group.checkedId() # No radio button checked, then default value = None if radio_button_checked_id == -1: self._parameter.default = None # The last radio button (custom) is checked, get the value from the # line edit elif (radio_button_checked_id == len(self._parameter.default_values) - 1): self._parameter.default_values[radio_button_checked_id] \ = self.custom_value.value() self._parameter.default = self.custom_value.value() else: self._parameter.default = self._parameter.default_values[ radio_button_checked_id] return self._parameter def set_default(self, default): """Set default value by item's string. :param default: The default. :type default: str, int :returns: True if success, else False. :rtype: bool """ # Find index of choice try: default_index = self._parameter.default_values.index(default) self.default_input_button_group.button(default_index).setChecked( True) except ValueError: last_index = len(self._parameter.default_values) - 1 self.default_input_button_group.button(last_index).setChecked( True) self.custom_value.setValue(default) self.toggle_custom_value() def toggle_custom_value(self): radio_button_checked_id = self.default_input_button_group.checkedId() if (radio_button_checked_id == len(self._parameter.default_values) - 1): self.custom_value.setDisabled(False) else: self.custom_value.setDisabled(True) def toggle_input(self): """Change behaviour of radio button based on input.""" current_index = self.input.currentIndex() # If current input is not a radio button enabler, disable radio button. if self.input.itemData(current_index, Qt.UserRole) != ( self.radio_button_enabler): self.disable_radio_button() # Otherwise, enable radio button. else: self.enable_radio_button() def set_selected_radio_button(self): """Set selected radio button to 'Do not report'.""" dont_use_button = self.default_input_button_group.button( len(self._parameter.default_values) - 2) dont_use_button.setChecked(True) def disable_radio_button(self): """Disable radio button group and custom value input area.""" checked = self.default_input_button_group.checkedButton() if checked: self.default_input_button_group.setExclusive(False) checked.setChecked(False) self.default_input_button_group.setExclusive(True) for button in self.default_input_button_group.buttons(): button.setDisabled(True) self.custom_value.setDisabled(True) def enable_radio_button(self): """Enable radio button and custom value input area then set selected radio button to 'Do not report'. """ for button in self.default_input_button_group.buttons(): button.setEnabled(True) self.set_selected_radio_button() self.custom_value.setEnabled(True)
class SDEllipseDialog(QDialog, FORM_CLASS): def __init__(self, iface, parent=None): self.iface = iface self.plugin_dir = os.path.dirname(__file__) # Some constants self.SDELLIPSE = self.tr('SD Ellipse') self.CANCEL = self.tr('Cancel') self.HELP = self.tr('Help') self.CLOSE = self.tr('Close') self.OK = self.tr('OK') """Constructor.""" super(SDEllipseDialog, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) self.method_group = QButtonGroup() self.method_group.addButton(self.yuill_rb) self.method_group.addButton(self.crimestat_rb) self.method_group.buttonClicked[QAbstractButton].connect( self.methodChanged) self.yuill_rb.setChecked(True) self.method = 1 okButton = self.button_box.button(QDialogButtonBox.Ok) okButton.setText(self.OK) cancelButton = self.button_box.button(QDialogButtonBox.Cancel) cancelButton.setText(self.CANCEL) cancelButton.setEnabled(False) # helpButton = self.button_box.button(QDialogButtonBox.Help) helpButton = self.helpButton helpButton.setText(self.HELP) closeButton = self.button_box.button(QDialogButtonBox.Close) closeButton.setText(self.CLOSE) # Connect signals okButton.clicked.connect(self.startWorker) cancelButton.clicked.connect(self.killWorker) helpButton.clicked.connect(self.giveHelp) closeButton.clicked.connect(self.reject) self.cumulative = False inpIndexCh = self.InputLayer.currentIndexChanged['QString'] inpIndexCh.connect(self.layerchanged) # QObject.disconnect(self.button_box, SIGNAL("rejected()"), # self.reject) self.button_box.rejected.disconnect(self.reject) # Set instance variables self.worker = None self.inputlayerid = None self.layerlistchanging = False self.selectedFeatures_cb.setChecked(True) self.useWeights_cb.setChecked(False) self.result = None # end of __init__ def giveHelp(self): self.showInfo('Giving help') QDesktopServices.openUrl(QUrl.fromLocalFile( self.plugin_dir + "/help/html/index.html")) # showPluginHelp(None, "help/html/index") # end of giveHelp def startWorker(self): # self.showInfo('Ready to start worker') self.degfreedCorr = self.degfreedcorr_cb.isChecked() self.crimestatCorr = self.crimestatcorr_cb.isChecked() # Get the input layer layerindex = self.InputLayer.currentIndex() layerId = self.InputLayer.itemData(layerindex) inputlayer = QgsProject.instance().mapLayer(layerId) if inputlayer is None: self.showError(self.tr('No input layer defined')) return self.featureCount = 0 if self.selectedFeatures_cb.isChecked(): self.featureCount = inputlayer.selectedFeatureCount() if self.featureCount == 0: self.featureCount = inputlayer.featureCount() if self.featureCount < 2: self.showError(self.tr('Not enough features')) # self.scene.clear() return if (self.useWeights_cb.isChecked() and self.inputField.count() == 0): self.showError(self.tr('Missing numerical field')) return fieldindex = self.inputField.currentIndex() fieldname = self.inputField.itemData(fieldindex) # inpfield = inputlayer.fieldNameIndex(fieldindex) # minval = inputlayer.minimumValue(inpfield) if (not self.useWeights_cb.isChecked()): fieldname = None self.result = None self.SDLayer = inputlayer self.method = 1 if self.yuill_rb.isChecked(): self.method = 1 elif self.crimestat_rb.isChecked(): self.method = 2 if self.featureCount < 3 and (self.method == 2 or self.degfreedCorr): self.showError(self.tr('Not enough features')) return # create a new worker instance worker = Worker(inputlayer, self.selectedFeatures_cb.isChecked(), fieldname, self.method) # start the worker in a new thread thread = QThread(self) worker.moveToThread(thread) worker.finished.connect(self.workerFinished) worker.error.connect(self.workerError) worker.status.connect(self.workerInfo) worker.progress.connect(self.progressBar.setValue) # worker.progress.connect(self.aprogressBar.setValue) thread.started.connect(worker.run) thread.start() self.thread = thread self.worker = worker self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) self.button_box.button(QDialogButtonBox.Close).setEnabled(False) self.button_box.button(QDialogButtonBox.Cancel).setEnabled(True) self.InputLayer.setEnabled(False) self.inputField.setEnabled(False) # end of startWorker def workerFinished(self, ok, ret): """Handles the output from the worker and cleans up after the worker has finished.""" # clean up the worker and thread self.worker.deleteLater() self.thread.quit() self.thread.wait() self.thread.deleteLater() # remove widget from the message bar (pop) # self.iface.messageBar().popWidget(self.messageBar) if ok and ret is not None: # self.showInfo("Result: " + str(ret)) self.result = ret # Draw the ellipse self.drawEllipse() else: # notify the user that something went wrong if not ok: self.showError(self.tr('Aborted') + '!') else: self.showError(self.tr('Not able to create ellipse') + '!') # Update the user interface self.progressBar.setValue(0.0) self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) self.button_box.button(QDialogButtonBox.Close).setEnabled(True) self.button_box.button(QDialogButtonBox.Cancel).setEnabled(False) self.InputLayer.setEnabled(True) if self.method == 1 and self.inputField.count() > 0: self.inputField.setEnabled(True) # end of workerFinished(self, ok, ret) def workerError(self, exception_string): """Report an error from the worker.""" self.showError(self.tr('Worker failed - exception') + ': ' + exception_string) def workerInfo(self, message_string): """Report an info message from the worker.""" self.showInfo(self.tr('Worker') + ': ' + message_string) def killWorker(self): """Kill the worker thread.""" if self.worker is not None: QgsMessageLog.logMessage(self.tr('Killing worker'), self.SDELLIPSE, Qgis.Info) self.worker.kill() # Implement the reject method to have the possibility to avoid # exiting the dialog when cancelling def reject(self): """Reject override.""" # exit the dialog QDialog.reject(self) def drawEllipse(self): # self.showInfo('Result: ' + str(self.result)) meanx = self.result[0] meany = self.result[1] angle1 = self.result[2] angle2 = self.result[3] SD1 = self.result[4] SD2 = self.result[5] if self.method == 2: # CrimeStat SD1 = SD1 * (sqrt(2) * sqrt(self.featureCount) / sqrt(self.featureCount - 2)) SD2 = SD2 * (sqrt(2) * sqrt(self.featureCount) / sqrt(self.featureCount - 2)) if self.crimestatCorr and self.method != 2: SD1 = SD1 * sqrt(2) SD2 = SD2 * sqrt(2) if self.degfreedCorr and self.method != 2: SD1 = SD1 * sqrt(self.featureCount) / sqrt(self.featureCount - 2) SD2 = SD2 * sqrt(self.featureCount) / sqrt(self.featureCount - 2) # SD1 = SD1 * sqrt(self.featureCount) / sqrt(self.featureCount - 1) # SD2 = SD2 * sqrt(self.featureCount) / sqrt(self.featureCount - 1) # Find the major and minor axis majoraxisangle = angle1 minoraxisangle = angle2 majorSD = SD2 minorSD = SD1 if SD2 < SD1: majoraxisangle = angle2 minoraxisangle = angle1 majorSD = SD1 minorSD = SD2 # Calculate the "compass" direction angle (clockwise from north) direction = 90.0 - majoraxisangle * 180 / pi # Calculte the eccentricity eccentricity = sqrt(1 - pow(minorSD, 2) / pow(majorSD, 2)) # Create the memory layer for the ellipse sdefields = [] sdefields.append(QgsField("meanx", QVariant.Double)) sdefields.append(QgsField("meany", QVariant.Double)) sdefields.append(QgsField("majoranglerad", QVariant.Double)) # sdefields.append(QgsField("minoranglerad", QVariant.Double)) sdefields.append(QgsField("directiondeg", QVariant.Double)) sdefields.append(QgsField("majorsd", QVariant.Double)) sdefields.append(QgsField("minorsd", QVariant.Double)) sdefields.append(QgsField("eccentricity", QVariant.Double)) layeruri = 'Polygon?' layeruri = (layeruri + 'crs=' + str(self.SDLayer.dataProvider().crs().authid())) memSDlayer = QgsVectorLayer(layeruri, self.OutputLayerName.text(), "memory") # Set the CRS to the original CRS object memSDlayer.setCrs(self.SDLayer.dataProvider().crs()) memSDlayer.startEditing() # ? for field in sdefields: memSDlayer.dataProvider().addAttributes([field]) sdfeature = QgsFeature() theta1 = majoraxisangle points = [] step = pi / 180 # 360 points to draw the ellipse t = 0.0 while t < 2 * pi: p1 = QPointF(meanx + majorSD * cos(t) * cos(majoraxisangle) - minorSD * sin(t) * sin(majoraxisangle), meany + majorSD * cos(t) * sin(majoraxisangle) + minorSD * sin(t) * cos(majoraxisangle)) points.append(QgsPointXY(p1)) t = t + step # Close the polygon p1 = QPointF(meanx + majorSD * cos(majoraxisangle), meany + majorSD * sin(majoraxisangle)) points.append(QgsPointXY(p1)) sdfeature.setGeometry(QgsGeometry.fromPolygonXY([points])) attrs = [meanx, meany, majoraxisangle, direction, majorSD, minorSD, eccentricity] sdfeature.setAttributes(attrs) memSDlayer.dataProvider().addFeatures([sdfeature]) memSDlayer.commitChanges() # ? memSDlayer.updateExtents() QgsProject.instance().addMapLayers([memSDlayer]) # end of drawEllipse def layerchanged(self, number=0): """Do the necessary updates after a layer selection has been changed.""" self.layerselectionactive = True layerindex = self.InputLayer.currentIndex() layerId = self.InputLayer.itemData(layerindex) self.inputlayerid = layerId inputlayer = QgsProject.instance().mapLayer(layerId) while self.inputField.count() > 0: self.inputField.removeItem(0) self.useWeights_cb.setEnabled(False) self.inputField.setEnabled(False) self.layerselectionactive = False # Get the numerical fields if inputlayer is not None: provider = inputlayer.dataProvider() attribs = provider.fields() if str(type(attribs)) != "<type 'dict'>": atr = {} for i in range(attribs.count()): atr[i] = attribs.at(i) attrdict = atr for id, attrib in attrdict.items(): # Check for numeric attribute #if attrib.typeName().upper() in ('REAL', 'INTEGER', 'INT4', # 'INT8', 'FLOAT4'): if attrib.isNumeric(): self.inputField.addItem(attrib.name(), attrib.name()) if (self.inputField.count() > 0): if self.method == 1: self.useWeights_cb.setEnabled(True) self.inputField.setEnabled(True) else: self.useWeights_cb.setChecked(False) self.OutputLayerName.setText( "SDE_" + self.method_group.checkedButton().text().strip('"') + "_" + self.InputLayer.currentText()) # end of layerchanged def methodChanged(self, button): if self.InputLayer.currentText() is not None: self.OutputLayerName.setText("SDE_" + button.text().strip('"') + "_" + self.InputLayer.currentText()) # Disable all options self.crimestatcorr_cb.setEnabled(False) self.crimestatcorr_cb.setChecked(False) self.degfreedcorr_cb.setEnabled(False) self.degfreedcorr_cb.setChecked(False) self.useWeights_cb.setEnabled(False) self.useWeights_cb.setChecked(False) self.inputField.setEnabled(False) if button.text() == '"CrimeStat"': self.method = 2 elif button.text() == "Yuill": self.method = 1 self.crimestatcorr_cb.setEnabled(True) self.degfreedcorr_cb.setEnabled(True) if self.inputField.count() > 0: self.useWeights_cb.setEnabled(True) self.inputField.setEnabled(True) else: # Should not be reached yet if self.inputField.count() > 0: self.useWeights_cb.setEnabled(True) self.inputField.setEnabled(True) def showError(self, text): """Show an error.""" # self.iface.messageBar().pushMessage(self.tr('Error'), text, # level=QgsMessageBar.CRITICAL, # duration=3) QgsMessageLog.logMessage('Error: ' + text, self.SDELLIPSE, Qgis.Critical) def showWarning(self, text): """Show a warning.""" # self.iface.messageBar().pushMessage(self.tr('Warning'), text, # level=QgsMessageBar.WARNING, # duration=2) QgsMessageLog.logMessage('Warning: ' + text, self.SDELLIPSE, Qgis.Warning) def showInfo(self, text): """Show info.""" # self.iface.messageBar().pushMessage(self.tr('Info'), text, # level=QgsMessageBar.INFO, # duration=2) QgsMessageLog.logMessage('Info: ' + text, self.SDELLIPSE, Qgis.Info) # Implement the accept method to avoid exiting the dialog when # starting the work def accept(self): """Accept override.""" pass # Implement the reject method to have the possibility to avoid # exiting the dialog when cancelling def reject(self): """Reject override.""" # exit the dialog QDialog.reject(self) # Translation def tr(self, message): """Get the translation for a string using Qt translation API. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ return QCoreApplication.translate('Dialog', message) # Overriding def resizeEvent(self, event): return # self.showInfo("resizeEvent") # Overriding def showEvent(self, event): return
class OptionWidget(EditorWidget): widgettype = 'Option Row' def __init__(self, *args, **kwargs): super(OptionWidget, self).__init__(*args, **kwargs) self._bindvalue = None self.group = QButtonGroup() self.group.buttonClicked.connect(self.emitvaluechanged) def createWidget(self, parent=None): widget = QWidget(parent) return widget def _buildfromlist(self, listconfig, multiselect): def chunks(l, n): """ Yield successive n-sized chunks from l. """ for i in range(0, len(l), n): yield l[i:i + n] items = listconfig['items'] wrap = self.config.get('wrap', 0) showcolor = self.config.get('always_color', False) if wrap > 0: rows = list(chunks(items, wrap)) else: rows = [items] for rowcount, row in enumerate(rows): for column, item in enumerate(row): parts = item.split(';') data = parts[0] try: desc = parts[1] except IndexError: desc = data button = QPushButton() button.setCheckable(multiselect) self.group.setExclusive(not multiselect) icon = QIcon() try: path = parts[2] if path.startswith("#"): # Colour the button with the hex value. # If show color is enabled we always show the color regardless of selection. if not showcolor: style = """ QPushButton::checked {{ border: 3px solid rgb(137, 175, 255); background-color: {colour}; }}""".format(colour=path) else: style = """ QPushButton::checked {{ border: 3px solid rgb(137, 175, 255); }} QPushButton {{ background-color: {colour}; }}""".format(colour=path) button.setStyleSheet(style) elif path.endswith("_icon"): icon = QIcon(":/icons/{}".format(path)) else: icon = QIcon(path) except: icon = QIcon() button.setCheckable(True) button.setText(desc) button.setProperty("value", data) button.setIcon(icon) button.setIconSize(QSize(24, 24)) if isinstance(self.widget.layout(), QBoxLayout): self.widget.layout().addWidget(button) else: self.widget.layout().addWidget(button, rowcount, column) self.group.addButton(button) def initWidget(self, widget, config): if not widget.layout(): widget.setLayout(QGridLayout()) widget.layout().setContentsMargins(0, 0, 0, 0) def updatefromconfig(self): super(OptionWidget, self).updatefromconfig() for button in self.group.buttons(): self.group.removeButton(button) self.widget.layout().removeWidget(button) button.deleteLater() button.setParent(None) listconfig = self.config['list'] multiselect = self.config.get('multi', False) self._buildfromlist(listconfig, multiselect) super(OptionWidget, self).endupdatefromconfig() def validate(self, *args): button = self.group.checkedButton() if button: return True return False @property def buttons(self): return self.group.buttons() @property def nullvalues(self): return ['NULL'] @property def multioption(self): return self.config.get('multi', False) def setvalue(self, value): def set_button(setvalue): for button in self.group.buttons(): buttonvalue = button.property("value") if (setvalue is None and buttonvalue in self.nullvalues) or buttonvalue == str(setvalue): button.setChecked(True) self.emitvaluechanged() return if value in self.nullvalues: value = None if self.multioption and value: values = value.split(';') else: values = [value] for value in values: set_button(value) def value(self): def _returnvalue(): if self.multioption: _values = [] checked = [button for button in self.group.buttons() if button.isChecked()] for button in checked: value = button.property("value") _values.append(value) if not _values: return None return ";".join(_values) else: checked = self.group.checkedButton() if not checked: return None value = checked.property("value") return value returnvalue = _returnvalue() if returnvalue in self.nullvalues: returnvalue = None return returnvalue def reset(self): for button in self.group.buttons(): if button.isChecked(): button.setChecked(False) self.emitvaluechanged() return def togglebutton(self, value): for button in self.group.buttons(): if button.property("value") == value: button.setChecked(not button.isChecked()) self.emitvaluechanged() return return