def __init__(self, iface): QMainWindow.__init__(self) # Set up the user interface from Designer. self.ui = Ui_FieldPyculatorDialog() self.ui.setupUi(self) active_layer = iface.activeLayer() self.iface = iface self.active_layer_id = active_layer.id() self._code_from_file = None self._is_dirty = False #restore settings rest_font_size = self.load_font_size(self.ui.txtFieldExp.get_font_size()) self.ui.txtFieldExp.set_font_size(rest_font_size) self.ui.txtGlobalExp.set_font_size(rest_font_size) #INIT CONTROLS VALUES self.ui.lblLayerName.setText(active_layer.name()) self.ui.cmbUpdateField.addItems(self.get_field_names(active_layer)) self.ui.lstFields.addItems(self.get_field_names(active_layer)) self.ui.txtGlobalExp.hide() self.ui.txtFieldExp.insertPlainText(self.RESULT_VAR_NAME + ' = ') #setup auto focus self.ui.lstFields.setFocusProxy(self.ui.txtFieldExp) self.ui.lstValues.setFocusProxy(self.ui.txtFieldExp) self.ui.btnId.setFocusProxy(self.ui.txtFieldExp) self.ui.btnGeom.setFocusProxy(self.ui.txtFieldExp) #setup icons self.init_toolbox() self.ui.btnRun.setIcon(QgsApplication.getThemeIcon('console/iconRunScriptConsole.png')) #SIGNALS QObject.connect(self.ui.lstFields, SIGNAL('currentItemChanged ( QListWidgetItem * , QListWidgetItem * )'), self.update_field_sample_values) QObject.connect(self.ui.lstFields, SIGNAL('itemDoubleClicked(QListWidgetItem *)'), self.add_field_to_expression) QObject.connect(self.ui.lstValues, SIGNAL('itemDoubleClicked(QListWidgetItem *)'), self.add_value_to_expression) QObject.connect(self.ui.btnGetAll, SIGNAL('clicked()'), self.update_field_all_values) QObject.connect(self.ui.btnId, SIGNAL('clicked()'), self.add_id_to_expression) QObject.connect(self.ui.btnGeom, SIGNAL('clicked()'), self.add_geom_to_expression) QObject.connect(self.ui.btnRun, SIGNAL('clicked()'), self.processing) QObject.connect(self.ui.txtFieldExp, SIGNAL('wheelEvent(QWheelEvent)'), self.editorWheelEvent) QObject.connect(self.ui.txtGlobalExp, SIGNAL('wheelEvent(QWheelEvent)'), self.editorWheelEvent) QObject.connect(self.ui.action_open, SIGNAL('triggered(bool)'), self.action_open_handler) QObject.connect(self.ui.action_save, SIGNAL('triggered(bool)'), self.action_save_handler) QObject.connect(self.ui.action_save_as, SIGNAL('triggered(bool)'), self.action_save_as_handler) QObject.connect(self.ui.txtGlobalExp, SIGNAL('textChanged()'), self.set_dirty_flag) QObject.connect(self.ui.txtFieldExp, SIGNAL('textChanged()'), self.set_dirty_flag)
def __init__(self, iface): QDialog.__init__(self) # Set up the user interface from Designer. self.ui = Ui_FieldPyculatorDialog() self.ui.setupUi(self) self.iface = iface self.active_layer = self.iface.activeLayer() self.data_provider = self.active_layer.dataProvider() #INIT CONTROLS VALUES self.ui.lblLayerName.setText(self.active_layer.name()) self.ui.cmbUpdateField.addItems(self.get_field_names(self.active_layer)) self.ui.lstFields.addItems(self.get_field_names(self.active_layer)) self.ui.txtGlobalExp.hide() self.ui.txtFieldExp.insertPlainText(self.RESULT_VAR_NAME + ' = ') #setup actions self.ui.btnToggleEditing.setDefaultAction(self.iface.actionToggleEditing()) #setup syntax highlight self.highlight_field = PythonHighlighter(self.ui.txtFieldExp.document()) self.highlight_global = PythonHighlighter(self.ui.txtGlobalExp.document()) #setup auto focus self.ui.lstFields.setFocusProxy(self.ui.txtFieldExp) self.ui.lstValues.setFocusProxy(self.ui.txtFieldExp) self.ui.btnId.setFocusProxy(self.ui.txtFieldExp) self.ui.btnGeom.setFocusProxy(self.ui.txtFieldExp) #SIGNALS QObject.connect( self.ui.lstFields, SIGNAL( "currentItemChanged ( QListWidgetItem * , QListWidgetItem * )" ), self.update_field_sample_values) QObject.connect( self.ui.lstFields, SIGNAL( "itemDoubleClicked(QListWidgetItem *)" ), self.add_field_to_expression) QObject.connect( self.ui.lstValues, SIGNAL( "itemDoubleClicked(QListWidgetItem *)" ), self.add_value_to_expression) QObject.connect( self.ui.btnGetAll, SIGNAL( " clicked()" ), self.update_field_all_values) QObject.connect( self.ui.btnId, SIGNAL( " clicked()" ), self.add_id_to_explession) QObject.connect( self.ui.btnGeom, SIGNAL( " clicked()" ), self.add_geom_to_explession) QObject.connect( self.ui.btnRun, SIGNAL( " clicked()" ), self.processing)
class FieldPyculatorDialog(QMainWindow): RESULT_VAR_NAME = 'value' _filter = 'Pyculator files (*.pycl);; Python files (*.py);; All files (*)' def __init__(self, iface): QMainWindow.__init__(self) # Set up the user interface from Designer. self.ui = Ui_FieldPyculatorDialog() self.ui.setupUi(self) active_layer = iface.activeLayer() self.iface = iface self.active_layer_id = active_layer.id() self._code_from_file = None self._is_dirty = False #restore settings rest_font_size = self.load_font_size(self.ui.txtFieldExp.get_font_size()) self.ui.txtFieldExp.set_font_size(rest_font_size) self.ui.txtGlobalExp.set_font_size(rest_font_size) #INIT CONTROLS VALUES self.ui.lblLayerName.setText(active_layer.name()) self.ui.cmbUpdateField.addItems(self.get_field_names(active_layer)) self.ui.lstFields.addItems(self.get_field_names(active_layer)) self.ui.txtGlobalExp.hide() self.ui.txtFieldExp.insertPlainText(self.RESULT_VAR_NAME + ' = ') #setup auto focus self.ui.lstFields.setFocusProxy(self.ui.txtFieldExp) self.ui.lstValues.setFocusProxy(self.ui.txtFieldExp) self.ui.btnId.setFocusProxy(self.ui.txtFieldExp) self.ui.btnGeom.setFocusProxy(self.ui.txtFieldExp) #setup icons self.init_toolbox() self.ui.btnRun.setIcon(QgsApplication.getThemeIcon('console/iconRunScriptConsole.png')) #SIGNALS QObject.connect(self.ui.lstFields, SIGNAL('currentItemChanged ( QListWidgetItem * , QListWidgetItem * )'), self.update_field_sample_values) QObject.connect(self.ui.lstFields, SIGNAL('itemDoubleClicked(QListWidgetItem *)'), self.add_field_to_expression) QObject.connect(self.ui.lstValues, SIGNAL('itemDoubleClicked(QListWidgetItem *)'), self.add_value_to_expression) QObject.connect(self.ui.btnGetAll, SIGNAL('clicked()'), self.update_field_all_values) QObject.connect(self.ui.btnId, SIGNAL('clicked()'), self.add_id_to_expression) QObject.connect(self.ui.btnGeom, SIGNAL('clicked()'), self.add_geom_to_expression) QObject.connect(self.ui.btnRun, SIGNAL('clicked()'), self.processing) QObject.connect(self.ui.txtFieldExp, SIGNAL('wheelEvent(QWheelEvent)'), self.editorWheelEvent) QObject.connect(self.ui.txtGlobalExp, SIGNAL('wheelEvent(QWheelEvent)'), self.editorWheelEvent) QObject.connect(self.ui.action_open, SIGNAL('triggered(bool)'), self.action_open_handler) QObject.connect(self.ui.action_save, SIGNAL('triggered(bool)'), self.action_save_handler) QObject.connect(self.ui.action_save_as, SIGNAL('triggered(bool)'), self.action_save_as_handler) QObject.connect(self.ui.txtGlobalExp, SIGNAL('textChanged()'), self.set_dirty_flag) QObject.connect(self.ui.txtFieldExp, SIGNAL('textChanged()'), self.set_dirty_flag) #-------------- GUI INIT def init_toolbox(self): icon = QgsApplication.getThemeIcon('console/iconOpenConsole.png') or QgsApplication.getThemeIcon('/mActionFileOpen.svg') self.ui.action_open.setIcon(icon) icon = QgsApplication.getThemeIcon('console/iconSaveConsole.png') or QgsApplication.getThemeIcon('/mActionFileSave.svg') self.ui.action_save.setIcon(icon) icon = QgsApplication.getThemeIcon('console/iconSaveAsConsole.png') or QgsApplication.getThemeIcon('/mActionFileSaveAs.svg') self.ui.action_save_as.setIcon(icon) self.update_btn_status() #--------------- def editorWheelEvent(self, event): if event.modifiers() == Qt.ControlModifier: delta = event.delta()/100 font_size = self.ui.txtFieldExp.get_font_size() new_font_size = font_size + delta if 5 < (new_font_size) < 20: self.ui.txtFieldExp.set_font_size(new_font_size) self.ui.txtGlobalExp.set_font_size(new_font_size) self.save_font_size(new_font_size) def save_font_size(self, new_font_size): sett = QSettings() sett.setValue('field_pyculator/font_size', new_font_size) def load_font_size(self, def_value): sett = QSettings() return sett.value('field_pyculator/font_size', def_value, type=int) def get_active_layer(self): #get and check layer active_layer = QgsMapLayerRegistry.instance().mapLayer(self.active_layer_id) if not active_layer: QMessageBox.critical(self, self.tr('FieldPyculator error'), self.tr('Layer was not found! It could be removed!')) return None else: return active_layer #--------------- Open/Save handlers ----------------------- def action_open_handler(self): #TODO: Add _is_dirty flag check file_name = QFileDialog.getOpenFileName(self, self.tr('Open file with code...'), filter=self._filter) if file_name: if path.exists(file_name): with codecs.open(file_name, 'r', encoding='utf-8') as content_file: content = content_file.read() glob_code, local_code = CodeComposer.decompose(content) self.ui.txtGlobalExp.setText(glob_code) self.ui.txtFieldExp.setText(local_code) if glob_code: self.ui.grpGlobalExpression.setChecked(True) else: self.ui.grpGlobalExpression.setChecked(False) self._code_from_file = file_name self._is_dirty = False self.update_btn_status() else: QMessageBox.warning(self, self.tr('FieldPyculator warning'), self.tr('No such file: ') + file_name) def action_save_handler(self): if self._code_from_file and self._is_dirty: content = CodeComposer.compose(self.ui.txtGlobalExp.toPlainText(), self.ui.txtFieldExp.toPlainText()) with codecs.open(self._code_from_file, 'w', encoding='utf-8') as content_file: content_file.write(content) self._is_dirty = False self.update_btn_status() def action_save_as_handler(self): file_name = QFileDialog.getSaveFileName(self, self.tr('Save code to file...'), filter=self._filter) if file_name: content = CodeComposer.compose(self.ui.txtGlobalExp.toPlainText(), self.ui.txtFieldExp.toPlainText()) with codecs.open(file_name, 'w', encoding='utf-8') as content_file: content_file.write(content) self._code_from_file = file_name self._is_dirty = False self.update_btn_status() def update_btn_status(self): self.ui.action_save.setEnabled(self._code_from_file is not None and self._is_dirty) def set_dirty_flag(self): self._is_dirty = True self.update_btn_status() #--------------- Fields handlers --------------------------- def update_field_sample_values(self, new_item, old_item): field_name = new_item.text() self.update_field_values(field_name, 25) def update_field_all_values(self): if self.ui.lstFields.currentItem(): field_name = self.ui.lstFields.currentItem().text() self.update_field_values(field_name) def update_field_values(self, field_name, limit=-1): active_layer = self.get_active_layer() if not active_layer: return data_provider = active_layer.dataProvider() self.setCursor(Qt.WaitCursor) field_ind = data_provider.fieldNameIndex(field_name) field_type = data_provider.fields()[field_ind].typeName() self.ui.lstValues.clear() values = data_provider.uniqueValues(field_ind, limit) for val in values: new_item = QListWidgetItem() if field_type in ('String', 'Date'): new_item.setText("'" + unicode(val) + "'") new_item.setData(Qt.UserRole, "u'" + unicode(val) + "'") else: #TODO: is too long!!! new_item.setText(unicode(val)) new_item.setData(Qt.UserRole, unicode(val)) self.ui.lstValues.addItem(new_item) self.unsetCursor() def add_field_to_expression(self, item): field_name = item.text() self.ui.txtFieldExp.insertPlainText(' <'+field_name+'> ') def add_value_to_expression(self, item): value = item.data(Qt.UserRole) self.ui.txtFieldExp.insertPlainText(' '+value+' ') #------------- Vars handlers --------------------------------- def add_id_to_expression(self): self.ui.txtFieldExp.insertPlainText(' $id ') def add_geom_to_expression(self): self.ui.txtFieldExp.insertPlainText(' $geom ') #-------------------------------------------------------------- def processing(self): #get and check layer active_layer = self.get_active_layer() if not active_layer: # may be need to disable form? return data_provider = active_layer.dataProvider() #check edit mode if not active_layer.isEditable(): QMessageBox.warning(self, self.tr('FieldPyculator warning'), self.tr('Layer is not in edit mode! Please start editing the layer!')) return start = datetime.datetime.now() new_ns = {} #run global code if self.ui.grpGlobalExpression.isChecked(): try: code = unicode(self.ui.txtGlobalExp.toPlainText()) bytecode = compile(code, '<string>', 'exec') exec bytecode in new_ns except: QMessageBox.critical(self, self.tr('FieldPyculator code execute error'), (self.tr('Global code block can\'t be executed!\n{0}: {1}')) .format(unicode(sys.exc_info()[0].__name__), unicode(sys.exc_info()[1]))) return code = unicode(self.ui.txtFieldExp.toPlainText()) #TODO: check 'result' existing in text of code???!!! #replace all fields tags field_map = data_provider.fields() for field in field_map: field_name = unicode(field.name()) replval = '__attr[\'' + field_name + '\']' code = code.replace('<' + field_name + '>', replval) #replace all special vars code = code.replace('$id', '__id') code = code.replace('$geom', '__geom') #is it need: $area, $length, $x, $y???? #print code #debug #search needed vars (hmmm... comments?!) need_id = code.find('__id') != -1 need_geom = code.find('__geom') != -1 need_attrs = code.find('__attr') != -1 #compile try: bytecode = compile(code, '<string>', 'exec') except: QMessageBox.critical(self, self.tr('FieldPyculator code execute error'), (self.tr('Field code block can\'t be executed!\n{0}: {1}')) .format(unicode(sys.exc_info()[0].__name__), unicode(sys.exc_info()[1]))) return #get num of updating field field_num = data_provider.fieldNameIndex(self.ui.cmbUpdateField.currentText()) #setup progress bar self.ui.prgTotal.setValue(0) #run if not self.ui.chkOnlySelected.isChecked(): features_for_update = data_provider.featureCount() request = QgsFeatureRequest() if not need_geom: request.setFlags(QgsFeatureRequest.NoGeometry) if need_attrs: request.setSubsetOfAttributes(data_provider.attributeIndexes()) else: request.setSubsetOfAttributes([]) features = active_layer.getFeatures(request) else: features_for_update = active_layer.selectedFeatureCount() features = active_layer.selectedFeatures() if features_for_update > 0: self.ui.prgTotal.setMaximum(features_for_update) for feat in features: feat_id = feat.id() #add needed vars if need_id: new_ns['__id'] = feat_id if need_geom: geom = feat.geometry() new_ns['__geom'] = geom if need_attrs: fields = feat.fields() attr = {} for a in fields: attr[a.name()] = feat[a.name()] new_ns['__attr'] = attr #clear old result if self.RESULT_VAR_NAME in new_ns: del new_ns[self.RESULT_VAR_NAME] #exec try: exec bytecode in new_ns except: QMessageBox.critical(self, self.tr('FieldPyculator code execute error'), self.tr('Field code block can\'t be executed for feature {2}!\n{0}: {1}') .format(unicode(sys.exc_info()[0].__name__), unicode(sys.exc_info()[1]), unicode(feat_id))) return #check result if not self.RESULT_VAR_NAME in new_ns: QMessageBox.critical(self, self.tr('FieldPyculator code execute error'), self.tr('Field code block does not return \'{0}\' variable! Please declare this variable in your code!') .format(self.RESULT_VAR_NAME)) return #try assign try: active_layer.changeAttributeValue(feat_id, field_num, new_ns[self.RESULT_VAR_NAME]) except: QMessageBox.critical(self, self.tr('FieldPyculator code execute error'), self.tr('Result value can\'t be assigned to the feature {2}!\n{0}: {1}') .format(unicode(sys.exc_info()[0].__name__), unicode(sys.exc_info()[1]), unicode(feat_id))) return self.ui.prgTotal.setValue(self.ui.prgTotal.value()+1) stop = datetime.datetime.now() #workaround for python < 2.7 td = stop - start if sys.version_info[:2] < (2, 7): total_sec = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 else: total_sec = td.total_seconds() QMessageBox.information(self, self.tr('FieldPyculator code executed successfully'), (self.tr('Updated {0} features for {1} seconds')) .format(unicode(features_for_update), unicode(total_sec))) @staticmethod def get_field_names(layer): field_map = layer.dataProvider().fields() field_list = [] for field in field_map: field_list.append(unicode(field.name())) return field_list # sorted( field_list, cmp=locale.strcoll )
def __init__(self, iface): QMainWindow.__init__(self) # Set up the user interface from Designer. self.ui = Ui_FieldPyculatorDialog() self.ui.setupUi(self) active_layer = iface.activeLayer() self.iface = iface self.active_layer_id = active_layer.id() self._code_from_file = None self._is_dirty = False #restore settings rest_font_size = self.load_font_size( self.ui.txtFieldExp.get_font_size()) self.ui.txtFieldExp.set_font_size(rest_font_size) self.ui.txtGlobalExp.set_font_size(rest_font_size) #INIT CONTROLS VALUES self.ui.lblLayerName.setText(active_layer.name()) self.ui.cmbUpdateField.addItems(self.get_field_names(active_layer)) self.ui.lstFields.addItems(self.get_field_names(active_layer)) self.ui.txtGlobalExp.hide() self.ui.txtFieldExp.insertPlainText(self.RESULT_VAR_NAME + ' = ') #setup auto focus self.ui.lstFields.setFocusProxy(self.ui.txtFieldExp) self.ui.lstValues.setFocusProxy(self.ui.txtFieldExp) self.ui.btnId.setFocusProxy(self.ui.txtFieldExp) self.ui.btnGeom.setFocusProxy(self.ui.txtFieldExp) #setup icons self.init_toolbox() self.ui.btnRun.setIcon( QgsApplication.getThemeIcon('console/iconRunScriptConsole.png')) #SIGNALS QObject.connect( self.ui.lstFields, SIGNAL( 'currentItemChanged ( QListWidgetItem * , QListWidgetItem * )' ), self.update_field_sample_values) QObject.connect(self.ui.lstFields, SIGNAL('itemDoubleClicked(QListWidgetItem *)'), self.add_field_to_expression) QObject.connect(self.ui.lstValues, SIGNAL('itemDoubleClicked(QListWidgetItem *)'), self.add_value_to_expression) QObject.connect(self.ui.btnGetAll, SIGNAL('clicked()'), self.update_field_all_values) QObject.connect(self.ui.btnId, SIGNAL('clicked()'), self.add_id_to_expression) QObject.connect(self.ui.btnGeom, SIGNAL('clicked()'), self.add_geom_to_expression) QObject.connect(self.ui.btnRun, SIGNAL('clicked()'), self.processing) QObject.connect(self.ui.txtFieldExp, SIGNAL('wheelEvent(QWheelEvent)'), self.editorWheelEvent) QObject.connect(self.ui.txtGlobalExp, SIGNAL('wheelEvent(QWheelEvent)'), self.editorWheelEvent) QObject.connect(self.ui.action_open, SIGNAL('triggered(bool)'), self.action_open_handler) QObject.connect(self.ui.action_save, SIGNAL('triggered(bool)'), self.action_save_handler) QObject.connect(self.ui.action_save_as, SIGNAL('triggered(bool)'), self.action_save_as_handler) QObject.connect(self.ui.txtGlobalExp, SIGNAL('textChanged()'), self.set_dirty_flag) QObject.connect(self.ui.txtFieldExp, SIGNAL('textChanged()'), self.set_dirty_flag)
class FieldPyculatorDialog(QMainWindow): RESULT_VAR_NAME = 'value' _filter = 'Pyculator files (*.pycl);; Python files (*.py);; All files (*)' def __init__(self, iface): QMainWindow.__init__(self) # Set up the user interface from Designer. self.ui = Ui_FieldPyculatorDialog() self.ui.setupUi(self) active_layer = iface.activeLayer() self.iface = iface self.active_layer_id = active_layer.id() self._code_from_file = None self._is_dirty = False #restore settings rest_font_size = self.load_font_size( self.ui.txtFieldExp.get_font_size()) self.ui.txtFieldExp.set_font_size(rest_font_size) self.ui.txtGlobalExp.set_font_size(rest_font_size) #INIT CONTROLS VALUES self.ui.lblLayerName.setText(active_layer.name()) self.ui.cmbUpdateField.addItems(self.get_field_names(active_layer)) self.ui.lstFields.addItems(self.get_field_names(active_layer)) self.ui.txtGlobalExp.hide() self.ui.txtFieldExp.insertPlainText(self.RESULT_VAR_NAME + ' = ') #setup auto focus self.ui.lstFields.setFocusProxy(self.ui.txtFieldExp) self.ui.lstValues.setFocusProxy(self.ui.txtFieldExp) self.ui.btnId.setFocusProxy(self.ui.txtFieldExp) self.ui.btnGeom.setFocusProxy(self.ui.txtFieldExp) #setup icons self.init_toolbox() self.ui.btnRun.setIcon( QgsApplication.getThemeIcon('console/iconRunScriptConsole.png')) #SIGNALS QObject.connect( self.ui.lstFields, SIGNAL( 'currentItemChanged ( QListWidgetItem * , QListWidgetItem * )' ), self.update_field_sample_values) QObject.connect(self.ui.lstFields, SIGNAL('itemDoubleClicked(QListWidgetItem *)'), self.add_field_to_expression) QObject.connect(self.ui.lstValues, SIGNAL('itemDoubleClicked(QListWidgetItem *)'), self.add_value_to_expression) QObject.connect(self.ui.btnGetAll, SIGNAL('clicked()'), self.update_field_all_values) QObject.connect(self.ui.btnId, SIGNAL('clicked()'), self.add_id_to_expression) QObject.connect(self.ui.btnGeom, SIGNAL('clicked()'), self.add_geom_to_expression) QObject.connect(self.ui.btnRun, SIGNAL('clicked()'), self.processing) QObject.connect(self.ui.txtFieldExp, SIGNAL('wheelEvent(QWheelEvent)'), self.editorWheelEvent) QObject.connect(self.ui.txtGlobalExp, SIGNAL('wheelEvent(QWheelEvent)'), self.editorWheelEvent) QObject.connect(self.ui.action_open, SIGNAL('triggered(bool)'), self.action_open_handler) QObject.connect(self.ui.action_save, SIGNAL('triggered(bool)'), self.action_save_handler) QObject.connect(self.ui.action_save_as, SIGNAL('triggered(bool)'), self.action_save_as_handler) QObject.connect(self.ui.txtGlobalExp, SIGNAL('textChanged()'), self.set_dirty_flag) QObject.connect(self.ui.txtFieldExp, SIGNAL('textChanged()'), self.set_dirty_flag) #-------------- GUI INIT def init_toolbox(self): icon = QgsApplication.getThemeIcon( 'console/iconOpenConsole.png') or QgsApplication.getThemeIcon( '/mActionFileOpen.svg') self.ui.action_open.setIcon(icon) icon = QgsApplication.getThemeIcon( 'console/iconSaveConsole.png') or QgsApplication.getThemeIcon( '/mActionFileSave.svg') self.ui.action_save.setIcon(icon) icon = QgsApplication.getThemeIcon( 'console/iconSaveAsConsole.png') or QgsApplication.getThemeIcon( '/mActionFileSaveAs.svg') self.ui.action_save_as.setIcon(icon) self.update_btn_status() #--------------- def editorWheelEvent(self, event): if event.modifiers() == Qt.ControlModifier: delta = event.delta() / 100 font_size = self.ui.txtFieldExp.get_font_size() new_font_size = font_size + delta if 5 < (new_font_size) < 20: self.ui.txtFieldExp.set_font_size(new_font_size) self.ui.txtGlobalExp.set_font_size(new_font_size) self.save_font_size(new_font_size) def save_font_size(self, new_font_size): sett = QSettings() sett.setValue('field_pyculator/font_size', new_font_size) def load_font_size(self, def_value): sett = QSettings() return sett.value('field_pyculator/font_size', def_value, type=int) def get_active_layer(self): #get and check layer active_layer = QgsMapLayerRegistry.instance().mapLayer( self.active_layer_id) if not active_layer: QMessageBox.critical( self, self.tr('FieldPyculator error'), self.tr('Layer was not found! It could be removed!')) return None else: return active_layer #--------------- Open/Save handlers ----------------------- def action_open_handler(self): #TODO: Add _is_dirty flag check file_name = QFileDialog.getOpenFileName( self, self.tr('Open file with code...'), filter=self._filter) if file_name: if path.exists(file_name): with codecs.open(file_name, 'r', encoding='utf-8') as content_file: content = content_file.read() glob_code, local_code = CodeComposer.decompose(content) self.ui.txtGlobalExp.setText(glob_code) self.ui.txtFieldExp.setText(local_code) if glob_code: self.ui.grpGlobalExpression.setChecked(True) else: self.ui.grpGlobalExpression.setChecked(False) self._code_from_file = file_name self._is_dirty = False self.update_btn_status() else: QMessageBox.warning(self, self.tr('FieldPyculator warning'), self.tr('No such file: ') + file_name) def action_save_handler(self): if self._code_from_file and self._is_dirty: content = CodeComposer.compose(self.ui.txtGlobalExp.toPlainText(), self.ui.txtFieldExp.toPlainText()) with codecs.open(self._code_from_file, 'w', encoding='utf-8') as content_file: content_file.write(content) self._is_dirty = False self.update_btn_status() def action_save_as_handler(self): file_name = QFileDialog.getSaveFileName( self, self.tr('Save code to file...'), filter=self._filter) if file_name: content = CodeComposer.compose(self.ui.txtGlobalExp.toPlainText(), self.ui.txtFieldExp.toPlainText()) with codecs.open(file_name, 'w', encoding='utf-8') as content_file: content_file.write(content) self._code_from_file = file_name self._is_dirty = False self.update_btn_status() def update_btn_status(self): self.ui.action_save.setEnabled(self._code_from_file is not None and self._is_dirty) def set_dirty_flag(self): self._is_dirty = True self.update_btn_status() #--------------- Fields handlers --------------------------- def update_field_sample_values(self, new_item, old_item): field_name = new_item.text() self.update_field_values(field_name, 25) def update_field_all_values(self): if self.ui.lstFields.currentItem(): field_name = self.ui.lstFields.currentItem().text() self.update_field_values(field_name) def update_field_values(self, field_name, limit=-1): active_layer = self.get_active_layer() if not active_layer: return data_provider = active_layer.dataProvider() self.setCursor(Qt.WaitCursor) field_ind = data_provider.fieldNameIndex(field_name) field_type = data_provider.fields()[field_ind].typeName() self.ui.lstValues.clear() values = data_provider.uniqueValues(field_ind, limit) for val in values: new_item = QListWidgetItem() if field_type in ('String', 'Date'): new_item.setText("'" + unicode(val) + "'") new_item.setData(Qt.UserRole, "u'" + unicode(val) + "'") else: #TODO: is too long!!! new_item.setText(unicode(val)) new_item.setData(Qt.UserRole, unicode(val)) self.ui.lstValues.addItem(new_item) self.unsetCursor() def add_field_to_expression(self, item): field_name = item.text() self.ui.txtFieldExp.insertPlainText(' <' + field_name + '> ') def add_value_to_expression(self, item): value = item.data(Qt.UserRole) self.ui.txtFieldExp.insertPlainText(' ' + value + ' ') #------------- Vars handlers --------------------------------- def add_id_to_expression(self): self.ui.txtFieldExp.insertPlainText(' $id ') def add_geom_to_expression(self): self.ui.txtFieldExp.insertPlainText(' $geom ') #-------------------------------------------------------------- def processing(self): #get and check layer active_layer = self.get_active_layer() if not active_layer: # may be need to disable form? return data_provider = active_layer.dataProvider() #check edit mode if not active_layer.isEditable(): QMessageBox.warning( self, self.tr('FieldPyculator warning'), self. tr('Layer is not in edit mode! Please start editing the layer!' )) return start = datetime.datetime.now() new_ns = {} #run global code if self.ui.grpGlobalExpression.isChecked(): try: code = unicode(self.ui.txtGlobalExp.toPlainText()) bytecode = compile(code, '<string>', 'exec') exec bytecode in new_ns except: QMessageBox.critical( self, self.tr('FieldPyculator code execute error'), (self.tr('Global code block can\'t be executed!\n{0}: {1}') ).format(unicode(sys.exc_info()[0].__name__), unicode(sys.exc_info()[1]))) return code = unicode(self.ui.txtFieldExp.toPlainText()) #TODO: check 'result' existing in text of code???!!! #replace all fields tags field_map = data_provider.fields() for field in field_map: field_name = unicode(field.name()) replval = '__attr[\'' + field_name + '\']' code = code.replace('<' + field_name + '>', replval) #replace all special vars code = code.replace('$id', '__id') code = code.replace('$geom', '__geom') #is it need: $area, $length, $x, $y???? #print code #debug #search needed vars (hmmm... comments?!) need_id = code.find('__id') != -1 need_geom = code.find('__geom') != -1 need_attrs = code.find('__attr') != -1 #compile try: bytecode = compile(code, '<string>', 'exec') except: QMessageBox.critical( self, self.tr('FieldPyculator code execute error'), (self.tr('Field code block can\'t be executed!\n{0}: {1}') ).format(unicode(sys.exc_info()[0].__name__), unicode(sys.exc_info()[1]))) return #get num of updating field field_num = data_provider.fieldNameIndex( self.ui.cmbUpdateField.currentText()) #setup progress bar self.ui.prgTotal.setValue(0) #run if not self.ui.chkOnlySelected.isChecked(): features_for_update = data_provider.featureCount() request = QgsFeatureRequest() if not need_geom: request.setFlags(QgsFeatureRequest.NoGeometry) if need_attrs: request.setSubsetOfAttributes(data_provider.attributeIndexes()) else: request.setSubsetOfAttributes([]) features = active_layer.getFeatures(request) else: features_for_update = active_layer.selectedFeatureCount() features = active_layer.selectedFeatures() if features_for_update > 0: self.ui.prgTotal.setMaximum(features_for_update) for feat in features: feat_id = feat.id() #add needed vars if need_id: new_ns['__id'] = feat_id if need_geom: geom = feat.geometry() new_ns['__geom'] = geom if need_attrs: fields = feat.fields() attr = {} for a in fields: attr[a.name()] = feat[a.name()] new_ns['__attr'] = attr #clear old result if self.RESULT_VAR_NAME in new_ns: del new_ns[self.RESULT_VAR_NAME] #exec try: exec bytecode in new_ns except: QMessageBox.critical( self, self.tr('FieldPyculator code execute error'), self. tr('Field code block can\'t be executed for feature {2}!\n{0}: {1}' ).format(unicode(sys.exc_info()[0].__name__), unicode(sys.exc_info()[1]), unicode(feat_id))) return #check result if not self.RESULT_VAR_NAME in new_ns: QMessageBox.critical( self, self.tr('FieldPyculator code execute error'), self. tr('Field code block does not return \'{0}\' variable! Please declare this variable in your code!' ).format(self.RESULT_VAR_NAME)) return #try assign try: active_layer.changeAttributeValue(feat_id, field_num, new_ns[self.RESULT_VAR_NAME]) except: QMessageBox.critical( self, self.tr('FieldPyculator code execute error'), self. tr('Result value can\'t be assigned to the feature {2}!\n{0}: {1}' ).format(unicode(sys.exc_info()[0].__name__), unicode(sys.exc_info()[1]), unicode(feat_id))) return self.ui.prgTotal.setValue(self.ui.prgTotal.value() + 1) stop = datetime.datetime.now() #workaround for python < 2.7 td = stop - start if sys.version_info[:2] < (2, 7): total_sec = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 else: total_sec = td.total_seconds() QMessageBox.information( self, self.tr('FieldPyculator code executed successfully'), (self.tr('Updated {0} features for {1} seconds')).format( unicode(features_for_update), unicode(total_sec))) @staticmethod def get_field_names(layer): field_map = layer.dataProvider().fields() field_list = [] for field in field_map: field_list.append(unicode(field.name())) return field_list # sorted( field_list, cmp=locale.strcoll )
class FieldPyculatorDialog(QDialog): RESULT_VAR_NAME = 'value' def __init__(self, iface): QDialog.__init__(self) # Set up the user interface from Designer. self.ui = Ui_FieldPyculatorDialog() self.ui.setupUi(self) self.iface = iface self.active_layer = self.iface.activeLayer() self.data_provider = self.active_layer.dataProvider() #INIT CONTROLS VALUES self.ui.lblLayerName.setText(self.active_layer.name()) self.ui.cmbUpdateField.addItems(self.get_field_names(self.active_layer)) self.ui.lstFields.addItems(self.get_field_names(self.active_layer)) self.ui.txtGlobalExp.hide() self.ui.txtFieldExp.insertPlainText(self.RESULT_VAR_NAME + ' = ') #setup actions self.ui.btnToggleEditing.setDefaultAction(self.iface.actionToggleEditing()) #setup syntax highlight self.highlight_field = PythonHighlighter(self.ui.txtFieldExp.document()) self.highlight_global = PythonHighlighter(self.ui.txtGlobalExp.document()) #setup auto focus self.ui.lstFields.setFocusProxy(self.ui.txtFieldExp) self.ui.lstValues.setFocusProxy(self.ui.txtFieldExp) self.ui.btnId.setFocusProxy(self.ui.txtFieldExp) self.ui.btnGeom.setFocusProxy(self.ui.txtFieldExp) #SIGNALS QObject.connect( self.ui.lstFields, SIGNAL( "currentItemChanged ( QListWidgetItem * , QListWidgetItem * )" ), self.update_field_sample_values) QObject.connect( self.ui.lstFields, SIGNAL( "itemDoubleClicked(QListWidgetItem *)" ), self.add_field_to_expression) QObject.connect( self.ui.lstValues, SIGNAL( "itemDoubleClicked(QListWidgetItem *)" ), self.add_value_to_expression) QObject.connect( self.ui.btnGetAll, SIGNAL( " clicked()" ), self.update_field_all_values) QObject.connect( self.ui.btnId, SIGNAL( " clicked()" ), self.add_id_to_explession) QObject.connect( self.ui.btnGeom, SIGNAL( " clicked()" ), self.add_geom_to_explession) QObject.connect( self.ui.btnRun, SIGNAL( " clicked()" ), self.processing) # TODO: add handler for tab replacing in txtFieldExp and txtGlobalExp # TODO: add handler for ctrl + scroll as font size selector in txtFieldExp and txtGlobalExp #--------------- Fields handlers --------------------------- def update_field_sample_values(self, new_item, old_item): field_name = new_item.text() self.update_field_values(field_name, 25) def update_field_all_values(self): if self.ui.lstFields.currentItem(): field_name = self.ui.lstFields.currentItem().text() self.update_field_values(field_name) def update_field_values(self, field_name, limit = -1): self.setCursor(Qt.WaitCursor) field_ind = self.data_provider.fieldNameIndex(field_name) field_type = self.data_provider.fields()[field_ind].typeName() self.ui.lstValues.clear() values = self.data_provider.uniqueValues(field_ind, limit) for val in values: if field_type == 'String': self.ui.lstValues.addItem("'" + unicode(val.toString()) + "'") else: self.ui.lstValues.addItem(unicode(val.toString())) self.unsetCursor() def add_field_to_expression(self, item): field_name = item.text() self.ui.txtFieldExp.insertPlainText(' <'+field_name+'> ') def add_value_to_expression(self, item): value = item.text() self.ui.txtFieldExp.insertPlainText(' '+value+' ') #------------- Vars handlers --------------------------------- def add_id_to_explession(self): self.ui.txtFieldExp.insertPlainText(' $id ') def add_geom_to_explession(self): self.ui.txtFieldExp.insertPlainText(' $geom ') #-------------------------------------------------------------- def processing(self): #check edit mode if not self.active_layer.isEditable(): QMessageBox.warning(self, self.tr("FieldPyculator warning"), self.tr("Layer is not in edit mode! Please start editing the layer!")) return start = datetime.datetime.now() new_ns = {} #run global code if self.ui.grpGlobalExpression.isChecked(): try: code = unicode(self.ui.txtGlobalExp.toPlainText()) bytecode = compile(code, '<string>', 'exec') exec bytecode in new_ns except: QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), (self.tr("Global code block can't be executed!\n%1: %2")) .arg(unicode(sys.exc_info()[0].__name__)) .arg(unicode(sys.exc_info()[1]))) return code = unicode(self.ui.txtFieldExp.toPlainText()) #TODO: check 'result' existing in text of code???!!! #replace all fields tags field_map = self.data_provider.fields() for num, field in field_map.iteritems(): field_name = unicode(field.name()) replval = '__attr[' + str(num) + ']' code = code.replace("<"+field_name+">", replval) #replace all special vars code = code.replace('$id', '__id') code = code.replace('$geom', '__geom') #is it need: $area, $length, $x, $y???? #print code #debug #search needed vars (hmmm... comments?!) need_id = code.find("__id") != -1 need_geom = code.find("__geom") != -1 need_attrs = code.find("__attr") != -1 #compile try: bytecode = compile(code, '<string>', 'exec') except: QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), self.tr("Field code block can't be executed!\n%1: %2") .arg(unicode(sys.exc_info()[0].__name__)) .arg(unicode(sys.exc_info()[1]))) return #get num of updating field field_num = self.data_provider.fieldNameIndex(self.ui.cmbUpdateField.currentText()) #setup progress bar self.ui.prgTotal.setValue(0) #run if not self.ui.chkOnlySelected.isChecked(): #select all features features_for_update = self.data_provider.featureCount() if features_for_update > 0: self.ui.prgTotal.setMaximum(features_for_update) feat = QgsFeature() if need_attrs: attr_ind = self.data_provider.attributeIndexes() else: attr_ind = [] self.data_provider.select(attr_ind, QgsRectangle(), need_geom) while self.data_provider.nextFeature( feat ): feat_id = feat.id() #add needed vars if need_id: new_ns['__id'] = feat_id if need_geom: geom = feat.geometry() new_ns['__geom'] = geom if need_attrs: attr_map = feat.attributeMap() attr = [] for num, a in attr_map.iteritems(): attr.append(self.qvar2py(a)) new_ns['__attr'] = attr #clear old result if new_ns.has_key(self.RESULT_VAR_NAME): del new_ns[self.RESULT_VAR_NAME] #exec try: exec bytecode in new_ns except: QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), self.tr("Field code block can't be executed for feature %3!\n%1: %2") .arg(unicode(sys.exc_info()[0].__name__)) .arg(unicode(sys.exc_info()[1])) .arg(unicode(feat_id))) return #check result if not new_ns.has_key(self.RESULT_VAR_NAME): QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), self.tr("Field code block does not return '%1' variable! Please declare this variable in your code!") .arg(self.RESULT_VAR_NAME)) return #try assign try: self.active_layer.changeAttributeValue(feat_id, field_num, new_ns[self.RESULT_VAR_NAME]) except: QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), self.tr("Result value can't be assigned to the feature %3!\n%1: %2") .arg(unicode(sys.exc_info()[0].__name__)) .arg(unicode(sys.exc_info()[1])) .arg(unicode(feat_id))) return self.ui.prgTotal.setValue(self.ui.prgTotal.value()+1) else: #only selected (TODO: NEED REFACTORING - copy-past!!!) features_for_update = self.active_layer.selectedFeatureCount() if features_for_update > 0: self.ui.prgTotal.setMaximum(features_for_update) for feat in self.active_layer.selectedFeatures(): feat_id = feat.id() #add needed vars if need_id: new_ns['__id'] = feat_id if need_geom: geom = feat.geometry() new_ns['__geom'] = geom if need_attrs: attr_map = feat.attributeMap() attr = [] for num, a in attr_map.iteritems(): attr.append(self.qvar2py(a)) new_ns['__attr'] = attr #clear old result if new_ns.has_key(self.RESULT_VAR_NAME): del new_ns[self.RESULT_VAR_NAME] #exec try: exec bytecode in new_ns except: QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), self.tr("Field code block can't be executed for feature %3!\n%1: %2") .arg(unicode(sys.exc_info()[0].__name__)) .arg(unicode(sys.exc_info()[1])) .arg(unicode(feat_id))) return #check result if not new_ns.has_key(self.RESULT_VAR_NAME): QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), self.tr("Field code block does not return '%1' variable! Please declare this variable in your code!") .arg(self.RESULT_VAR_NAME)) return #try assign try: self.active_layer.changeAttributeValue(feat_id, field_num, new_ns[self.RESULT_VAR_NAME]) except: QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), self.tr("Result value can't be assigned to the feature %3!\n%1: %2") .arg(unicode(sys.exc_info()[0].__name__)) .arg(unicode(sys.exc_info()[1])) .arg(unicode(feat_id))) return self.ui.prgTotal.setValue(self.ui.prgTotal.value()+1) stop = datetime.datetime.now() #workaround for python < 2.7 td = stop - start if sys.version_info[:2] < (2, 7): total_sec = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 else: total_sec = td.total_seconds() QMessageBox.information(self, self.tr("FieldPyculator code executed successfully"), self.tr("Updated %1 features for %2 seconds") .arg(unicode(features_for_update)) .arg(unicode(total_sec)) ) def qvar2py(self, qv): if qv.type() == 2: return qv.toInt()[0] if qv.type() == 10: return unicode(qv.toString()) if qv.type() == 6: return qv.toDouble()[0] return None def get_field_names(self, layer): field_map = layer.dataProvider().fields() field_list = [] for num, field in field_map.iteritems(): field_list.append( unicode( field.name() ) ) return field_list # sorted( field_list, cmp=locale.strcoll )