def selectFeatureClicked(self, layer, column, message, searchsize, bindto): """ Loads the select from map action tool. Switchs to the map to allow the user to select a feature. controlname - The control name when looking up in the settings for the button config. """ self.tool = SelectFeatureTool(self.canvas, layer, column, bindto, searchsize) self.tool.foundFeature.connect(self.bindHighlightedFeature) self.tool.setActive() self.beginSelectFeature.emit(message)
class FormBinder(QObject): beginSelectFeature = pyqtSignal(str) endSelectFeature = pyqtSignal() """ Handles binding of values to and out of the form. """ def __init__(self, layer, formInstance, canvas, settings, formmodule, db): QObject.__init__(self) self.layer = layer self.canvas = canvas self.forminstance = formInstance self.fields = self.layer.pendingFields() self.fieldtocontrol = {} self.actionlist = [] self.settings = settings self.images = {} self.mandatory_group = MandatoryGroup() self.forsaving = set() self.formmodule = formmodule self.db = db def bindFeature(self, qgsfeature, mandatory_fields=True, editing=False): """ Binds a features values to the form. If the control has the mandatory property set then it will be added to the mandatory group. qgsfeature - A QgsFeature to bind the values from mandatory_fields - True if mandatory fields should be respected (default) """ self.feature = qgsfeature self.connectControlsToSQLCommands() defaults = self.getDefaults() for index, value in qgsfeature.attributeMap().items(): field = self.fields[index] try: control = self.getControl(field.name()) except ControlNotFound as ex: warning(ex.message) continue if mandatory_fields: mandatory = control.property("mandatory").toBool() if mandatory: buddy = self.getBuddy(control) self.mandatory_group.addWidget(control, buddy) info("Binding %s to %s" % (control.objectName(), value.toString())) isdefaultset = False if not editing: try: # Get the default value from the database and use that instead. value = defaults[control] isdefaultset = control in defaults except KeyError: pass try: self.bindValueToControl(control, value) except BindingError as er: warning(er.reason) self.bindSaveValueButton(control, indefaults=isdefaultset) self.createHelpLink(control) self.fieldtocontrol[index] = control def createHelpLink(self, control): name = control.objectName() helpfile = self.formmodule.getHelpFile(name) if helpfile: label = self.getBuddy(control) if label is control: return if label is None: return text = '<a href="%s">%s<a>' % (helpfile, label.text()) label.setText(text) label.linkActivated.connect(self.showHelp) def showHelp(self, url): dlg = HelpViewDialog() dlg.loadFile(url) dlg.exec_() def getDefaults(self): query = QSqlQuery("SELECT control, value FROM DefaultValues") query.exec_() defaults = {} while query.next(): try: name = query.value(0).toString() control = self.getControl(name) except ControlNotFound: continue value = query.value(1) defaults[control] = value return defaults def connectControlsToSQLCommands(self): """ Loops all the controls and connects update signals in order to use SQL commands correctly. Note: We check all control because we can use SQL on non field bound controls in order to show information. """ for control in self.forminstance.findChildren(QWidget): self.connectSQL(control) def getBuddy(self, control): try: label = self.getControl(control.objectName() + "_label", control_type=QLabel) return label except ControlNotFound: return control def getControl(self, name, control_type=QWidget): control = self.forminstance.findChild(control_type, name) if not control: raise ControlNotFound(name) return control def bindByName(self, controlname, value): """ Binds a value to a control based on the control name. controlname - Name of the control to bind value - QVariant holding the value. """ control = self.getControl(controlname) try: self.bindValueToControl(control, value) except BindingError as er: warning(er.reason) def saveComboValues(self, combobox, text): """ Save the value of the combo box into the form settings values. Only saves new values. """ comboitems = [combobox.itemText(i) for i in range(combobox.count())] name = combobox.objectName() query = QSqlQuery() query.prepare("SELECT value FROM ComboBoxItems WHERE control = :contorl") query.bindValue(":control", name) query.exec_() log("LAST ERROR") log(query.lastError().text()) items = [] while query.next(): value = query.value(0).toString() if not value.isEmpty(): items.append(str(value)) if not text in comboitems and not text in items: query = QSqlQuery() query.prepare("INSERT INTO ComboBoxItems (control, value)" "VALUES (:control,:value)") query.bindValue(":control", name) query.bindValue(":value", text) query.exec_() log("LAST ERROR FOR INSERT") log(query.lastError().text()) def updateControl(self, control, mapping, sql, *args): """ Update control with result from SQL query. """ query = QSqlQuery() query.prepare(sql) # Loop though all the placeholders and get value from the # function assigned to each one. for holder, function in mapping.items(): value = function() query.bindValue(":%s" % holder, value) query.exec_() for key, value in query.boundValues().items(): log("%s is %s" % (key, value.toString())) if isinstance(control, QComboBox): control.clear() control.addItem("") while query.next(): value = query.value(0).toString() if not value.isEmpty(): control.addItem(value) def connectSQL(self, control): sql = control.property("sql").toString() if not sql.isEmpty(): log("QUERY:%s" % sql) placeholders = re.findall(r":(\w+)", sql) mapping = {} # Loop though all the sql placeholders and look for a # control with that name to get updates from. for holder in placeholders: linked_control = self.getControl(holder) if linked_control is None: continue if isinstance(linked_control, QListWidget): mapping[holder] = lambda c=linked_control: c.currentItem().text() linked_control.currentItemChanged.connect(partial(self.updateControl, control, mapping, sql)) def bindValueToControl(self, control, value): """ Binds a control to the supplied value. Raises BindingError() if control is not supported. control - QWidget based control that takes the new value value - A QVariant holding the value """ if isinstance(control, QCalendarWidget): control.setSelectedDate(QDate.fromString(value.toString(), Qt.ISODate)) elif isinstance(control, QListWidget): items = control.findItems(value.toString(), Qt.MatchExactly) try: control.setCurrentItem(items[0]) control.currentItemChanged.emit(None, items[0]) except IndexError: pass elif isinstance(control, QLineEdit) or isinstance(control, QTextEdit): control.setText(value.toString()) elif isinstance(control, QCheckBox) or isinstance(control, QGroupBox): control.setChecked(value.toBool()) elif isinstance(control, QPlainTextEdit): control.setPlainText(value.toString()) elif isinstance(control, QComboBox): # Add items stored in the database query = QSqlQuery() query.prepare("SELECT value FROM ComboBoxItems WHERE control = :contorl") query.bindValue(":control", control.objectName()) query.exec_() while query.next(): newvalue = query.value(0).toString() if not newvalue.isEmpty(): control.addItem(newvalue) itemindex = control.findText(value.toString()) control.setCurrentIndex(itemindex) elif isinstance(control, QDoubleSpinBox): double, passed = value.toDouble() control.setValue(double) elif isinstance(control, QSpinBox): integer, passed = value.toInt() control.setValue(integer) elif isinstance(control, QDateTimeEdit): control.setDateTime(QDateTime.fromString(value.toString(), Qt.ISODate)) # Wire up the date picker button parent = control.parentWidget() if parent: button = parent.findChild(QPushButton, control.objectName() + "_pick") if button: button.setIcon(QIcon(":/icons/calender")) button.setText("Pick") button.setIconSize(QSize(24, 24)) button.pressed.connect(partial(self.pickDateTime, control, "DateTime")) elif isinstance(control, QPushButton): if control.text() == "Drawing": control.setIcon(QIcon(":/icons/draw")) control.setIconSize(QSize(24, 24)) control.pressed.connect(partial(self.loadDrawingTool, control)) else: raise BindingError(control, value.toString(), "Unsupported widget %s" % control) def loadDrawingTool(self, control): """ Load the drawing tool. control - The control (QWidget) who owns this drawing. Its name is used in the naming of the final image. """ controlname = str(control.objectName()) self.forminstance.hide() curdir = os.path.dirname(__file__) id = self.feature.attributeMap()[self.layer.fieldNameIndex("UniqueID")].toString() savedname = str(id) + "_" + controlname + ".jpg" imagename = os.path.join(curdir, "data", str(self.layer.name()), "images", savedname) tempname = "drawingFor_{0}".format(controlname) tempimage = os.path.join(tempfile.gettempdir(), tempname) log("Looking for {0} or {1}".format(imagename, tempimage)) imagetoload = self.images.get(controlname, imagename) drawingpad = DrawingPad(imagetoload) drawingpad.setWindowState(Qt.WindowFullScreen | Qt.WindowActive) drawingpad.ui.actionMapSnapshot.triggered.connect(partial(self.drawingPadMapSnapshot, drawingpad)) if drawingpad.exec_(): # Save the image to a temporay location until commit. self.images[controlname] = tempimage + ".png" drawingpad.saveImage(tempimage) self.forminstance.show() else: self.forminstance.show() def drawingPadMapSnapshot(self, pad): """ Saves the current view of the map canvas to a image and it into the drawing pad. pad - The drawing pad that will take the final image. """ # TODO Refactor me!! image = QPixmap.fromImage(pad.scribbleArea.image) tempimage = os.path.join(tempfile.gettempdir(), "mapcanvascapture.png") self.canvas.saveAsImage(tempimage, image) pad.openImage(tempimage) def unbindFeature(self, qgsfeature): """ Unbinds the feature from the form saving the values back to the QgsFeature. qgsfeature -- A QgsFeature that will store the new values. TODO: If the parent of the control is a QGroupBox and is disabled, the control is ignored for changing. """ for index, control in self.fieldtocontrol.items(): value = None if isinstance(control, QLineEdit): value = control.text() elif isinstance(control, QTextEdit) or isinstance(control, QPlainTextEdit): value = control.toPlainText() elif isinstance(control, QCalendarWidget): value = control.selectedDate().toString(Qt.ISODate) elif isinstance(control, QCheckBox) or isinstance(control, QGroupBox): value = 0 if control.isChecked(): value = 1 elif isinstance(control, QComboBox): value = control.currentText() if control.isEditable(): self.saveComboValues(control, value) elif isinstance(control, QDoubleSpinBox) or isinstance(control, QSpinBox): value = control.value() elif isinstance(control, QDateTimeEdit): value = control.dateTime().toString(Qt.ISODate) elif isinstance(control, QListWidget): item = control.currentItem() if item: value = item.text() else: return QString("") info("Setting value to %s from %s" % (value, control.objectName())) qgsfeature.changeAttribute(index, value) # Save the value to the database as a default if it is needed. if self.shouldSaveValue(control): self.saveDefault(control, value) else: self.removeDefault(control) return qgsfeature def pickDateTime(self, control, mode): """ Open the date time picker dialog control - The control that will recive the user set date time. """ dlg = DateTimePickerDialog(mode) dlg.setWindowTitle("Select a date") dlg.setDateTime(control.dateTime()) if dlg.exec_(): if hasattr(control, "setDate"): control.setDate(dlg.getSelectedDate()) if hasattr(control, "setTime"): control.setTime(dlg.getSelectedTime()) def shouldSaveValue(self, control): try: button = self.getControl(control.objectName() + "_save", QToolButton) except ControlNotFound: log("_save button not found for %s" % control.objectName()) return log("_save button for %s is %s" % (control.objectName(), str(button.isChecked()))) return button.isChecked() def bindSaveValueButton(self, control, indefaults=False): try: button = self.getControl(control.objectName() + "_save", QToolButton) except ControlNotFound: return button.setCheckable(True) button.setIcon(QIcon(":/icons/save_default")) button.setIconSize(QSize(24, 24)) button.setChecked(indefaults) def removeDefault(self, control): name = control.objectName() query = QSqlQuery() query.prepare("DELETE FROM DefaultValues WHERE control = :control") query.bindValue(":control", name) query.exec_() def saveDefault(self, control, value): self.removeDefault(control) name = control.objectName() query = QSqlQuery() query.prepare("INSERT INTO DefaultValues (control, value)" "VALUES (:control,:value)") query.bindValue(":control", name) query.bindValue(":value", value) query.exec_() def bindSelectButtons(self): """ Binds all the buttons on the form that need a select from map action. """ tools = self.forminstance.findChildren(QToolButton, QRegExp(".*_mapselect")) log(tools) layers = {QString(l.name()): l for l in self.canvas.layers()} log(layers) for tool in tools: try: control = self.getControl(tool.objectName()[:-10]) except ControlNotFound as ex: warning(ex.message) tool.setEnabled(False) continue settings = tool.dynamicPropertyNames() if not "from_layer" in settings or not "using_column" in settings: warning("from_layer or using_column not found") tool.setEnabled(False) continue layer_name = tool.property("from_layer").toString() column_name = tool.property("using_column").toString() layer = None try: layer = layers[QString(layer_name)] except KeyError: warning("layer not found in list") tool.setEnabled(False) continue message = tool.property("message").toString() if message.isEmpty(): message = "Please select a feature in the map" radius, valid = tool.property("radius").toInt() if not valid: radius = 5 tool.pressed.connect( partial(self.selectFeatureClicked, layer, column_name, message, radius, control.objectName()) ) tool.setIcon(QIcon(":/icons/select")) tool.setIconSize(QSize(24, 24)) def selectFeatureClicked(self, layer, column, message, searchsize, bindto): """ Loads the select from map action tool. Switchs to the map to allow the user to select a feature. controlname - The control name when looking up in the settings for the button config. """ self.tool = SelectFeatureTool(self.canvas, layer, column, bindto, searchsize) self.tool.foundFeature.connect(self.bindHighlightedFeature) self.tool.setActive() self.beginSelectFeature.emit(message) def bindHighlightedFeature(self, feature, value, bindto): """ Binds the selected features value to a control. """ self.bindByName(bindto, value) self.endSelectFeature.emit()
class FormBinder(QObject): beginSelectFeature = pyqtSignal(str) endSelectFeature = pyqtSignal() """ Handles binding of values to and out of the form. TODO: This whole class is really messy and needs a good clean up. """ def __init__(self, layer, formInstance, canvas): QObject.__init__(self) self.layer = layer self.canvas = canvas self.forminstance = formInstance self.images = {} self.mandatory_group = MandatoryGroup() self.boundControls = [] def bindFeature(self, qgsfeature, mandatory_fields=True, editing=False): """ Binds a features values to the form. If the control has the mandatory property set then it will be added to the mandatory group. qgsfeature - A QgsFeature to bind the values from mandatory_fields - True if mandatory fields should be respected (default) """ self.feature = qgsfeature defaults = qmaplayer.getSavedValues(self.layer) for index in xrange(qgsfeature.fields().count()): value = qgsfeature[index] name = qgsfeature.fields()[index].name() try: control = self.getControl(name) except ControlNotFound as ex: warning(ex.message) continue if mandatory_fields: mandatory = control.property("mandatory") if mandatory: buddy = self.getBuddy(control) self.mandatory_group.addWidget(control, buddy) self.bindSaveValueButton(control, defaults, editingmode=editing) if not editing: try: value = defaults[name] except KeyError: pass try: self.bindValueToControl(control, value, index) except BindingError as er: warning(er.reason) self.createHelpLink(control) self.mandatory_group.validateAll() def createHelpLink(self, control): name = control.objectName() helpfile = qmaplayer.getHelpFile(self.layer, name) log(helpfile) log(name) if helpfile: label = self.getBuddy(control) if label is control: return if label is None: return text = '<a href="{}">{}<a>'.format(helpfile, label.text()) label.setText(text) label.linkActivated.connect(self.showHelp) def showHelp(self, url): dlg = HelpViewDialog() dlg.loadFile(url) dlg.exec_() def getBuddy(self, control): try: label = self.getControl(control.objectName() + "_label", control_type=QLabel) return label except ControlNotFound: return control def getControl(self, name, control_type=QWidget): control = self.forminstance.findChild(control_type, name) if control is None: raise ControlNotFound(name) return control def bindByName(self, controlname, value): """ Binds a value to a control based on the control name. controlname - Name of the control to bind value - QVariant holding the value. """ control = self.getControl(controlname) try: self.bindValueToControl(control, value) except BindingError as er: warning(er.reason) def bindValueToControl(self, control, value, index=0): """ Binds a control to the supplied value. control - QWidget based control that takes the new value value - A QVariant holding the value """ if isinstance(control, QDateTimeEdit): # Can be removed after http://hub.qgis.org/issues/7013 is fixed. if isinstance(value, QDateTime): control.setDateTime(value) else: control.setDateTime(QDateTime.fromString(value, Qt.ISODate)) try: button = self.getControl(control.objectName() + "_pick", QPushButton) button.setIcon(QIcon(":/icons/calender")) button.setText("Pick") button.setIconSize(QSize(24, 24)) log("{} : {}".format(control.objectName(), type(control) is QDateEdit)) if type(control) is QDateEdit: log("Type is QDateEdit") button.pressed.connect( partial(self.pickDateTime, control, "Date")) else: button.pressed.connect( partial(self.pickDateTime, control, "DateTime")) except ControlNotFound: pass self.boundControls.append(control) elif isinstance(control, QPushButton): if control.text() == "Drawing": control.setIcon(QIcon(":/icons/draw")) control.setIconSize(QSize(24, 24)) control.pressed.connect(partial(self.loadDrawingTool, control)) self.boundControls.append(control) elif hasattr(control, 'loadImage'): image = value control.loadImage(image) self.boundControls.append(control) else: if (isinstance(control, QComboBox) and self.layer.editType(index) == QgsVectorLayer.UniqueValuesEditable): for v in self.layer.dataProvider().uniqueValues(index): control.addItem(v, v) control.setEditText(value) self.boundControls.append(control) try: # Remove the validator because there seems to be a bug with the # MS SQL layers and validators. control.setValidator(None) except AttributeError: pass QgsAttributeEditor.setValue(control, self.layer, index, value) def unbindFeature(self, feature): """ Unbinds the feature from the form saving the values back to the QgsFeature. feature -- A QgsFeature that will store the new values. """ for control in self.boundControls: name = control.objectName() if isinstance(control, QDateTimeEdit): value = control.dateTime().toString(Qt.ISODate) try: feature[name] = value except KeyError as e: log(e) continue elif hasattr(control, 'getImage'): image = control.getImage() value = image try: feature[name] = value except KeyError: log("No field named " + name) pass return feature def saveValues(self, feature): tosave = {} for field, shouldsave in self._getSaveButtons(): log(field) log(shouldsave) if shouldsave: tosave[field] = feature[field] qmaplayer.setSavedValues(self.layer, tosave) def _getSaveButtons(self): buttons = self.forminstance.findChildren(QToolButton) for button in buttons: name = button.objectName() if name.endswith('_save'): yield name[:-5], button.isChecked() def loadDrawingTool(self, control): """ Load the drawing tool. control - The control (QWidget) who owns this drawing. Its name is used in the naming of the final image. """ raise NotImplementedError controlname = control.objectName() self.forminstance.hide() curdir = os.path.dirname(__file__) id = self.feature.attributeMap()[self.layer.fieldNameIndex("UniqueID")] savedname = str(id) + "_" + controlname + ".jpg" imagename = os.path.join(curdir, "data", self.layer.name(), "images", \ savedname) tempname = "drawingFor_{0}".format(controlname) tempimage = os.path.join(tempfile.gettempdir(), tempname) log("Looking for {0} or {1}".format(imagename, tempimage)) imagetoload = self.images.get(controlname, imagename) drawingpad = DrawingPad(imagetoload) drawingpad.setWindowState(Qt.WindowFullScreen | Qt.WindowActive) drawingpad.ui.actionMapSnapshot.triggered.connect( partial(self.drawingPadMapSnapshot, drawingpad)) if drawingpad.exec_(): #Save the image to a temporay location until commit. self.images[controlname] = tempimage + ".png" drawingpad.saveImage(tempimage) self.forminstance.show() else: self.forminstance.show() def drawingPadMapSnapshot(self, pad): """ Saves the current view of the map canvas to a image and it into the drawing pad. pad - The drawing pad that will take the final image. """ #TODO Refactor me!! image = QPixmap.fromImage(pad.scribbleArea.image) tempimage = os.path.join(tempfile.gettempdir(), "mapcanvascapture.png") self.canvas.saveAsImage(tempimage, image) pad.openImage(tempimage) def pickDateTime(self, control, mode): """ Open the date time picker dialog control - The control that will recive the user set date time. """ log(mode) dlg = DateTimePickerDialog(mode) dlg.setWindowTitle("Select a date") if control.dateTime() == QDateTime(2000, 1, 1, 00, 00, 00, 0): dlg.setAsNow() else: dlg.setDateTime(control.dateTime()) if dlg.exec_(): if hasattr(control, 'setDate'): control.setDate(dlg.getSelectedDate()) if hasattr(control, 'setTime'): control.setTime(dlg.getSelectedTime()) def shouldSaveValue(self, control): try: button = self.getControl(control.objectName() + "_save", QToolButton) except ControlNotFound: return return button.isChecked() def bindSaveValueButton(self, control, defaults, editingmode=False): name = control.objectName() try: button = self.getControl("{}_save".format(name), QToolButton) except ControlNotFound: return button.setCheckable(not editingmode) button.setIcon(QIcon(":/icons/save_default")) button.setIconSize(QSize(24, 24)) button.setChecked(name in defaults) button.setVisible(not editingmode) def bindSelectButtons(self): """ Binds all the buttons on the form that need a select from map action. """ tools = self.forminstance.findChildren(QToolButton, QRegExp('.*_mapselect')) layers = {l.name(): l for l in self.canvas.layers()} for tool in tools: try: control = self.getControl(tool.objectName()[:-10]) except ControlNotFound as ex: warning(ex.message) tool.setEnabled(False) continue settings = tool.dynamicPropertyNames() if not 'from_layer' in settings or not 'using_column' in settings: warning('from_layer or using_column not found') tool.setEnabled(False) continue layer_name = tool.property('from_layer') column_name = tool.property('using_column') layer = None try: layer = layers[layer_name] except KeyError: warning('layer not found in list') tool.setEnabled(False) continue message = tool.property('message') if message.isEmpty(): message = "Please select a feature in the map" radius, valid = tool.property('radius') if not valid: radius = 5 tool.pressed.connect( partial(self.selectFeatureClicked, layer, column_name, message, radius, control.objectName())) tool.setIcon(QIcon(":/icons/select")) tool.setIconSize(QSize(24, 24)) def selectFeatureClicked(self, layer, column, message, searchsize, bindto): """ Loads the select from map action tool. Switchs to the map to allow the user to select a feature. controlname - The control name when looking up in the settings for the button config. """ self.tool = SelectFeatureTool(self.canvas, layer, column, bindto, searchsize) self.tool.foundFeature.connect(self.bindHighlightedFeature) self.tool.setActive() self.beginSelectFeature.emit(message) def bindHighlightedFeature(self, feature, value, bindto): """ Binds the selected features value to a control. """ self.bindByName(bindto, value) self.endSelectFeature.emit()
class FormBinder(QObject): beginSelectFeature = pyqtSignal(str) endSelectFeature = pyqtSignal() """ Handles binding of values to and out of the form. TODO: This whole class is really messy and needs a good clean up. """ def __init__(self, layer, formInstance, canvas): QObject.__init__(self) self.layer = layer self.canvas = canvas self.forminstance = formInstance self.images = {} self.mandatory_group = MandatoryGroup() self.boundControls = [] def bindFeature(self, qgsfeature, mandatory_fields=True, editing=False): """ Binds a features values to the form. If the control has the mandatory property set then it will be added to the mandatory group. qgsfeature - A QgsFeature to bind the values from mandatory_fields - True if mandatory fields should be respected (default) """ self.feature = qgsfeature defaults = qmaplayer.getSavedValues(self.layer) for index in xrange(qgsfeature.fields().count()): value = qgsfeature[index] name = qgsfeature.fields()[index].name() try: control = self.getControl(name) except ControlNotFound as ex: warning(ex.message) continue if mandatory_fields: mandatory = control.property("mandatory") if mandatory: buddy = self.getBuddy(control) self.mandatory_group.addWidget(control, buddy) self.bindSaveValueButton(control, defaults, editingmode=editing) if not editing: try: value = defaults[name] except KeyError: pass try: self.bindValueToControl(control, value, index) except BindingError as er: warning(er.reason) self.createHelpLink(control) self.mandatory_group.validateAll() def createHelpLink(self, control): name = control.objectName() helpfile = qmaplayer.getHelpFile(self.layer, name) log(helpfile) log(name) if helpfile: label = self.getBuddy(control) if label is control: return if label is None: return text = '<a href="{}">{}<a>'.format(helpfile, label.text()) label.setText(text) label.linkActivated.connect(self.showHelp) def showHelp(self, url): dlg = HelpViewDialog() dlg.loadFile(url) dlg.exec_() def getBuddy(self, control): try: label = self.getControl(control.objectName() + "_label", control_type=QLabel) return label except ControlNotFound: return control def getControl(self, name, control_type=QWidget): control = self.forminstance.findChild(control_type, name) if control is None: raise ControlNotFound(name) return control def bindByName(self, controlname, value): """ Binds a value to a control based on the control name. controlname - Name of the control to bind value - QVariant holding the value. """ control = self.getControl(controlname) try: self.bindValueToControl(control, value) except BindingError as er: warning(er.reason) def bindValueToControl(self, control, value, index=0): """ Binds a control to the supplied value. control - QWidget based control that takes the new value value - A QVariant holding the value """ if isinstance(control, QDateTimeEdit): # Can be removed after http://hub.qgis.org/issues/7013 is fixed. if isinstance(value, QDateTime): control.setDateTime(value) else: control.setDateTime(QDateTime.fromString(value, Qt.ISODate)) try: button = self.getControl(control.objectName() + "_pick", QPushButton) button.setIcon(QIcon(":/icons/calender")) button.setText("Pick") button.setIconSize(QSize(24, 24)) log("{} : {}".format(control.objectName(), type(control) is QDateEdit)) if type(control) is QDateEdit: log("Type is QDateEdit") button.pressed.connect(partial(self.pickDateTime, control, "Date")) else: button.pressed.connect(partial(self.pickDateTime, control, "DateTime")) except ControlNotFound: pass self.boundControls.append(control) elif isinstance(control, QPushButton): if control.text() == "Drawing": control.setIcon(QIcon(":/icons/draw")) control.setIconSize(QSize(24, 24)) control.pressed.connect(partial(self.loadDrawingTool, control)) self.boundControls.append(control) elif hasattr(control, 'loadImage'): image = value control.loadImage(image) self.boundControls.append(control) else: if (isinstance(control, QComboBox) and self.layer.editType(index) == QgsVectorLayer.UniqueValuesEditable): for v in self.layer.dataProvider().uniqueValues(index): control.addItem(v, v) control.setEditText(value) self.boundControls.append(control) try: # Remove the validator because there seems to be a bug with the # MS SQL layers and validators. control.setValidator(None) except AttributeError: pass QgsAttributeEditor.setValue(control, self.layer, index, value) def unbindFeature(self, feature): """ Unbinds the feature from the form saving the values back to the QgsFeature. feature -- A QgsFeature that will store the new values. """ for control in self.boundControls: name = control.objectName() if isinstance(control, QDateTimeEdit): value = control.dateTime().toString(Qt.ISODate) try: feature[name] = value except KeyError as e: log(e) continue elif hasattr(control, 'getImage'): image = control.getImage() value = image try: feature[name] = value except KeyError: log("No field named " + name) pass return feature def saveValues(self, feature): tosave = {} for field, shouldsave in self._getSaveButtons(): log(field) log(shouldsave) if shouldsave: tosave[field] = feature[field] qmaplayer.setSavedValues(self.layer, tosave) def _getSaveButtons(self): buttons = self.forminstance.findChildren(QToolButton) for button in buttons: name = button.objectName() if name.endswith('_save'): yield name[:-5], button.isChecked() def loadDrawingTool(self, control): """ Load the drawing tool. control - The control (QWidget) who owns this drawing. Its name is used in the naming of the final image. """ raise NotImplementedError controlname = control.objectName() self.forminstance.hide() curdir = os.path.dirname(__file__) id = self.feature.attributeMap()[self.layer.fieldNameIndex("UniqueID")] savedname = str(id) + "_" + controlname + ".jpg" imagename = os.path.join(curdir, "data", self.layer.name(), "images", \ savedname) tempname = "drawingFor_{0}".format(controlname) tempimage = os.path.join(tempfile.gettempdir(), tempname) log("Looking for {0} or {1}".format(imagename, tempimage)) imagetoload = self.images.get(controlname, imagename) drawingpad = DrawingPad(imagetoload) drawingpad.setWindowState(Qt.WindowFullScreen | Qt.WindowActive) drawingpad.ui.actionMapSnapshot.triggered.connect(partial(self.drawingPadMapSnapshot, drawingpad)) if drawingpad.exec_(): #Save the image to a temporay location until commit. self.images[controlname] = tempimage + ".png" drawingpad.saveImage(tempimage) self.forminstance.show() else: self.forminstance.show() def drawingPadMapSnapshot(self, pad): """ Saves the current view of the map canvas to a image and it into the drawing pad. pad - The drawing pad that will take the final image. """ #TODO Refactor me!! image = QPixmap.fromImage(pad.scribbleArea.image) tempimage = os.path.join(tempfile.gettempdir(), "mapcanvascapture.png") self.canvas.saveAsImage(tempimage, image) pad.openImage(tempimage) def pickDateTime(self, control, mode): """ Open the date time picker dialog control - The control that will recive the user set date time. """ log(mode) dlg = DateTimePickerDialog(mode) dlg.setWindowTitle("Select a date") if control.dateTime() == QDateTime(2000, 1, 1, 00, 00, 00, 0): dlg.setAsNow() else: dlg.setDateTime(control.dateTime()) if dlg.exec_(): if hasattr(control, 'setDate'): control.setDate(dlg.getSelectedDate()) if hasattr(control, 'setTime'): control.setTime(dlg.getSelectedTime()) def shouldSaveValue(self, control): try: button = self.getControl(control.objectName() + "_save", QToolButton) except ControlNotFound: return return button.isChecked() def bindSaveValueButton(self, control, defaults, editingmode=False): name = control.objectName() try: button = self.getControl("{}_save".format(name), QToolButton) except ControlNotFound: return button.setCheckable(not editingmode) button.setIcon(QIcon(":/icons/save_default")) button.setIconSize(QSize(24, 24)) button.setChecked(name in defaults) button.setVisible(not editingmode) def bindSelectButtons(self): """ Binds all the buttons on the form that need a select from map action. """ tools = self.forminstance.findChildren(QToolButton, QRegExp('.*_mapselect')) layers = {l.name(): l for l in self.canvas.layers()} for tool in tools: try: control = self.getControl(tool.objectName()[:-10]) except ControlNotFound as ex: warning(ex.message) tool.setEnabled(False) continue settings = tool.dynamicPropertyNames() if not 'from_layer' in settings or not 'using_column' in settings: warning('from_layer or using_column not found') tool.setEnabled(False) continue layer_name = tool.property('from_layer') column_name = tool.property('using_column') layer = None try: layer = layers[layer_name] except KeyError: warning('layer not found in list') tool.setEnabled(False) continue message = tool.property('message') if message.isEmpty(): message = "Please select a feature in the map" radius, valid = tool.property('radius') if not valid: radius = 5 tool.pressed.connect(partial(self.selectFeatureClicked, layer, column_name, message, radius, control.objectName())) tool.setIcon(QIcon(":/icons/select")) tool.setIconSize(QSize(24, 24)) def selectFeatureClicked(self, layer, column, message, searchsize, bindto): """ Loads the select from map action tool. Switchs to the map to allow the user to select a feature. controlname - The control name when looking up in the settings for the button config. """ self.tool = SelectFeatureTool(self.canvas, layer, column, bindto, searchsize) self.tool.foundFeature.connect(self.bindHighlightedFeature) self.tool.setActive() self.beginSelectFeature.emit(message) def bindHighlightedFeature(self, feature, value, bindto): """ Binds the selected features value to a control. """ self.bindByName(bindto, value) self.endSelectFeature.emit()
class FormBinder(QObject): beginSelectFeature = pyqtSignal(str) endSelectFeature = pyqtSignal() """ Handles binding of values to and out of the form. """ def __init__(self, layer, formInstance, canvas, settings, form): QObject.__init__(self) self.layer = layer self.canvas = canvas self.forminstance = formInstance self.fields = self.layer.pendingFields() self.fieldtocontrol = {} self.actionlist = [] self.settings = settings self.images = {} self.mandatory_group = MandatoryGroup() self.form = form def bindFeature(self, qgsfeature, mandatory_fields=True, editing=False): """ Binds a features values to the form. If the control has the mandatory property set then it will be added to the mandatory group. qgsfeature - A QgsFeature to bind the values from mandatory_fields - True if mandatory fields should be respected (default) """ self.feature = qgsfeature defaults = self.form.getSavedValues() for index, value in qgsfeature.attributeMap().items(): name = str(self.fields[index].name()) try: control = self.getControl(name) except ControlNotFound as ex: warning(ex.message) continue if mandatory_fields: mandatory = control.property("mandatory").toBool() if mandatory: buddy = self.getBuddy(control) self.mandatory_group.addWidget(control, buddy) info("Binding %s to %s" % (control.objectName(), value.toString())) self.bindSaveValueButton(control, defaults, editingmode=editing) if not editing: try: value = defaults[name] except KeyError: pass try: self.bindValueToControl(control, value, index) except BindingError as er: warning(er.reason) self.createHelpLink(control) self.fieldtocontrol[index] = control def createHelpLink(self, control): name = control.objectName() helpfile = self.form.getHelpFile(name) if helpfile: label = self.getBuddy(control) if label is control: return if label is None: return text = '<a href="%s">%s<a>' % (helpfile, label.text()) label.setText(text) label.linkActivated.connect(self.showHelp) def showHelp(self, url): dlg = HelpViewDialog() dlg.loadFile(url) dlg.exec_() def getBuddy(self, control): try: label = self.getControl(control.objectName() + "_label", control_type=QLabel) return label except ControlNotFound: return control def getControl(self, name, control_type=QWidget): control = self.forminstance.findChild(control_type, name) if control is None: raise ControlNotFound(name) return control def bindByName(self, controlname, value): """ Binds a value to a control based on the control name. controlname - Name of the control to bind value - QVariant holding the value. """ control = self.getControl(controlname) try: self.bindValueToControl(control, value) except BindingError as er: warning(er.reason) def bindValueToControl(self, control, value, index=0): """ Binds a control to the supplied value. control - QWidget based control that takes the new value value - A QVariant holding the value """ if isinstance(control, QDateTimeEdit): # Can be removed after http://hub.qgis.org/issues/7013 is fixed. control.setDateTime(QDateTime.fromString(value.toString(), Qt.ISODate)) button = self.getControl(control.objectName() + "_pick", QPushButton) if not button: return button.setIcon(QIcon(":/icons/calender")) button.setText("Pick") button.setIconSize(QSize(24, 24)) button.pressed.connect(partial(self.pickDateTime, control, "DateTime")) elif isinstance(control, QPushButton): if control.text() == "Drawing": control.setIcon(QIcon(":/icons/draw")) control.setIconSize(QSize(24, 24)) control.pressed.connect(partial(self.loadDrawingTool, control)) else: if self.layer.editType(index) == QgsVectorLayer.UniqueValues: editable = control.isEditable() widget = QgsAttributeEditor.createAttributeEditor(self.forminstance, control, self.layer, index, value) wasset = QgsAttributeEditor.setValue(control, self.layer, index, value) log(widget) try: control.setValidator(None) except AttributeError: pass if self.layer.editType(index) == QgsVectorLayer.UniqueValues: # Set the control back to the editable state the form says it should be. # This is to work around http://hub.qgis.org/issues/7012 control.setEditable(editable) def unbindFeature(self, qgsfeature, editingmode=False): """ Unbinds the feature from the form saving the values back to the QgsFeature. qgsfeature -- A QgsFeature that will store the new values. """ savefields = [] for index, control in self.fieldtocontrol.items(): value = QVariant() if isinstance(control, QDateTimeEdit): value = control.dateTime().toString(Qt.ISODate) else: if self.layer.editType(index) == QgsVectorLayer.UniqueValues and control.isEditable(): # Due to http://hub.qgis.org/issues/7012 we can't have editable # comboxs using QgsAttributeEditor. If the value isn't in the # dataset already it will return null. Until that bug is fixed # we are just going to handle ourself. value = control.currentText() else: modified = QgsAttributeEditor.retrieveValue(control, self.layer, index, value) info("Setting value to %s from %s" % (value, control.objectName())) qgsfeature.changeAttribute(index, value) # Save the value to the database as a default if it is needed. if self.shouldSaveValue(control): savefields.append(index) if not editingmode: m = qgsfeature.attributeMap() fields_map = self.layer.pendingFields() attr = {str(fields_map[k].name()): str(v.toString()) for k, v in m.items() if k in savefields} self.form.setSavedValues(attr) return qgsfeature def loadDrawingTool(self, control): """ Load the drawing tool. control - The control (QWidget) who owns this drawing. Its name is used in the naming of the final image. """ controlname = str(control.objectName()) self.forminstance.hide() curdir = os.path.dirname(__file__) id = self.feature.attributeMap()[self.layer.fieldNameIndex("UniqueID")].toString() savedname = str(id) + "_" + controlname + ".jpg" imagename = os.path.join(curdir, "data", str(self.layer.name()), "images", savedname) tempname = "drawingFor_{0}".format(controlname) tempimage = os.path.join(tempfile.gettempdir(), tempname) log("Looking for {0} or {1}".format(imagename, tempimage)) imagetoload = self.images.get(controlname, imagename) drawingpad = DrawingPad(imagetoload) drawingpad.setWindowState(Qt.WindowFullScreen | Qt.WindowActive) drawingpad.ui.actionMapSnapshot.triggered.connect(partial(self.drawingPadMapSnapshot, drawingpad)) if drawingpad.exec_(): # Save the image to a temporay location until commit. self.images[controlname] = tempimage + ".png" drawingpad.saveImage(tempimage) self.forminstance.show() else: self.forminstance.show() def drawingPadMapSnapshot(self, pad): """ Saves the current view of the map canvas to a image and it into the drawing pad. pad - The drawing pad that will take the final image. """ # TODO Refactor me!! image = QPixmap.fromImage(pad.scribbleArea.image) tempimage = os.path.join(tempfile.gettempdir(), "mapcanvascapture.png") self.canvas.saveAsImage(tempimage, image) pad.openImage(tempimage) def pickDateTime(self, control, mode): """ Open the date time picker dialog control - The control that will recive the user set date time. """ dlg = DateTimePickerDialog(mode) dlg.setWindowTitle("Select a date") if control.dateTime() == QDateTime(2000, 1, 1, 00, 00, 00, 0): dlg.setAsNow() else: dlg.setDateTime(control.dateTime()) if dlg.exec_(): if hasattr(control, "setDate"): control.setDate(dlg.getSelectedDate()) if hasattr(control, "setTime"): control.setTime(dlg.getSelectedTime()) def shouldSaveValue(self, control): try: button = self.getControl(control.objectName() + "_save", QToolButton) except ControlNotFound: return return button.isChecked() def bindSaveValueButton(self, control, defaults, editingmode=False): name = str(control.objectName()) try: button = self.getControl(name + "_save", QToolButton) except ControlNotFound: return button.setCheckable(not editingmode) button.setIcon(QIcon(":/icons/save_default")) button.setIconSize(QSize(24, 24)) button.setChecked(name in defaults) button.setVisible(not editingmode) def bindSelectButtons(self): """ Binds all the buttons on the form that need a select from map action. """ tools = self.forminstance.findChildren(QToolButton, QRegExp(".*_mapselect")) log(tools) layers = {QString(l.name()): l for l in self.canvas.layers()} log(layers) for tool in tools: try: control = self.getControl(tool.objectName()[:-10]) except ControlNotFound as ex: warning(ex.message) tool.setEnabled(False) continue settings = tool.dynamicPropertyNames() if not "from_layer" in settings or not "using_column" in settings: warning("from_layer or using_column not found") tool.setEnabled(False) continue layer_name = tool.property("from_layer").toString() column_name = tool.property("using_column").toString() layer = None try: layer = layers[QString(layer_name)] except KeyError: warning("layer not found in list") tool.setEnabled(False) continue message = tool.property("message").toString() if message.isEmpty(): message = "Please select a feature in the map" radius, valid = tool.property("radius").toInt() if not valid: radius = 5 tool.pressed.connect( partial(self.selectFeatureClicked, layer, column_name, message, radius, control.objectName()) ) tool.setIcon(QIcon(":/icons/select")) tool.setIconSize(QSize(24, 24)) def selectFeatureClicked(self, layer, column, message, searchsize, bindto): """ Loads the select from map action tool. Switchs to the map to allow the user to select a feature. controlname - The control name when looking up in the settings for the button config. """ self.tool = SelectFeatureTool(self.canvas, layer, column, bindto, searchsize) self.tool.foundFeature.connect(self.bindHighlightedFeature) self.tool.setActive() self.beginSelectFeature.emit(message) def bindHighlightedFeature(self, feature, value, bindto): """ Binds the selected features value to a control. """ self.bindByName(bindto, value) self.endSelectFeature.emit()
class FormBinder(QObject): beginSelectFeature = pyqtSignal(str) endSelectFeature = pyqtSignal() """ Handles binding of values to and out of the form. TODO: This whole class is really messy and needs a good clean up. """ def __init__(self, layer, formInstance, canvas): QObject.__init__(self) self.layer = layer self.canvas = canvas self.forminstance = formInstance self.images = {} self.mandatory_group = MandatoryGroup() self.boundControls = [] def loadDrawingTool(self, control): """ Load the drawing tool. control - The control (QWidget) who owns this drawing. Its name is used in the naming of the final image. """ raise NotImplementedError controlname = control.objectName() self.forminstance.hide() curdir = os.path.dirname(__file__) id = self.feature.attributeMap()[self.layer.fieldNameIndex("UniqueID")] savedname = str(id) + "_" + controlname + ".jpg" imagename = os.path.join(curdir, "data", self.layer.name(), "images", \ savedname) tempname = "drawingFor_{0}".format(controlname) tempimage = os.path.join(tempfile.gettempdir(), tempname) log("Looking for {0} or {1}".format(imagename, tempimage)) imagetoload = self.images.get(controlname, imagename) drawingpad = DrawingPad(imagetoload) drawingpad.setWindowState(Qt.WindowFullScreen | Qt.WindowActive) drawingpad.ui.actionMapSnapshot.triggered.connect(partial(self.drawingPadMapSnapshot, drawingpad)) if drawingpad.exec_(): #Save the image to a temporay location until commit. self.images[controlname] = tempimage + ".png" drawingpad.saveImage(tempimage) self.forminstance.show() else: self.forminstance.show() def drawingPadMapSnapshot(self, pad): """ Saves the current view of the map canvas to a image and it into the drawing pad. pad - The drawing pad that will take the final image. """ #TODO Refactor me!! image = QPixmap.fromImage(pad.scribbleArea.image) tempimage = os.path.join(tempfile.gettempdir(), "mapcanvascapture.png") self.canvas.saveAsImage(tempimage, image) pad.openImage(tempimage) def bindSelectButtons(self): """ Binds all the buttons on the form that need a select from map action. """ tools = self.forminstance.findChildren(QToolButton, QRegExp('.*_mapselect')) layers = {l.name(): l for l in self.canvas.layers()} for tool in tools: try: control = self.getControl(tool.objectName()[:-10]) except ControlNotFound as ex: warning(ex.message) tool.setEnabled(False) continue settings = tool.dynamicPropertyNames() if not 'from_layer' in settings or not 'using_column' in settings: warning('from_layer or using_column not found') tool.setEnabled(False) continue layer_name = tool.property('from_layer') column_name = tool.property('using_column') layer = None try: layer = layers[layer_name] except KeyError: warning('layer not found in list') tool.setEnabled(False) continue message = tool.property('message') if message.isEmpty(): message = "Please select a feature in the map" radius, valid = tool.property('radius') if not valid: radius = 5 tool.pressed.connect(partial(self.selectFeatureClicked, layer, column_name, message, radius, control.objectName())) tool.setIcon(QIcon(":/icons/select")) tool.setIconSize(QSize(24, 24)) def selectFeatureClicked(self, layer, column, message, searchsize, bindto): """ Loads the select from map action tool. Switchs to the map to allow the user to select a feature. controlname - The control name when looking up in the settings for the button config. """ self.tool = SelectFeatureTool(self.canvas, layer, column, bindto, searchsize) self.tool.foundFeature.connect(self.bindHighlightedFeature) self.tool.setActive() self.beginSelectFeature.emit(message) def bindHighlightedFeature(self, feature, value, bindto): """ Binds the selected features value to a control. """ self.bindByName(bindto, value) self.endSelectFeature.emit()