class FeatureTemplates: def __init__(self, iface): self.iface = iface self.dock = None self.plugin_dir = os.path.dirname(__file__) locale = QSettings().value("locale/userLocale")[0:2] localePath = os.path.join(self.plugin_dir, 'i18n', 'featuretemplates_{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) def initGui(self): self.action = QAction(QIcon(":/plugins/featuretemplates/icon.png"), u"Feature Templates", self.iface.mainWindow()) self.action.triggered.connect(self.show_dock) self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu(u"&FeatureTemplates", self.action) def unload(self): self.iface.removePluginMenu(u"&FeatureTemplates", self.action) self.iface.removeToolBarIcon(self.action) if self.dock: self.iface.removeDockWidget(self.dock) def show_dock(self): if not self.dock: self.dock = QDockWidget('Feature Templates', self.iface.mainWindow()) self.widget = FeatureTemplatesWidget(self.dock) template = {'name' : 'Test'} self.model = FeatureTemplatesModel(templates=[template]) self.widget.setModel(self.model) self.dock.setWidget(self.widget) self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dock) self.dock.show()
class gazetteerSearch: def __init__(self, iface): self.dock = None self.results = [] # Save reference to the QGIS interface self.iface = iface self.iface.newProjectCreated.connect(self._hideMarker) self.iface.projectRead.connect(self._hideMarker) self.canvas = self.iface.mapCanvas() self.marker = QgsVertexMarker(self.iface.mapCanvas()) self.marker.setIconSize(20) self.marker.setPenWidth(3) self.marker.setIconType(QgsVertexMarker.ICON_CROSS) self.marker.hide() # Create the dialog and keep reference self.widget = gazetteerSearchDialog() self.widget.runSearch.connect(self.runSearch) self.widget.ui.clearButton.pressed.connect(self.clearResults) self.widget.zoomRequested.connect(self.zoomTo) # initialize plugin directory self.plugin_dir = QFileInfo(QgsApplication.qgisUserDbFilePath()).path() + "/python/plugins/gazetteersearch" # initialize locale localePath = "" if QGis.QGIS_VERSION_INT < 10900: locale = QSettings().value("locale/userLocale").toString()[0:2] else: locale = QSettings().value("locale/userLocale")[0:2] if QFileInfo(self.plugin_dir).exists(): localePath = self.plugin_dir + "/i18n/gazetteersearch_" + locale + ".qm" if QFileInfo(localePath).exists(): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) def initGui(self): # Create action that will start plugin configuration self.action = QAction(QIcon(":/plugins/gazetteersearch/icon.png"), \ u"Gazetteer Search", self.iface.mainWindow()) # connect the action to the run method self.action.triggered.connect(self.run) # Add toolbar button and menu item self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu(u"&Gazetteer Search", self.action) def unload(self): # Remove the plugin menu item and icon self.iface.removePluginMenu(u"&Gazetteer Search",self.action) self.iface.removeToolBarIcon(self.action) self.iface.mapCanvas().scene().removeItem(self.marker) self.marker = None def _hideMarker(self): self.marker.hide() # run method that performs all the real work def run(self): if not self.dock: self.dock = QDockWidget("Gazetteer Search", self.iface.mainWindow()) self.dock.setWidget(self.widget) self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dock) self.gazetteers = common.getGazetteers() for gazetter in self.gazetteers.iterkeys(): self.widget.addGazetter(gazetter) if len(self.gazetteers) == 1: self.widget.hideGazetteers() else: self.dock.show() def runSearch(self, searchString, selectedGazetteer): searchString = searchString.encode('utf-8') gazetteer_config = self.gazetteers[str(selectedGazetteer)] gazetteer = self.getGazetteerModule(gazetteer_config) url = common.prepareURL(gazetteer.url, gazetteer.params, searchString) def callback(data): try: self.results = list(gazetteer.parseRequestResults(data, self.iface)) except: self.results = [] if len(self.results) == 0: self.widget.addError('No results found for "%s"' % searchString) for res in self.results: self.widget.addResult(res.description) common.search(url, callback) def clearResults(self): self.widget.clearResults() self.marker.hide() def getGazetteerModule(self, config): gazetteer_module = config['gazetteer'] imported_gazetteer = import_module('gazetteersearch.gazetteers.%s' % gazetteer_module) return imported_gazetteer def zoomTo(self, name): for res in self.results: if unicode(res.description) == unicode(name): dest_crs = self.canvas.mapRenderer().destinationCrs() if QGis.QGIS_VERSION_INT < 10900: src_crs = QgsCoordinateReferenceSystem() src_crs.createFromEpsg(res.epsg) else: src_crs = QgsCoordinateReferenceSystem(res.epsg, QgsCoordinateReferenceSystem.EpsgCrsId) transform = QgsCoordinateTransform(src_crs, dest_crs) new_point = transform.transform(res.x, res.y) x = new_point.x() y = new_point.y() self.canvas.setExtent(QgsRectangle(x,y,x,y)) self.canvas.zoomScale(res.zoom) self.canvas.refresh() self.marker.setCenter(new_point) self.marker.show() return
class ComposerWrapper(QObject): """ Embeds custom STDM tools in a QgsComposer instance for managing map-based STDM document templates. """ dataSourceSelected = pyqtSignal(str) def __init__(self, composerView, iface): QObject.__init__(self, composerView) self._compView = composerView self._stdmTB = self.mainWindow().addToolBar("STDM Document Designer") self._selectMoveAction = None self._iface = iface #Container for custom editor widgets self._widgetMappings = {} #Hide default dock widgets if self.itemDock() is not None: self.itemDock().hide() if self.atlasDock() is not None: self.atlasDock().hide() if self.generalDock() is not None: self.generalDock().hide() # Remove default toolbars self._remove_composer_toolbar('mAtlasToolbar') self._remove_composer_toolbar('mComposerToolbar') #Create dock widget for configuring STDM data source self._stdmDataSourceDock = QDockWidget( QApplication.translate("ComposerWrapper", "STDM Data Source"), self.mainWindow()) self._stdmDataSourceDock.setObjectName("STDMDataSourceDock") self._stdmDataSourceDock.setMinimumWidth(300) self._stdmDataSourceDock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self._stdmDataSourceDock) self._dataSourceWidget = ComposerDataSourceSelector() self._stdmDataSourceDock.setWidget(self._dataSourceWidget) self._stdmDataSourceDock.show() #Re-insert dock widgets if self.generalDock() is not None: self.generalDock().show() if self.itemDock() is not None: self.itemDock().show() #Create dock widget for configuring STDM item properties self._stdmItemPropDock = QDockWidget( QApplication.translate("ComposerWrapper", "STDM item properties"), self.mainWindow()) self._stdmItemPropDock.setObjectName("STDMItemDock") self._stdmItemPropDock.setMinimumWidth(300) self._stdmItemPropDock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self._stdmItemPropDock) self._stdmItemPropDock.show() #Re-arrange dock widgets and push up STDM data source dock widget if self.generalDock() is not None: self.mainWindow().splitDockWidget(self._stdmDataSourceDock, self.generalDock(), Qt.Vertical) if self.itemDock() is not None: self.mainWindow().splitDockWidget(self._stdmDataSourceDock, self.itemDock(), Qt.Vertical) if self.generalDock() is not None: self.mainWindow().tabifyDockWidget(self.generalDock(), self.itemDock()) if self.itemDock() is not None: self.mainWindow().splitDockWidget(self.itemDock(), self._stdmItemPropDock, Qt.Vertical) #Set focus on composition properties window if self.generalDock() is not None: self.generalDock().activateWindow() self.generalDock().raise_() #Connect signals self.composition().itemRemoved.connect(self._onItemRemoved) self._dataSourceWidget.cboDataSource.currentIndexChanged.connect( self.propagateDataSourceSelection) self.composerView().selectedItemChanged.connect(self._onItemSelected) #Current template document file self._currDocFile = None #Copy of template document file self._copy_template_file = None self._selected_item_uuid = unicode() self._current_ref_table_index = -1 @property def copy_template_file(self): return self._copy_template_file @copy_template_file.setter def copy_template_file(self, value): self._copy_template_file = value @property def selected_item_uuid(self): return self._selected_item_uuid @selected_item_uuid.setter def selected_item_uuid(self, uuid): self._selected_item_uuid = uuid @property def current_ref_table_index(self): return self._current_ref_table_index @current_ref_table_index.setter def current_ref_table_index(self, value): self._current_ref_table_index = value def _remove_composer_toolbar(self, object_name): """ Removes toolbars from composer window. :param object_name: The object name of the toolbar :type object_name: String :return: None :rtype: NoneType """ composers = self._iface.activeComposers() for i in range(len(composers)): comp = composers[i].composerWindow() widgets = comp.findChildren(QToolBar, object_name) for widget in widgets: comp.removeToolBar(widget) def _removeActions(self): """ Remove inapplicable actions and their corresponding toolbars and menus. """ removeActions = [ "mActionSaveProject", "mActionNewComposer", "mActionDuplicateComposer" ] composerToolbar = self.composerMainToolBar() if composerToolbar != None: saveProjectAction = None for itemAction in composerToolbar.actions(): if itemAction.objectName() == "mActionSaveProject": saveProjectAction = itemAction break if saveProjectAction != None: composerMenu = saveProjectAction.menu() def configure(self): #Create instances of custom STDM composer item configurations for ciConfig in ComposerItemConfig.itemConfigurations: ciConfig(self) def addWidgetMapping(self, uniqueIdentifier, widget): """ Add custom STDM editor widget based on the unique identifier of the composer item """ self._widgetMappings[uniqueIdentifier] = widget def widgetMappings(self): """ Returns a dictionary containing uuid values of composer items linked to STDM widgets. """ return self._widgetMappings def clearWidgetMappings(self): """ Resets the widget mappings collection. """ self._widgetMappings = {} def mainWindow(self): """ Returns the QMainWindow used by the composer view. """ return self._compView.composerWindow() def stdmToolBar(self): """ Returns the instance of the STDM toolbar added to the QgsComposer. """ return self._stdmTB def composerView(self): """ Returns the composer view. """ return self._compView def composition(self): """ Returns the QgsComposition instance used in the composer view. """ return self._compView.composition() def composerItemToolBar(self): """ Returns the toolbar containing actions for adding composer items. """ return self.mainWindow().findChild(QToolBar, "mItemToolbar") def composerMainToolBar(self): """ Returns the toolbar containing actions for managing templates. """ return self.mainWindow().findChild(QToolBar, "mComposerToolbar") def selectMoveAction(self): """ Returns the QAction for selecting or moving composer items. """ if self.composerItemToolBar() != None: if self._selectMoveAction == None: for itemAction in self.composerItemToolBar().actions(): if itemAction.objectName() == "mActionSelectMoveItem": self._selectMoveAction = itemAction break return self._selectMoveAction def checkedItemAction(self): """ Returns the currently selected composer item action. """ if self.selectMoveAction() != None: return self.selectMoveAction().actionGroup().checkedAction() return None def itemDock(self): """ Get the 'Item Properties' dock widget. """ return self.mainWindow().findChild(QDockWidget, "ItemDock") def atlasDock(self): """ Get the 'Atlas generation' dock widget. """ return self.mainWindow().findChild(QDockWidget, "AtlasDock") def generalDock(self): """ Get the 'Composition' dock widget. """ return self.mainWindow().findChild(QDockWidget, "CompositionDock") def stdmDataSourceDock(self): """ Returns the STDM data source dock widget. """ return self._stdmDataSourceDock def stdmItemDock(self): """ Returns the STDM item dock widget. """ return self._stdmItemPropDock def documentFile(self): """ Returns the QFile instance associated with the current document. 'None' will be returned for new, unsaved documents. """ return self._currDocFile def setDocumentFile(self, docFile): """ Sets the document file. """ if not isinstance(docFile, QFile): return self._currDocFile = docFile def selectedDataSource(self): """ Returns the name of the data source specified by the user. """ row = self._stdmDataSourceDock.widget().cboDataSource.currentIndex() table_name = self._stdmDataSourceDock.widget().cboDataSource.itemData( row) return table_name def selected_referenced_table(self): """ :return: Returns the name of currently specified referenced table name. :rtype: str """ return self._stdmDataSourceDock.widget().referenced_table_name() def selectedDataSourceCategory(self): """ Returns the category (view or table) that the data source belongs to. """ if not self.stdmDataSourceDock().widget() is None: return self.stdmDataSourceDock().widget().category() return "" def propagateDataSourceSelection(self, index): """ Propagates the signal when a user select a data source. Listening objects can hook on to it. """ data_source_name = self._stdmDataSourceDock.widget( ).cboDataSource.itemData(index) self.dataSourceSelected.emit(data_source_name) def composer_items(self): """ :return: Returns a list of custom composer items. :rtype: list """ return [ self.composition().getComposerItemById(uuid) for uuid in self._widgetMappings.keys() if not self.composition().getComposerItemById(uuid) is None ] def _clear_composition(self): """ Removes composer items which, otherwise, are causing QGIS to crash when loading a subsequent document template. """ items = self.composition().items() for c_item in items: if isinstance(c_item, QgsComposerItem) and not isinstance( c_item, QgsPaperItem): if c_item.uuid() in self._widgetMappings: #Remove corresponding widget as well as reference in the collection del self._widgetMappings[c_item.uuid()] self.composition().removeItem(c_item) self.composition().itemRemoved.emit(c_item) del c_item self.composition().undoStack().clear() self.composition().itemsModel().clear() def create_new_document_designer(self, file_path): """ Creates a new document designer and loads the document template defined in file path. :param file_path: Path to document template :type file_path: str """ if len(self.composerView().items()) == 3: self.composerView().composerWindow().close() document_designer = self._iface.createNewComposer( "STDM Document Designer") #Embed STDM customizations cw = ComposerWrapper(document_designer, self._iface) cw.configure() #Load template cw.loadTemplate(file_path) def loadTemplate(self, filePath): """ Loads a document template into the view and updates the necessary STDM-related composer items. """ if not QFile.exists(filePath): QMessageBox.critical( self.composerView(), QApplication.translate("OpenTemplateConfig", "Open Template Error"), QApplication.translate( "OpenTemplateConfig", "The specified template does not exist.")) return copy_file = filePath.replace('sdt', 'cpy') # remove existing copy file if QFile.exists(copy_file): copy_template = QFile(copy_file) copy_template.remove() orig_template_file = QFile(filePath) self.setDocumentFile(orig_template_file) # make a copy of the original orig_template_file.copy(copy_file) #templateFile = QFile(filePath) # work with copy templateFile = QFile(copy_file) self.copy_template_file = templateFile if not templateFile.open(QIODevice.ReadOnly): QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Open Operation Error"), "{0}\n{1}".format( QApplication.translate("ComposerWrapper", "Cannot read template file."), templateFile.errorString())) return templateDoc = QDomDocument() if templateDoc.setContent(templateFile): table_config_collection = TableConfigurationCollection.create( templateDoc) ''' First load vector layers for the table definitions in the config collection before loading the composition from file. ''' load_table_layers(table_config_collection) self.clearWidgetMappings() #Load items into the composition and configure STDM data controls self.composition().loadFromTemplate(templateDoc) #Load data controls composerDS = ComposerDataSource.create(templateDoc) #Set title by appending template name title = QApplication.translate("STDMPlugin", "STDM Document Designer") composer_el = templateDoc.documentElement() if not composer_el is None: template_name = "" if composer_el.hasAttribute("title"): template_name = composer_el.attribute("title", "") elif composer_el.hasAttribute("_title"): template_name = composer_el.attribute("_title", "") if template_name: win_title = u"{0} - {1}".format(title, template_name) self.mainWindow().setWindowTitle(template_name) self._configure_data_controls(composerDS) #Load symbol editors spatialFieldsConfig = SpatialFieldsConfiguration.create( templateDoc) self._configureSpatialSymbolEditor(spatialFieldsConfig) #Load photo editors photo_config_collection = PhotoConfigurationCollection.create( templateDoc) self._configure_photo_editors(photo_config_collection) # Load table editors self._configure_table_editors(table_config_collection) items = self.composerView().items() items = [] #Load chart property editors chart_config_collection = ChartConfigurationCollection.create( templateDoc) self._configure_chart_editors(chart_config_collection) # Load QR code property editors qrc_config_collection = QRCodeConfigurationCollection.create( templateDoc) self._configure_qr_code_editors(qrc_config_collection) self._sync_ids_with_uuids() def saveTemplate(self): """ Creates and saves a new document template. """ # Validate if the user has specified the data source if not self.selectedDataSource(): QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Error"), QApplication.translate( "ComposerWrapper", "Please specify the " "data source name for the document composition.")) return # Assert if the referenced table name has been set if not self.selected_referenced_table(): QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Error"), QApplication.translate( "ComposerWrapper", "Please specify the " "referenced table name for the selected data source.")) return # If it is a new unsaved document template then prompt for the document name. docFile = self.documentFile() if docFile is None: docName, ok = QInputDialog.getText( self.composerView(), QApplication.translate("ComposerWrapper", "Template Name"), QApplication.translate("ComposerWrapper", "Please enter the template name below"), ) if not ok: return if ok and not docName: QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Error"), QApplication.translate("ComposerWrapper", "Please enter a template name!")) self.saveTemplate() if ok and docName: templateDir = self._composerTemplatesPath() if templateDir is None: QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Error"), QApplication.translate( "ComposerWrapper", "Directory for document templates cannot not be found." )) return absPath = templateDir + "/" + docName + ".sdt" #Check if there is an existing document with the same name caseInsenDic = CaseInsensitiveDict(documentTemplates()) if docName in caseInsenDic: result = QMessageBox.warning( self.composerView(), QApplication.translate("ComposerWrapper", "Existing Template"), u"'{0}' {1}.\nDo you want to replace the " "existing template?".format( docName, QApplication.translate("ComposerWrapper", "already exists")), QMessageBox.Yes | QMessageBox.No) if result == QMessageBox.Yes: #Delete the existing template delFile = QFile(absPath) remStatus = delFile.remove() if not remStatus: QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Delete Error"), "'{0}' {1}.".format( docName, QApplication.translate( "ComposerWrapper", "template could not be removed by the system," " please remove it manually from the document templates directory." ))) return else: return docFile = QFile(absPath) else: return docFileInfo = QFileInfo(docFile) if not docFile.open(QIODevice.WriteOnly): QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Save Operation Error"), "{0}\n{1}".format( QApplication.translate("ComposerWrapper", "Could not save template file."), docFile.errorString())) return templateDoc = QDomDocument() template_name = docFileInfo.completeBaseName() # Catch exception raised when writing items' elements try: self._writeXML(templateDoc, template_name) except Exception as exc: msg = unicode(exc) QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Save Error"), msg) docFile.close() docFile.remove() return if docFile.write(templateDoc.toByteArray()) == -1: QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Save Error"), QApplication.translate("ComposerWrapper", "Could not save template file.")) return else: self.mainWindow().setWindowTitle(template_name) self.setDocumentFile(docFile) docFile.close() if self.copy_template_file: self.copy_template_file.close() def _writeXML(self, xml_doc, doc_name): """ Write the template configuration into the XML document. """ #Write default composer configuration composer_element = xml_doc.createElement("Composer") composer_element.setAttribute("title", doc_name) composer_element.setAttribute("visible", 1) xml_doc.appendChild(composer_element) self.composition().writeXML(composer_element, xml_doc) #Write STDM data field configurations dataSourceElement = ComposerDataSource.domElement(self, xml_doc) composer_element.appendChild(dataSourceElement) #Write spatial field configurations spatialColumnsElement = SpatialFieldsConfiguration.domElement( self, xml_doc) dataSourceElement.appendChild(spatialColumnsElement) #Write photo configuration photos_element = PhotoConfigurationCollection.dom_element( self, xml_doc) dataSourceElement.appendChild(photos_element) #Write table configuration tables_element = TableConfigurationCollection.dom_element( self, xml_doc) dataSourceElement.appendChild(tables_element) #Write chart configuration charts_element = ChartConfigurationCollection.dom_element( self, xml_doc) dataSourceElement.appendChild(charts_element) # Write QRCode configuration qr_codes_element = QRCodeConfigurationCollection.dom_element( self, xml_doc) dataSourceElement.appendChild(qr_codes_element) def _configure_data_controls(self, composer_data_source): """ Configure the data source and data field controls based on the composer data source configuration. """ if not self.stdmDataSourceDock().widget() is None: #Set data source dataSourceWidget = self.stdmDataSourceDock().widget() dataSourceWidget.setCategory(composer_data_source.category()) dataSourceWidget.setSelectedSource(composer_data_source.name()) dataSourceWidget.set_referenced_table( composer_data_source.referenced_table_name) #Set data field controls for composerId in composer_data_source.dataFieldMappings().reverse: #Use composer item id since the uuid is stripped off composerItem = self.composition().getComposerItemById( composerId) if not composerItem is None: compFieldSelector = ComposerFieldSelector( self, composerItem, self.composerView()) compFieldSelector.selectFieldName( composer_data_source.dataFieldName(composerId)) #Add widget to the collection but now use the current uuid of the composition item self.addWidgetMapping(composerItem.uuid(), compFieldSelector) def _configureSpatialSymbolEditor(self, spatial_field_config): """ Configure symbol editor controls. """ if not self.stdmDataSourceDock().widget() is None: for item_id, spFieldsMappings in spatial_field_config.spatialFieldsMapping( ).iteritems(): mapItem = self.composition().getComposerItemById(item_id) if not mapItem is None: composerSymbolEditor = ComposerSymbolEditor( self, self.composerView()) composerSymbolEditor.add_spatial_field_mappings( spFieldsMappings) #Add widget to the collection but now use the current uuid of the composer map self.addWidgetMapping(mapItem.uuid(), composerSymbolEditor) def _configure_photo_editors(self, photo_config_collection): """ Creates widgets for editing photo data sources. :param photo_config_collection: PhotoConfigurationCollection instance. :type photo_config_collection: PhotoConfigurationCollection """ if self.stdmDataSourceDock().widget() is None: return for item_id, photo_config in photo_config_collection.mapping( ).iteritems(): pic_item = self.composition().getComposerItemById(item_id) if not pic_item is None: photo_editor = ComposerPhotoDataSourceEditor( self, self.composerView()) photo_editor.set_configuration(photo_config) self.addWidgetMapping(pic_item.uuid(), photo_editor) def _configure_chart_editors(self, chart_config_collection): """ Creates widgets for editing chart properties. :param chart_config_collection: ChartConfigurationCollection instance. :type chart_config_collection: ChartConfigurationCollection """ if self.stdmDataSourceDock().widget() is None: return for item_id, chart_config in chart_config_collection.mapping( ).iteritems(): chart_item = self.composition().getComposerItemById(item_id) if not chart_item is None: chart_editor = ComposerChartConfigEditor( self, self.composerView()) chart_editor.set_configuration(chart_config) self.addWidgetMapping(chart_item.uuid(), chart_editor) def _configure_table_editors(self, table_config_collection): """ Creates widgets for editing table data sources. :param table_config_collection: TableConfigurationCollection instance. :type table_config_collection: TableConfigurationCollection """ if self.stdmDataSourceDock().widget() is None: return for item_id, table_config in table_config_collection.mapping( ).iteritems(): table_item = self.composition().getComposerItemById(item_id) if table_item is not None: table_editor = ComposerTableDataSourceEditor( self, table_item, self.composerView()) table_editor.set_configuration(table_config) table_editor.ref_table.cbo_ref_table.currentIndexChanged[ str].connect(table_editor.set_table_vector_layer) self.addWidgetMapping(table_item.uuid(), table_editor) def _configure_qr_code_editors(self, qr_code_config_collection): """ Creates widgets for editing QR code properties. :param qr_code_config_collection: QRCodeConfigurationCollection instance. :type qr_code_config_collection: QRCodeConfigurationCollection """ if self.stdmDataSourceDock().widget() is None: return for item_id, qrc_config in qr_code_config_collection.mapping( ).iteritems(): qrc_item = self.composition().getComposerItemById(item_id) if not qrc_item is None: qrc_editor_cls = qr_code_config_collection.editor_type qrc_editor = qrc_editor_cls(self, self.composerView()) qrc_editor.set_configuration(qrc_config) self.addWidgetMapping(qrc_item.uuid(), qrc_editor) def _sync_ids_with_uuids(self): """ Matches IDs of custom STDM items with the corresponding UUIDs. This is applied when loading existing templates so that the saved document contains a matching pair of ID and UUID for each composer item. """ items = self._widgetMappings.keys() for item_uuid in self._widgetMappings.keys(): item = self.composition().getComposerItemByUuid(item_uuid) if not item is None: item.setId(item_uuid) def _composerTemplatesPath(self): """ Reads the path of composer templates in the registry. """ regConfig = RegistryConfig() keyName = "ComposerTemplates" valueCollection = regConfig.read([keyName]) if len(valueCollection) == 0: return None else: return valueCollection[keyName] def _onItemRemoved(self, item): """ Slot raised when a composer item is removed from the scene. """ """ Code will not work since a QObject instance is returned instead of a QgsComposerItem if item.uuid() in self._widgetMappings: del self._widgetMappings[item.uuid()] """ pass def _onItemSelected(self, item): """ Slot raised when a composer item is selected. Load the corresponding field selector if the selection is an STDM data field label. QComposerLabel is returned as a QObject in the slot argument hence, we have resorted to capturing the current selected items in the composition. """ selectedItems = self.composition().selectedComposerItems() if len(selectedItems) == 0: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) == 1: composer_item = selectedItems[0] if composer_item.uuid() in self._widgetMappings: stdmWidget = self._widgetMappings[composer_item.uuid()] self.selected_item_uuid = composer_item.uuid() if stdmWidget == self._stdmItemPropDock.widget(): return else: self._stdmItemPropDock.setWidget(stdmWidget) #Playing it safe in applying the formatting for the editor controls where applicable itemFormatter = None if isinstance(stdmWidget, ComposerTableDataSourceEditor): itemFormatter = TableFormatter() if isinstance(composer_item, QgsComposerArrow): itemFormatter = LineFormatter() elif isinstance(composer_item, QgsComposerLabel): itemFormatter = DataLabelFormatter() elif isinstance(composer_item, QgsComposerMap): itemFormatter = MapFormatter() elif isinstance(composer_item, QgsComposerPicture): """ Use widget attribute to distinguish type i.e. whether it is a photo, graph etc. """ editor_widget = self._widgetMappings[composer_item.uuid()] if isinstance(editor_widget, ComposerPhotoDataSourceEditor): itemFormatter = PhotoFormatter() elif isinstance(editor_widget, ComposerChartConfigEditor): itemFormatter = ChartFormatter() elif isinstance(editor_widget, QRCodeConfigurationCollection.editor_type): itemFormatter = QRCodeFormatter() elif isinstance(composer_item, QgsComposerAttributeTableV2): itemFormatter = TableFormatter() if itemFormatter is not None: itemFormatter.apply(composer_item, self, True) else: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) > 1: self._stdmItemPropDock.setWidget(None)
class Plugin(QObject): def __init__(self, iface): QObject.__init__(self) self.__iface = iface self.__shortcuts = [] self.__current_section = QComboBox() self.__current_section.setMinimumWidth(150) self.__current_graph = QComboBox() self.__current_graph.setMinimumWidth(150) self.__toolbar = None self.__axis_layer = None self.__menu = None self.__log_strati = None def initGui(self): for keyseq, slot in ( (Qt.CTRL + Qt.ALT + Qt.Key_K, self.__create_group), (Qt.CTRL + Qt.ALT + Qt.Key_S, self.__select_next_group), (Qt.CTRL + Qt.ALT + Qt.Key_N, self.__next_section), (Qt.CTRL + Qt.ALT + Qt.Key_B, self.__previous_section) ): short = QShortcut(QKeySequence(keyseq), self.__iface.mainWindow()) short.setContext(Qt.ApplicationShortcut) short.activated.connect(slot) self.__shortcuts.append(short) self.__menu = QMenu("Albion") self.__menu.aboutToShow.connect(self.__create_menu_entries) self.__iface.mainWindow().menuBar().addMenu(self.__menu) self.__toolbar = QToolBar('Albion') self.__iface.addToolBar(self.__toolbar) self.__toolbar.addAction(icon('log_strati.svg'), 'stratigraphic log').triggered.connect(self.__log_strati_clicked) self.__toolbar.addWidget(self.__current_graph) self.__current_graph.currentIndexChanged[unicode].connect(self.__current_graph_changed) self.__toolbar.addWidget(self.__current_section) self.__current_section.currentIndexChanged[unicode].connect(self.__current_section_changed) self.__toolbar.addAction(icon('previous_line.svg'), 'previous section (Ctrl+Alt+b)').triggered.connect(self.__previous_section) self.__toolbar.addAction(icon('next_line.svg'), 'next section (Ctrl+Alt+n)').triggered.connect(self.__next_section) self.__toolbar.addAction(icon('line_from_selected.svg'), 'create temporary section').triggered.connect(self.__section_from_selection) self.__viewer3d = QDockWidget('3D') self.__viewer3d.setWidget(Viewer3d()) self.__iface.addDockWidget(Qt.LeftDockWidgetArea, self.__viewer3d) self.__iface.mainWindow().tabifyDockWidget( self.__iface.mainWindow().findChild(QDockWidget, "Layers"), self.__viewer3d) self.__viewer3d_ctrl = QToolBar('3D controls') self.__viewer3d_ctrl.addWidget(ViewerControls(self.__viewer3d.widget())) self.__iface.addToolBar(self.__viewer3d_ctrl) QgsProject.instance().readProject.connect(self.__qgis__project__loaded) self.__qgis__project__loaded() # case of reload def unload(self): for s in self.__shortcuts: s.setParent(None) self.__toolbar and self.__toolbar.setParent(None) self.__menu and self.__menu.setParent(None) self.__viewer3d and self.__viewer3d.setParent(None) self.__viewer3d_ctrl and self.__viewer3d_ctrl.setParent(None) def __add_menu_entry(self, name, callback, enabled=True, help_str=''): act = self.__menu.addAction(name) if callback is not None: act.triggered.connect(callback) act.setEnabled(enabled) act.setToolTip(help_str) else: act.setEnabled(False) act.setToolTip("NOT INMPLEMENTED "+help_str) return act def __create_menu_entries(self): self.__menu.clear() self.__add_menu_entry('New &Project', self.__new_project) self.__add_menu_entry('Import Project', self.__import_project) self.__add_menu_entry('Upgrade Project', self.__upgrade_project) self.__menu.addSeparator() self.__add_menu_entry('&Import Data', self.__import_data, self.project is not None, "Import data from directory. The data files are in ") self.__menu.addSeparator() self.__add_menu_entry('Create cells', self.__create_cells, self.project is not None and self.project.has_collar, "Create Delaunay triangulation of collar layer.") self.__add_menu_entry('Refresh all edges', self.__refresh_all_edge, self.project is not None and self.project.has_cell, "Refresh materialized view of all cell edges used by graph possible edges.") self.__menu.addSeparator() #self.__add_menu_entry(u'Create section views 0° and 90°', self.__create_section_view_0_90, # False and self.project is not None and self.project.has_hole, # "Create section views (i.e. cut directions) to work West-East and South-North for this project") #self.__add_menu_entry(u'Create section views -45° and 45°', None, # False and self.project is not None and self.project.has_hole, # "Create section views (i.e. cut directions) to work SE-NW and SW-NE for this project") # #self.__menu.addSeparator() self.__add_menu_entry('Create sections', self.__create_sections, self.project is not None and self.project.has_group_cell, "Once cell groups have been defined, create section lines.") self.__menu.addSeparator() self.__add_menu_entry('Compute &Mineralization', self.__compute_mineralization, self.project is not None and self.project.has_radiometry, "") self.__menu.addSeparator() self.__add_menu_entry('New &Graph', self.__new_graph, self.project is not None, "Create a new grap.h") self.__add_menu_entry('Delete Graph', self.__delete_graph, self.project is not None) self.__menu.addSeparator() self.__add_menu_entry('Create volumes', self.__create_volumes, self.project is not None and bool(self.__current_graph.currentText()), "Create volumes associated with current graph.") self.__menu.addSeparator() self.__add_menu_entry('Create terminations', self.__create_terminations, self.project is not None and bool(self.__current_graph.currentText()), "Create terminations associated with current graph.") self.__menu.addSeparator() self.__add_menu_entry('Toggle axis', self.__toggle_axis) self.__menu.addSeparator() self.__add_menu_entry('Export Project', self.__export_project, self.project is not None) self.__add_menu_entry('Export Sections', None, self.project is not None and self.project.has_section, "Export triangulated section in .obj or .dxf format") self.__add_menu_entry('Export Volume', self.__export_volume, self.project is not None and bool(self.__current_graph.currentText()), "Export volume of current graph in .obj or .dxf format") self.__menu.addSeparator() self.__menu.addAction('Help').triggered.connect(self.open_help) def __current_graph_changed(self, graph_id): if self.project is None: return self.__viewer3d.widget().set_graph(graph_id) def __getattr__(self, name): if name == "project": project_name = QgsProject.instance().readEntry("albion", "project_name", "")[0] return Project(project_name) if project_name else None else: raise AttributeError(name) def __refresh_all_edge(self): if self.project is None: return self.project.refresh_all_edge() def __create_terminations(self): if self.project is None: return self.project.create_terminations(self.__current_graph.currentText()) self.__viewer3d.widget().refresh_data() self.__refresh_layers('section') def __create_volumes(self): if self.project is None: return self.project.create_volumes(self.__current_graph.currentText()) self.__viewer3d.widget().refresh_data() def __next_section(self): if self.project is None: return self.project.next_section(self.__current_section.currentText()) self.__refresh_layers('section') self.__viewer3d.widget().scene.update('section') self.__viewer3d.widget().update() def __previous_section(self): if self.project is None: return self.project.previous_section(self.__current_section.currentText()) self.__refresh_layers('section') self.__viewer3d.widget().scene.update('section') self.__viewer3d.widget().update() def __refresh_layers(self, name=None): for layer in self.__iface.mapCanvas().layers(): if name is None or layer.name().find(name) != -1: layer.triggerRepaint() def __current_section_changed(self, section_id): layers = QgsMapLayerRegistry.instance().mapLayersByName(u"group_cell") if len(layers): layers[0].setSubsetString("section_id='{}'".format(section_id)) self.__refresh_layers('section') def __select_next_group(self): if self.project and self.__iface.activeLayer() and self.__iface.activeLayer().name() == u"cell": self.__iface.activeLayer().removeSelection() self.__iface.activeLayer().selectByExpression( "id in ({})".format(",".join(project.next_group_ids()))) def __create_group(self): if self.project and self.__iface.activeLayer() and self.__iface.activeLayer().name() == u"cell": if self.__iface.activeLayer().selectedFeatureCount(): section = self.__current_section.currentText() self.project.create_group(section, [f['id'] for f in self.__iface.activeLayer().selectedFeatures()]) self.__iface.activeLayer().removeSelection() self.__refresh_layers('group_cell') def __qgis__project__loaded(self): if self.project is None: return self.__current_graph.clear() self.__current_section.clear() self.__current_section.addItems(self.project.sections()) self.__current_graph.addItems(self.project.graphs()) layers = QgsMapLayerRegistry.instance().mapLayersByName('section.anchor') if len(layers): layers[0].editingStopped.connect(self.__update_section_list) self.__viewer3d.widget().resetScene(self.project) def __update_section_list(self): self.__current_section.clear() self.__current_section.addItems(self.project.sections()) def __upgrade_project(self): project_name, ok = QInputDialog.getText(self.__iface.mainWindow(), "Database name", "Database name:", QLineEdit.Normal, '') if not ok: return Project(project_name).update() def __new_project(self): # @todo open dialog to configure project name and srid fil = QFileDialog.getSaveFileName(None, u"New project name (no space, plain ascii)", QgsProject.instance().readEntry("albion", "last_dir", "")[0], "QGIS poject file (*.qgs)") if not fil: return fil = fil if len(fil)>4 and fil[-4:]=='.qgs' else fil+'.qgs' fil = fil.replace(' ','_') try: fil.decode('ascii') except UnicodeDecodeError: self.__iface.messageBar().pushError('Albion:', "project name may only contain asci character (no accent)") return srid, ok = QInputDialog.getText(self.__iface.mainWindow(), "Project SRID", "Project SRID EPSG:", QLineEdit.Normal, '32632') if not ok: return srid = int(srid) project_name = str(os.path.split(fil)[1][:-4]) if Project.exists(project_name): if QMessageBox.Yes != QMessageBox(QMessageBox.Information, "Delete existing DB", "Database {} exits, to you want to delete it ?".format(project_name), QMessageBox.Yes|QMessageBox.No).exec_(): return Project.delete(project_name) self.__iface.messageBar().pushInfo('Albion:', "creating project...") Project.create(project_name, srid) # load template open(fil, 'w').write( open(resource('template_project.qgs')).read().replace('template_project', project_name)) self.__iface.newProject() QgsProject.instance().setFileName(fil) QgsProject.instance().read() QgsProject.instance().writeEntry("albion", "project_name", project_name) QgsProject.instance().writeEntry("albion", "srid", srid) QgsProject.instance().write() self.__qgis__project__loaded() def __import_data(self): if self.project is None: return if not QgsProject.instance().readEntry("albion", "conn_info", "")[0]: return dir_ = QFileDialog.getExistingDirectory(None, u"Data directory", QgsProject.instance().readEntry("albion", "last_dir", "")[0]) if not dir_: return QgsProject.instance().writeEntry("albion", "last_dir", dir_), progressMessageBar = self.__iface.messageBar().createMessage("Loading {}...".format(dir_)) progress = QProgressBar() progress.setAlignment(Qt.AlignLeft|Qt.AlignVCenter) progressMessageBar.layout().addWidget(progress) self.__iface.messageBar().pushWidget(progressMessageBar, self.__iface.messageBar().INFO) self.project.import_data(dir_, ProgressBar(progress)) self.project.triangulate() self.project.create_section_view_0_90(4) self.__iface.messageBar().clearWidgets() collar = QgsMapLayerRegistry.instance().mapLayersByName('collar')[0] collar.reload() collar.updateExtents() self.__iface.setActiveLayer(collar) QApplication.instance().processEvents() while self.__iface.mapCanvas().isDrawing(): QApplication.instance().processEvents() self.__iface.zoomToActiveLayer() self.__iface.actionSaveProject().trigger() self.__viewer3d.widget().resetScene(self.project) self.__current_section.clear() self.__current_section.addItems(self.project.sections()) def __new_graph(self): if self.project is None: return graph, ok = QInputDialog.getText(self.__iface.mainWindow(), "Graph", "Graph name:", QLineEdit.Normal, 'test_graph') if not ok: return parent, ok = QInputDialog.getText(self.__iface.mainWindow(), "Parent Graph", "Parent Graph name:", QLineEdit.Normal, '') if not ok: return self.project.new_graph(graph, parent) self.__current_graph.addItem(graph) self.__current_graph.setCurrentIndex(self.__current_graph.findText(graph)) def __delete_graph(self): if self.project is None: return graph, ok = QInputDialog.getText(self.__iface.mainWindow(), "Graph", "Graph name:", QLineEdit.Normal, self.__current_graph.currentText()) if not ok: return self.__current_graph.removeItem(self.__current_graph.findText(graph)) def __toggle_axis(self): if self.__axis_layer: pass QgsMapLayerRegistry.instance().removeMapLayer(self.__axis_layer.id()) self.__axis_layer = None else: self.__axis_layer = AxisLayer(self.__iface.mapCanvas().mapSettings().destinationCrs()) QgsMapLayerRegistry.instance().addMapLayer(self.__axis_layer) self.__refresh_layers() def __create_cells(self): if self.project is None: return self.project.triangulate() self.__refresh_layers('cells') def __create_sections(self): if self.project is None: return self.project.create_sections() def __compute_mineralization(self): MineralizationDialog(self.project).exec_() def __export_volume(self): if self.project is None: return fil = QFileDialog.getSaveFileName(None, u"Export volume for current graph", QgsProject.instance().readEntry("albion", "last_dir", "")[0], "File formats (*.dxf *.obj)") if not fil: return QgsProject.instance().writeEntry("albion", "last_dir", os.path.dirname(fil)), if fil[-4:] == '.obj': self.project.export_obj(self.__current_graph.currentText(), fil) elif fil[-4:] == '.dxf': self.project.export_dxf(self.__current_graph.currentText(), fil) else: self.__iface.messageBar().pushWarning('Albion', 'unsupported extension for volume export') def __import_project(self): fil = QFileDialog.getOpenFileName(None, u"Import project from file", QgsProject.instance().readEntry("albion", "last_dir", "")[0], "File formats (*.zip)") if not fil: return QgsProject.instance().writeEntry("albion", "last_dir", os.path.dirname(fil)), if fil[-4:] != '.zip': self.__iface.messageBar().pushWarning('Albion', 'unsupported extension for import') project_name = os.path.split(fil)[1][:-4] dir_ = tempfile.gettempdir() with zipfile.ZipFile(fil, "r") as z: z.extractall(dir_) dump = find_in_dir(dir_, '.dump') prj = find_in_dir(dir_, '.qgs') self.__iface.messageBar().pushInfo('Albion', 'loading {} from {}'.format(project_name, dump)) if Project.exists(project_name): if QMessageBox.Yes != QMessageBox(QMessageBox.Information, "Delete existing DB", "Database {} exits, to you want to delete it ?".format(project_name), QMessageBox.Yes|QMessageBox.No).exec_(): return Project.delete(project_name) project = Project.import_(project_name, dump) QgsProject.instance().read(QFileInfo(prj)) def __export_project(self): if self.project is None: return fil = QFileDialog.getSaveFileName(None, u"Export project", QgsProject.instance().readEntry("albion", "last_dir", "")[0], "Data files(*.zip)") if not fil: return QgsProject.instance().writeEntry("albion", "last_dir", os.path.dirname(fil)), if os.path.exists(fil): os.remove(fil) with zipfile.ZipFile(fil, 'w') as project: dump = tempfile.mkstemp()[1] self.project.export(dump) project.write(dump, self.project.name+'.dump') project.write(QgsProject.instance().fileName(), os.path.split(QgsProject.instance().fileName())[1]) def __create_section_view_0_90(self): if self.project is None: return self.project.create_section_view_0_90() def __log_strati_clicked(self): #@todo switch behavior when in section view -> ortho self.__click_tool = QgsMapToolEmitPoint(self.__iface.mapCanvas()) self.__iface.mapCanvas().setMapTool(self.__click_tool) self.__click_tool.canvasClicked.connect(self.__map_log_clicked) def __map_log_clicked(self, point, button): self.__click_tool.setParent(None) self.__click_tool = None if self.project is None: self.__log_strati and self.__log_strati.setParent(None) self.__log_strati = None return if self.__log_strati is None: self.__log_strati = QDockWidget('Stratigraphic Log') self.__log_strati.setWidget(BoreHoleWindow(self.project)) self.__iface.addDockWidget(Qt.LeftDockWidgetArea, self.__log_strati) self.__iface.mainWindow().tabifyDockWidget( self.__iface.mainWindow().findChild(QDockWidget, "Layers"), self.__log_strati) res = self.project.closest_hole_id(point.x(), point.y()) if res: self.__log_strati.widget().scene.set_current_id(res) self.__log_strati.show() self.__log_strati.raise_() def __section_from_selection(self): if self.project is None: return if self.__iface.activeLayer() and self.__iface.activeLayer().name() == u"collar" \ and self.__iface.activeLayer().selectedFeatures(): collar = self.__iface.activeLayer() selection = collar.selectedFeatures() if len(selection) < 2: return def align(l): assert len(l) >= 2 res = numpy.array(l[:2]) for p in l[2:]: u, v = res[0] - res[1], p - res[1] if numpy.dot(u,v) < 0: res[1] = p elif numpy.dot(u, u) < numpy.dot(v,v): res[0] = p # align with ref direction sqrt2 = math.sqrt(2.)/2 l = l[numpy.argsort(numpy.dot(l-res[0], res[1]-res[0]))] d = numpy.array(l[-1] - l[0]) dr = numpy.array([(0,1),(sqrt2, sqrt2),(1,0), (sqrt2, -sqrt2)]) i = numpy.argmax(numpy.abs(dr.dot(d))) return l if (dr.dot(d))[i] > 0 else l[::-1] line = LineString(align(numpy.array([f.geometry().asPoint() for f in selection]))) collar.removeSelection() self.project.set_section_geom(self.__current_section.currentText(), line) self.__refresh_layers('section') self.__viewer3d.widget().scene.update('section') self.__viewer3d.widget().update() def open_help(self): QDesktopServices.openUrl(QUrl.fromLocalFile(os.path.join(os.path.dirname(__file__), 'doc', 'build', 'html', 'index.html')))
class PartirUsoSuelo: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join( self.plugin_dir, 'i18n', 'PartirUsoSuelo_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) # Create the dialog (after translation) and keep reference """ Creamos el dialogo """ #self.dlg = PartirUsoSueloDialog() """ Creamos el DockWidget (le asignamos tambien al nombre dlg para no tener que modificar el codigo)""" self.dockWidget = PartirUsoSueloDockWidget() self.dlg = self.dockWidget # Declare instance attributes self.actions = [] self.menu = self.tr(u'&PartirUsoSuelo') # TODO: We are going to let the user set this up in a future iteration self.toolbar = self.iface.addToolBar(u'PartirUsoSuelo') self.toolbar.setObjectName(u'PartirUsoSuelo') """ Estas linias solo lo tiene el dialogo, el dock no """ # self.dlg.lineEdit.clear() # self.dlg.pushButton.clicked.connect(self.abrire_archivo) self.dlg.comboBox.currentIndexChanged.connect(self.habilitar_edicion) self.dlg.comboBox_2.currentIndexChanged.connect(self.habilitar_edicion) self.dlg.pushButton_iniciar.clicked.connect(self.iniciar_edicion) self.dlg.pushButton_finalizar.clicked.connect(self.finalizar_edicion) # noinspection PyMethodMayBeStatic def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('PartirUsoSuelo', message) def add_action( self, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): """Add a toolbar icon to the toolbar. :param icon_path: Path to the icon for this action. Can be a resource path (e.g. ':/plugins/foo/bar.png') or a normal file system path. :type icon_path: str :param text: Text that should be shown in menu items for this action. :type text: str :param callback: Function to be called when the action is triggered. :type callback: function :param enabled_flag: A flag indicating if the action should be enabled by default. Defaults to True. :type enabled_flag: bool :param add_to_menu: Flag indicating whether the action should also be added to the menu. Defaults to True. :type add_to_menu: bool :param add_to_toolbar: Flag indicating whether the action should also be added to the toolbar. Defaults to True. :type add_to_toolbar: bool :param status_tip: Optional text to show in a popup when mouse pointer hovers over the action. :type status_tip: str :param parent: Parent widget for the new action. Defaults None. :type parent: QWidget :param whats_this: Optional text to show in the status bar when the mouse pointer hovers over the action. :returns: The action that was created. Note that the action is also added to self.actions list. :rtype: QAction """ icon = QIcon(icon_path) action = QAction(icon, text, parent) action.triggered.connect(callback) action.setEnabled(enabled_flag) if status_tip is not None: action.setStatusTip(status_tip) if whats_this is not None: action.setWhatsThis(whats_this) if add_to_toolbar: self.toolbar.addAction(action) if add_to_menu: self.iface.addPluginToVectorMenu( self.menu, action) self.actions.append(action) return action def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = ':/plugins/PartirUsoSuelo/icon.png' self.add_action( icon_path, text=self.tr(u'Partir Uso Suelo'), callback=self.run, parent=self.iface.mainWindow()) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginVectorMenu( self.tr(u'&PartirUsoSuelo'), action) self.iface.removeToolBarIcon(action) # remove the toolbar del self.toolbar def habilitar_edicion(self): # Guardamos la capa seleccionada indiceLayerCortarSeleccionada = self.dlg.comboBox.currentIndex() indiceLayerProcesarSeleccionada = self.dlg.comboBox_2.currentIndex() layers = self.iface.legendInterface().layers() layerCortarSeleccionada = layers[indiceLayerCortarSeleccionada] layerProcesarSeleccionada = layers[indiceLayerProcesarSeleccionada] self.vlayer = layerCortarSeleccionada self.vlayerProcesar = layerProcesarSeleccionada # Habilitamos la edicion self.dlg.pushButton_iniciar.setEnabled(True) def iniciar_edicion(self): self.iface.setActiveLayer(self.vlayer) self.listado_features_modificadas = [] self.listado_features_nuevas = [] self.connect_signals() self.vlayer.startEditing() self.iface.actionSplitFeatures().trigger() self.dlg.pushButton_iniciar.setEnabled(False) self.dlg.pushButton_finalizar.setEnabled(True) def finalizar_edicion(self): self.vlayer.commitChanges() self.dlg.pushButton_iniciar.setEnabled(True) self.dlg.pushButton_finalizar.setEnabled(False) # Obtenemos la lista de campos inFields = self.vlayer.dataProvider().fields() # Convert its geometry type enum to a string we can pass to # QgsVectorLayer's constructor inLayerGeometryType = ['Point', 'Line', 'Polygon'][self.vlayer.geometryType()] # Convert its CRS to a string we can pass to QgsVectorLayer's constructor inLayerCRS = self.vlayer.crs().authid() self.listado_features_final = [] for feature in self.listado_features_nuevas: # Make the output layer outLayer = QgsVectorLayer(inLayerGeometryType + '?crs=' + inLayerCRS, \ u'parcela_nueva_id_' + str(feature.id()), \ 'memory') # changes are only possible when editing the layer outLayer.startEditing() outLayer.dataProvider().addAttributes(inFields.toList()) outLayer.dataProvider().addFeatures([feature]) # outLayer.addFeature(feature, True) # commit to stop editing the layer outLayer.commitChanges() # update layer's extent when new features have been added # because change of extent in provider is not propagated to the layer outLayer.updateExtents() # Add it to the map QgsMapLayerRegistry.instance().addMapLayer(outLayer) # Realizamos la intersección de la feautre nueva con la capa que se quiere procesar # interseccion = processing.runalg("qgis:intersection", self.vlayerProcesar, outLayer, None) nombre_interseccion = "interseccion_parcela_" + str(feature.id()) interseccion = processing.runalg("qgis:intersection", self.vlayerProcesar, outLayer, "C:\\Temp\\" + nombre_interseccion + ".shp") # self.iface.addVectorLayer(interseccion.get("OUTPUT"), nombre_interseccion, "ogr") # intersectionLayer = QgsMapLayerRegistry.instance().mapLayersByName(nombre_interseccion)[0] layer_intersect = QgsVectorLayer(interseccion.get("OUTPUT"), nombre_interseccion, "ogr") layer_intersect.startEditing() for feature_intersect in layer_intersect.getFeatures(): feature_intersect['id_parcela'] = feature.id() layer_intersect.updateFeature(feature_intersect) layer_intersect.commitChanges() # Eliminamos los campos que sobran numero_campos_original = self.vlayerProcesar.dataProvider().fields().count() numero_campos_intersect = layer_intersect.dataProvider().fields().count() lista_ids_campos_eliminar = range(numero_campos_original, numero_campos_intersect) layer_intersect.dataProvider().deleteAttributes(lista_ids_campos_eliminar) layer_intersect.commitChanges() layer_intersect.updateExtents() # QgsMapLayerRegistry.instance().addMapLayer(layer_intersect) QgsMapLayerRegistry.instance().removeMapLayer(outLayer.id()) # Añadimos la feature nueva al listado final self.listado_features_final.extend(layer_intersect.getFeatures()) for feature in self.listado_features_modificadas: outLayer = QgsVectorLayer(inLayerGeometryType + '?crs=' + inLayerCRS, \ u'parcela_modificada_id_' + str(feature.id()), \ 'memory') # changes are only possible when editing the layer outLayer.startEditing() outLayer.dataProvider().addAttributes(inFields.toList()) outLayer.dataProvider().addFeatures([feature]) outLayer.commitChanges() outLayer.updateExtents() QgsMapLayerRegistry.instance().addMapLayer(outLayer) nombre_interseccion = "interseccion_parcela_" + str(feature.id()) interseccion = processing.runalg("qgis:intersection", self.vlayerProcesar, outLayer, "C:\\Temp\\" + nombre_interseccion + ".shp") layer_intersect = QgsVectorLayer(interseccion.get("OUTPUT"), nombre_interseccion, "ogr") layer_intersect.startEditing() # Eliminamos los campos que sobran numero_campos_original = self.vlayerProcesar.dataProvider().fields().count() numero_campos_intersect = layer_intersect.dataProvider().fields().count() lista_ids_campos_eliminar = range(numero_campos_original, numero_campos_intersect) layer_intersect.dataProvider().deleteAttributes(lista_ids_campos_eliminar) layer_intersect.commitChanges() layer_intersect.updateExtents() # QgsMapLayerRegistry.instance().addMapLayer(layer_intersect) QgsMapLayerRegistry.instance().removeMapLayer(outLayer.id()) # Añadimos la feature modificada al listado final self.listado_features_final.extend(layer_intersect.getFeatures()) # Unimos las intersecciones nombre_layer_final = self.vlayerProcesar.name() + "_procesado" layerFinal = QgsVectorLayer(inLayerGeometryType + '?crs=' + inLayerCRS, \ nombre_layer_final, \ 'memory') # Obtenemos la lista de campos campos = self.vlayerProcesar.dataProvider().fields() layerFinal.startEditing() layerFinal.dataProvider().addAttributes(campos.toList()) layerFinal.dataProvider().addFeatures(self.listado_features_final) layerFinal.commitChanges() layerFinal.updateExtents() QgsMapLayerRegistry.instance().addMapLayer(layerFinal) def abrire_archivo(self): hola = "Hola Mundo!" self.dlg.lineEdit.setText(hola) # layers = self.iface.legendInterface().layers() # layer_list = [] # for layer in layers: # layer_list.append(layer.name()) # iter = layer.getFeatures() # for feature in iter: # geom = feature.geometry() # print "Feature ID %d: " % feature.id() # layer = self.iface.activeLayer() # if layer != None: # self.vlayer = layer # self.connect_signals() # iter = layer.getFeatures() # for feature in iter: # # retrieve every feature with its geometry and attributes # # fetch geometry # geom = feature.geometry() # print "Feature ID %d: " % feature.id() # print "Area:", geom.area() # print "Perimeter:", geom.length() # # # show some information about the feature # if geom.type() == QGis.Point: # x = geom.asPoint() # print "Point: " + str(x) # elif geom.type() == QGis.Line: # x = geom.asPolyline() # print "Line: %d points" % len(x) # elif geom.type() == QGis.Polygon: # x = geom.asPolygon() # numPts = 0 # for ring in x: # numPts += len(ring) # print "Polygon: %d rings with %d points" % (len(x), numPts) # else: # print "Unknown" # # # fetch attributes # attrs = feature.attributes() # # else: # self.dlg.lineEdit.setText("Seleccione una capa para editarla") def connect_signals(self): self.vlayer.editingStarted.connect(self.editing_started) self.vlayer.editingStopped.connect(self.editing_stopped) def editing_started(self): # # Disable attributes dialog # QSettings().setValue( # '/qgis/digitizing/disable_enter_attribute_values_dialog', True) self.edit_handler = EditHandler(self.vlayer) def editing_stopped(self): print('Editing stopped') self.listado_features_modificadas = self.edit_handler.get_listado_features_modificadas() self.listado_features_nuevas = self.edit_handler.get_listado_features_nuevas() self.edit_handler.disconnect_committed_signals() self.edit_handler = None def clean_up(self): QgsMapLayerRegistry.instance().removeMapLayer(self.vlayer.id()) self.iface.mapCanvas().clearCache() self.iface.mapCanvas().refresh() def crear_widget(self): self._widget_demo = QWidget() vbox = QGridLayout(self._foglioNotInLavWidget) styleSheet = "QLabel{font: bold italic 'DejaVu Sans'; font:14pt; color: darkGray}" self._foglioNotInLavWidget.setStyleSheet(styleSheet) self.lblFoglioNotInLav1 = QLabel("Selecciona la capa que quieres cortar", self._foglioNotInLavWidget) self.lblFoglioNotInLav2 = QLabel("Selecciona la capa que quieres que se actualice", self._foglioNotInLavWidget) vbox.addWidget(self.lblFoglioNotInLav1, 0, 0, Qt.AlignCenter) vbox.addWidget(self.lblFoglioNotInLav2, 1, 0, Qt.AlignCenter) vbox.addWidget(self.btnApriFoglio, 2, 0, Qt.AlignCenter) vbox.addWidget(self.btnCercaFoglio, 3, 0, Qt.AlignCenter) def crear_dockwidget(self): self.crear_widget() nombre = "Widget de prueba" self.dockWidget = QDockWidget(nombre) self.dockWidget.setObjectName(nombre) self.dockWidget.setWidget(self._widget_demo) self.iface().removeDockWidget(self.dockWidget) self.iface().mainWindow().addDockWidget(Qt.LeftDockWidgetArea, self.dockWidget) self.dockWidget.show() def run(self): """Run method that performs all the real work""" # lista_algoritmos = processing.alglist() # opciones_interseccion = processing.alghelp("qgis:intersection") """ Cargamos el listado de capas en los combobox """ layers = self.iface.legendInterface().layers() layer_list = [] for layer in layers: layer_list.append(layer.name()) self.dlg.comboBox.addItems(layer_list) self.dlg.comboBox_2.addItems(layer_list) """ Mostramos el dockWidget """ self.iface.mainWindow().addDockWidget(Qt.LeftDockWidgetArea, self.dockWidget) self.dockWidget.show() """ Mostramos el dialogo """
new_dock_widget = QDockWidget(u"My doc widget") layout = QVBoxLayout() map_canvas_overview = QgsMapOverviewCanvas( new_dock_widget, iface.mapCanvas() ) layerset = [iface.activeLayer().id()] map_canvas_overview.setLayerSet(layerset) map_canvas_overview.setBackgroundColor(QColor(255, 127, 0)) map_canvas_overview.enableAntiAliasing(True) map_canvas_overview.setMinimumWidth(380) map_canvas_overview.setMinimumHeight(280) new_dock_widget.resize(400, 300) layout.addWidget(map_canvas_overview) new_dock_widget.setLayout(layout) iface.mainWindow().addDockWidget(Qt.RightDockWidgetArea, new_dock_widget) new_dock_widget.show() map_canvas_overview.refresh() # Make the background color disappear? # Layout optional playground layout.setContentsMargins(0, 0, 0, 0) # To clean unuseful reference widgets [iface.mainWindow().removeDockWidget(i) for i in QObject.findChildren(iface.mainWindow(), QDockWidget) if i.windowTitle() == u'My doc widget']
def lauchGUI(self, WorkSpace, aCase, sobjXML, Args): """ mw.dockWidgetBrowser is the Browser of the CFD MainView """ log.debug("lauchGUI") from cs_gui import process_cmd_line if CFD_Code() == CFD_Saturne: from cs_package import package from code_saturne.Base.MainView import MainView elif CFD_Code() == CFD_Neptune: from nc_package import package from neptune_cfd.core.MainView import MainView if sobjXML == None: Title = "unnamed" else: Title = sobjXML.GetName() self.Workspace = WorkSpace pkg = package() case, splash = process_cmd_line(Args) mw = MainView(pkg, case, aCase) # Put the standard panel of the MainView inside a QDockWidget # in the SALOME Desktop aTitle = self.setWindowTitle_CFD(mw, aCase, Title) dsk = sgPyQt.getDesktop() dock = QDockWidget(aTitle) dock.setWidget(mw.frame) dock.setMinimumWidth(520) dsk.addDockWidget(Qt.RightDockWidgetArea, dock) dock.setVisible(True) dock.show() # Put the QTreeView of the MainView which is already inside a QDockWidget # in the SALOME Desktop BrowserTitle = aTitle + " Browser" mw.dockWidgetBrowser.setWindowTitle(BrowserTitle) dsk.addDockWidget(Qt.LeftDockWidgetArea, mw.dockWidgetBrowser) mw.dockWidgetBrowser.setVisible(True) mw.dockWidgetBrowser.show() mw.dockWidgetBrowser.raise_() dock.raise_() #Add Dock windows are managed by CFDGUI_Management class studyId = sgPyQt.getStudyId() aStudyCFD = aCase.GetFather() aCaseCFD = aCase xmlFileName = str(Title) _c_CFDGUI.set_d_CfdCases(studyId, dock, mw.dockWidgetBrowser, mw, aStudyCFD, aCaseCFD, xmlFileName, sobjXML) self.connect(dock, SIGNAL("visibilityChanged(bool)"), self.setdockWindowBrowserActivated) self.connect(mw.dockWidgetBrowser, SIGNAL("visibilityChanged(bool)"), self.setdockWindowActivated) self.connect(dock.toggleViewAction(), SIGNAL("toggled(bool)"), self.setdockWB) self.connect(mw.dockWidgetBrowser.toggleViewAction(), SIGNAL("toggled(bool)"), self.setdock) _c_CFDGUI.tabifyDockWindows(dsk, studyId) self.showDockWindows(studyId, xmlFileName, aCaseCFD.GetName(), aStudyCFD.GetName()) updateObjectBrowser() return mw
def lauchGUI(self, WorkSpace, aCase, sobjXML, Args): """ mw.dockWidgetBrowser is the Browser of the CFD MainView """ log.debug("lauchGUI") from cs_gui import process_cmd_line if CFD_Code() == CFD_Saturne: from cs_package import package from code_saturne.Base.MainView import MainView elif CFD_Code() == CFD_Neptune: from nc_package import package from neptune_cfd.core.MainView import MainView if sobjXML == None: Title = "unnamed" else: Title = sobjXML.GetName() self.Workspace = WorkSpace pkg = package() case, splash = process_cmd_line(Args) mw = MainView(pkg, case, aCase) # Put the standard panel of the MainView inside a QDockWidget # in the SALOME Desktop aTitle = self.setWindowTitle_CFD(mw, aCase, Title) dsk = sgPyQt.getDesktop() dock = QDockWidget(aTitle) dock.setWidget(mw.frame) dock.setMinimumWidth(520) dsk.addDockWidget(Qt.RightDockWidgetArea, dock) dock.setVisible(True) dock.show() # Put the QTreeView of the MainView which is already inside a QDockWidget # in the SALOME Desktop BrowserTitle = aTitle + " Browser" mw.dockWidgetBrowser.setWindowTitle(BrowserTitle) dsk.addDockWidget(Qt.LeftDockWidgetArea, mw.dockWidgetBrowser) mw.dockWidgetBrowser.setVisible(True) mw.dockWidgetBrowser.show() mw.dockWidgetBrowser.raise_() dock.raise_() #Add Dock windows are managed by CFDGUI_Management class studyId = sgPyQt.getStudyId() aStudyCFD = aCase.GetFather() aCaseCFD = aCase xmlFileName = str(Title) _c_CFDGUI.set_d_CfdCases(studyId, dock, mw.dockWidgetBrowser, mw, aStudyCFD, aCaseCFD, xmlFileName, sobjXML) self.connect(dock, SIGNAL("visibilityChanged(bool)"), self.setdockWindowBrowserActivated) self.connect(mw.dockWidgetBrowser, SIGNAL("visibilityChanged(bool)"),self.setdockWindowActivated) self.connect(dock.toggleViewAction(), SIGNAL("toggled(bool)"), self.setdockWB) self.connect(mw.dockWidgetBrowser.toggleViewAction(), SIGNAL("toggled(bool)"), self.setdock) _c_CFDGUI.tabifyDockWindows(dsk, studyId) self.showDockWindows(studyId, xmlFileName, aCaseCFD.GetName(), aStudyCFD.GetName()) updateObjectBrowser() return mw
class MainWindow(QMainWindow, Ui_MainWindow): """The Main window of Luma. """ logger = logging.getLogger(__name__) languages = {} translator = None languageHandler = None currentLanguage = '' def __init__(self, parent=None): """The constructor sets up the MainWindow widget, and connects all necessary signals and slots """ super(MainWindow, self).__init__(parent) self.setupUi(self) # We store the window size to make sure the previous window size # is restored when leaving fullscreen mode. This varible is used # in the toggleFullscreen slot. self.__tmpWinSize = self.size() self.eventFilter = LumaEventFilter(self) self.mainTabs.installEventFilter(self.eventFilter) self.translator = QTranslator() self.languageHandler = LanguageHandler() self.languages = self.languageHandler.availableLanguages #self.__createPluginToolBar() self.__createLoggerWidget() self.__loadSettings() self.__createLanguageOptions() self.setStatusBar(self.statusBar) self.mainTabs.setTabsClosable(True) self.mainTabs.setContextMenuPolicy(Qt.CustomContextMenu) self.mainTabs.customContextMenuRequested.connect( self.__mainTabsContextMenu) self.defaultTabStyle = '' self.lumaHeadStyle = 'background: url(:/icons/luma-gray);\n' + \ 'background-position: bottom right;\n' + \ 'background-attachment: fixed;\n' + \ 'background-repeat: no-repeat;' #Sets up pluginWidget #self in parameter is used to call pluginSelected here... self.pluginWidget = PluginListWidget(self) self.showPlugins() self.welcomeTab = WelcomeTab() self.welcomeTab.textBrowser.setStyleSheet(self.lumaHeadStyle) #This value comes from __loadSettings() #Its a checkbox set in WelcomeTab if self.showWelcomeSettings == 2: self.showWelcome() else: # Let's do some styling of the tab widget when no tabs are opened if self.mainTabs.currentIndex() == -1: self.__setTabWidgetStyle(self.lumaHeadStyle) self.actionShowWelcomeTab.setEnabled(True) self.serversChangedMessage = QErrorMessage(self) def __mainTabsContextMenu(self, pos): menu = QMenu() if self.mainTabs.count() > 0: return # The menu is displayed even when the rightclick is not # done over the actual tabs so to avoid confusion the # function is disabled entirey #menu.addAction(QApplication.translate( # "MainWindow", "Close all plugin-tabs"), self.tabCloseAll) else: # If there's no tabs, offer to display the pluginlist menu.addAction(self.actionShowPluginList) menu.exec_(self.mainTabs.mapToGlobal(pos)) def __createPluginToolBar(self): """Creates the pluign toolbar. """ self.pluginToolBar = PluginToolBar(self) self.pluginToolBar.setWindowTitle( QApplication.translate('MainWindow', 'Plugintoolbar', None, QApplication.UnicodeUTF8)) self.pluginToolBar.setObjectName('pluginToolBar') self.addToolBar(self.pluginToolBar) self.pluginToolBar.hide() def __createLoggerWidget(self): """Creates the logger widget. """ self.loggerDockWindow = QDockWidget(self) self.loggerDockWindow.setObjectName('loggerDockWindow') self.loggerDockWindow.visibilityChanged[bool].connect( self.actionShowLogger.setChecked) self.loggerDockWindow.setWindowTitle( QApplication.translate('MainWindow', 'Logger', None, QApplication.UnicodeUTF8)) self.loggerWidget = LoggerWidget(self.loggerDockWindow) self.loggerDockWindow.setWidget(self.loggerWidget) self.addDockWidget(Qt.BottomDockWidgetArea, self.loggerDockWindow) self.loggerDockWindow.hide() def __createLanguageOptions(self): """Creates the language selection in the menubar. """ self.langGroup = QActionGroup(self) self.langGroup.setExclusive(True) self.langGroup.triggered['QAction*'].connect(self.languageChanged) for key, name in self.languages.iteritems(): action = QAction(self) action.setCheckable(True) action.setData(key) action.setText(name[0]) action.setStatusTip(name[1]) action.setActionGroup(self.langGroup) self.menuLanguage.addAction(action) if key == self.currentLanguage: action.setChecked(True) def __loadSettings(self, mainWin=True): """Loads settings from file. :param mainWin: If set to ``False``, neither the values for the window size or the window position will be loaded. This is i.e done when the settings dialog returns 1. :type mainWin: bool """ settings = Settings() # We might want to use these methods to restore the # application state and geometry. if mainWin: self.restoreGeometry(settings.geometry) #self.restoreState(settings.state) # If the geometry saved inticates fullscreen mode, # we need to explicitly set the fullscreen menuaction checkbox if self.isFullScreen(): self.actionFullscreen.setChecked(True) # Logger # Logger # The `allwaysShowLoggerOnStart` a precedence on the # `showLogger` value. if settings.showLoggerOnStart: self.actionShowLogger.setChecked(True) else: self.actionShowLogger.setChecked(settings.showLogger) self.loggerWidget.errorBox.setChecked(settings.showErrors) self.loggerWidget.debugBox.setChecked(settings.showDebug) self.loggerWidget.infoBox.setChecked(settings.showInfo) self.toggleLoggerWindow(self.actionShowLogger.isChecked()) # Language self.loadLanguage(settings.language) #Tabs self.showWelcomeSettings = settings.value("showWelcome", 2).toInt()[0] def __writeSettings(self): """Save settings to file. """ settings = Settings() # We might want to use these methods to restore the # application state and geometry. settings.geometry = self.saveGeometry() #settings.state = self.saveState() # Mainwin #max = self.isMaximized() #settings.maximize = max #if not max: # settings.size = self.size() # settings.position = self.pos() # The global logger settings is managed from the settings dialog. # Logger settings.showLogger = self.actionShowLogger.isChecked() settings.showErrors = self.loggerWidget.errorBox.isChecked() settings.showDebug = self.loggerWidget.debugBox.isChecked() settings.showInfo = self.loggerWidget.infoBox.isChecked() # Language settings.language = self.currentLanguage def __switchTranslator(self, translator, qmFile): """Called when a new language is loaded. :param translator: The translator object to install. :type translator: QTranslator :qmFile: The translation file for the loaded language. :type qmFile: string """ qApp.removeTranslator(translator) if translator.load(qmFile): qApp.installTranslator(translator) def __setTabWidgetStyle(self, stylesheet): self.mainTabs.setStyleSheet(stylesheet) @pyqtSlot('QAction*') @pyqtSlot(int) def languageChanged(self, value): """This slot is called by actions and signals related to application translations. The slot contains validation for those parameters defined by the pyqtSlot meta info in the method header. :param value: Can be either a ``QAction`` or an integer value. I.e. menu actions provide ``QActions`` but a ``QCombobox`` might send it's index. :type value: QAction/int """ locale = None if isinstance(value, int): locale = self.languageSelector.itemData(value).toString() elif isinstance(value, QAction): locale = value.data().toString() #else: # locale = value if locale: self.loadLanguage(locale) def loadLanguage(self, locale): """Loads a language by the given language iso code. :param locale: A twoletter lowercase ISO 639 language code and possibly a twoletter uppercase ISO 3166 country code separeted by a underscore. :type locale: string """ if self.currentLanguage != locale: self.currentLanguage = locale qmFile = self.languageHandler.getQmFile(locale) self.__switchTranslator(self.translator, qmFile) def changeEvent(self, event): """This event is called when a new translator is loaded or the system language (locale) is changed. :param event: The event that generated the `changeEvent`. :type event: QEvent """ if None != event: type = event.type() if QEvent.LanguageChange == type or QEvent.LocaleChange == type: self.retranslateUi(self) self.loggerWidget.retranslateUi(self.loggerWidget) def showAboutLuma(self): """Slot for displaying the about dialog. """ AboutDialog().exec_() @pyqtSlot(bool) def toggleLoggerWindow(self, show): """Slot for toggling the logger window. :param show: a boolean value indicating whether the logger window should be shown or not. :type show: bool """ if show: self.loggerDockWindow.show() else: self.loggerDockWindow.hide() @pyqtSlot(bool) def toggleStatusbar(self, show): """Slot for toggling the logger window. :param show: a boolean value indicating whether the statusbar should be shown or not. :type show: bool """ if show: self.statusBar.show() else: self.statusBar.hide() @pyqtSlot(bool) def toggleFullscreen(self, fullscreen): """Slot for toggling the logger window. :param fullscreen: a boolean value indicating whether to enter fullscreenmode or not. :type fullcreen: bool """ if fullscreen: self.__tmpWinSize = self.size() self.showFullScreen() else: self.showNormal() self.resize(self.__tmpWinSize) def showServerEditor(self): """Slot to display the server editor dialog. """ serverEditor = ServerDialog() r = serverEditor.exec_() if r: #TODO -- only display if plugins open: self.serversChangedMessage.showMessage( QApplication.translate( "MainWindow", "You may need to restart plugins for changes to take effect." )) def showTempPasswordDialog(self): """ Sets overridePassword for a server. Using this one doesn't actually have to enter the password in the ServerDialog (and by extension save to disk). """ serverList = ServerList() # Create a stringlist to be used by the qinputdialog stringList = [] for server in serverList.getTable(): stringList.append(server.name) # Display list of servers (serverString, ok) = QInputDialog.getItem( self, QApplication.translate("MainWindow", "Select server"), QApplication.translate("MainWindow", "Server:"), stringList, editable=False) if ok: server = serverList.getServerObjectByName(serverString) if server != None: # Ask for password (value, ok) = QInputDialog.getText( self, QApplication.translate("MainWindow", "Temporary password"), QApplication.translate("MainWindow", "Enter password:"******"""Slot to display the settings dialog. If the settings dialog returns 1, i.e. the user has clicked the ok button, the loadSettings method is called with mainWin=False, to load the (assumed) newly changed settings. :param tab: The index of the tab to display in the settings dialog. :type tab: int """ #settingsDialog = SettingsDialog(self.currentLanguage, self.languages) settingsDialog = SettingsDialog() if tab < 0: tab = 0 settingsDialog.tabWidget.setCurrentIndex(tab) if settingsDialog.exec_(): self.reloadPlugins() # # We assume that some settings is changed # # if the user clicked the ok button, and # # reloads the application settings # self.__loadSettings(mainWin=False) # # A Hack but it'll do for now # for a in self.langGroup.actions(): # if a.data().toString() == self.currentLanguage: # a.setChecked(True) def configurePlugins(self): """Slot to display the plugins configuration. This currently calls `showSettingsDialog` with tab index set to 2. """ self.showSettingsDialog(1) def reloadPlugins(self): """Slot to reload plugins. """ self.pluginWidget.updatePlugins() def pluginSelected(self, item): """This method will be called from the `PluginListWidget`. """ # Clear the stylesheet when a tab is opened self.__setTabWidgetStyle(self.defaultTabStyle) widget = item.plugin.getPluginWidget(None, self) if platform.system() == "Windows": scroll = QScrollArea() scroll.setWidget(widget) scroll.setWidgetResizable(True) index = self.mainTabs.addTab(scroll, item.icon(), item.plugin.pluginUserString) else: index = self.mainTabs.addTab(widget, item.icon(), item.plugin.pluginUserString) self.mainTabs.setCurrentIndex(index) def tabClose(self, index): """Slot for the signal `tabCloseRequest(int)` for the tabMains. """ widget = self.mainTabs.widget(index) # If the tab closed is one of these, enable the toggle-action if widget == self.pluginWidget: self.actionShowPluginList.setEnabled(True) if widget == self.welcomeTab: self.actionShowWelcomeTab.setEnabled(True) self.mainTabs.removeTab(index) # Unparent the widget since it was reparented by the QTabWidget # so it's garbage collected widget.setParent(None) # In case the widget contained circular references # -- force GC to take care of the objects since there can be # quite many if it was BrowserWidget that was closed. # Can't call it directly since that'll be too soon QTimer.singleShot(1000, self.gc) # Let's do some styling of the tab widget when no tabs are opened if self.mainTabs.currentIndex() == -1: self.__setTabWidgetStyle(self.lumaHeadStyle) def gc(self): """Runs Python's garbage-collection manually. Used to make sure circular references are taken care of *now*. """ gc.collect() def showWelcome(self): """Shows the Welcome-tab """ self.__setTabWidgetStyle(self.defaultTabStyle) index = self.mainTabs.addTab( self.welcomeTab, QApplication.translate("MainWindow", "Welcome")) self.mainTabs.setCurrentIndex(index) self.actionShowWelcomeTab.setEnabled(False) def showPlugins(self): """Will show the pluginlistwidget-tab """ self.__setTabWidgetStyle(self.defaultTabStyle) if self.mainTabs.indexOf(self.pluginWidget) == -1: index = self.mainTabs.addTab( self.pluginWidget, QApplication.translate("MainWindow", "Plugins")) self.mainTabs.setCurrentIndex(index) self.actionShowPluginList.setEnabled(False) def closeEvent(self, e): """Overrides the ``QMainWindow.closeEvent`` slot to save settings before we tear down the application. """ self.__writeSettings() QMainWindow.closeEvent(self, e)
class gazetteerSearch: def __init__(self, iface): self.dock = None self.results = [] self.activeIndex = None # Save reference to the QGIS interface self.iface = iface self.iface.newProjectCreated.connect(self._hideMarker) self.iface.projectRead.connect(self._hideMarker) self.canvas = self.iface.mapCanvas() # Create the dialog and keep reference self.widget = gazetteerSearchDialog() self.widget.runSearch.connect(self.runSearch) self.widget.ui.clearButton.pressed.connect(self.clearResults) self.widget.zoomRequested.connect(self.zoomTo) self.widget.changeRequested.connect(self.changeSelected) # initialize plugin directory self.plugin_dir = QFileInfo(QgsApplication.qgisUserDbFilePath()).path() + "/python/plugins/SANBI_Gazetteer" self.widget.plugin_dir = self.plugin_dir # initialize locale localePath = "" locale = QSettings().value("locale/userLocale").toString()[0:2] if QFileInfo(self.plugin_dir).exists(): localePath = self.plugin_dir + "/i18n/gazetteersearch_" + locale + ".qm" if QFileInfo(localePath).exists(): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) def initGui(self): # Create action that will start plugin configuration self.action = QAction(QIcon(self.plugin_dir + "/icon.svg"), \ u"Gazetteer Search", self.iface.mainWindow()) # connect the action to the run method self.action.triggered.connect(self.run) # Add toolbar button and menu item self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu(u"&Gazetteer Search", self.action) def unload(self): # Remove the plugin menu item and icon self.iface.removePluginMenu(u"&Gazetteer Search",self.action) self.iface.removeToolBarIcon(self.action) for res in self.results: res.unload() def _hideMarker(self): for res in self.results: res.visible = False # run method that performs all the real work def run(self): if not self.dock: self.dock = QDockWidget("Gazetteer Search", self.iface.mainWindow()) self.dock.setWidget(self.widget) self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dock) self.gazetteers = common.getGazetteers() for gazetter in self.gazetteers.iterkeys(): self.widget.addGazetter(gazetter) if len(self.gazetteers) == 1: self.widget.hideGazetteers() else: self.dock.show() def runSearch(self, searchString, selectedGazetteer): self.clearResults() gazetteer_config = self.gazetteers[str(selectedGazetteer)] gazetteer = self.getGazetteerModule(gazetteer_config) url = common.prepareURL(gazetteer.url, gazetteer.params, searchString) try: data = common.search(url) except URLError: self.results = [] self.widget.addError('Problem connecting to "%s"' % selectedGazetteer) else: try: results = list(gazetteer.parseRequestResults(data)) except ValueError: self.results = [] if len(results) == 0: self.widget.addError('No results found for "%s"' % searchString) else: for res in results: r = Result(self.iface, res.description, res.x, res.y, res.zoom, res.epsg) self.widget.addResult(r.description) r.index = self.widget.getListCount()-1 r.visible = True self.results.append(r) def clearResults(self): self.widget.clearResults() for res in self.results: res.unload() self.results = [] self.activeIndex=None def getGazetteerModule(self, config): gazetteer_module = config['gazetteer'] imported_gazetteer = import_module('gazetteers.%s' % gazetteer_module) return imported_gazetteer def zoomTo(self, row): self.results[row-1].zoomTo() def changeSelected(self, row): if self.activeIndex is not None: self.results[self.activeIndex].active = False try: self.results[row-1].active = True self.activeIndex = row-1 except IndexError: pass
class MainWindow(QMainWindow, Ui_MainWindow): """The Main window of Luma. """ logger = logging.getLogger(__name__) languages = {} translator = None languageHandler = None currentLanguage = '' def __init__(self, parent=None): """The constructor sets up the MainWindow widget, and connects all necessary signals and slots """ super(MainWindow, self).__init__(parent) self.setupUi(self) # We store the window size to make sure the previous window size # is restored when leaving fullscreen mode. This varible is used # in the toggleFullscreen slot. self.__tmpWinSize = self.size() self.eventFilter = LumaEventFilter(self) self.mainTabs.installEventFilter(self.eventFilter) self.translator = QTranslator() self.languageHandler = LanguageHandler() self.languages = self.languageHandler.availableLanguages #self.__createPluginToolBar() self.__createLoggerWidget() self.__loadSettings() self.__createLanguageOptions() self.setStatusBar(self.statusBar) self.mainTabs.setTabsClosable(True) self.mainTabs.setContextMenuPolicy(Qt.CustomContextMenu) self.mainTabs.customContextMenuRequested.connect( self.__mainTabsContextMenu) self.defaultTabStyle = '' self.lumaHeadStyle = 'background: url(:/icons/luma-gray);\n' + \ 'background-position: bottom right;\n' + \ 'background-attachment: fixed;\n' + \ 'background-repeat: no-repeat;' #Sets up pluginWidget #self in parameter is used to call pluginSelected here... self.pluginWidget = PluginListWidget(self) self.showPlugins() self.welcomeTab = WelcomeTab() self.welcomeTab.textBrowser.setStyleSheet(self.lumaHeadStyle) #This value comes from __loadSettings() #Its a checkbox set in WelcomeTab if self.showWelcomeSettings == 2: self.showWelcome() else: # Let's do some styling of the tab widget when no tabs are opened if self.mainTabs.currentIndex() == -1: self.__setTabWidgetStyle(self.lumaHeadStyle) self.actionShowWelcomeTab.setEnabled(True) self.serversChangedMessage = QErrorMessage(self) def __mainTabsContextMenu(self, pos): menu = QMenu() if self.mainTabs.count() > 0: return # The menu is displayed even when the rightclick is not # done over the actual tabs so to avoid confusion the # function is disabled entirey #menu.addAction(QApplication.translate( # "MainWindow", "Close all plugin-tabs"), self.tabCloseAll) else: # If there's no tabs, offer to display the pluginlist menu.addAction(self.actionShowPluginList) menu.exec_(self.mainTabs.mapToGlobal(pos)) def __createPluginToolBar(self): """Creates the pluign toolbar. """ self.pluginToolBar = PluginToolBar(self) self.pluginToolBar.setWindowTitle(QApplication.translate( 'MainWindow', 'Plugintoolbar', None, QApplication.UnicodeUTF8)) self.pluginToolBar.setObjectName('pluginToolBar') self.addToolBar(self.pluginToolBar) self.pluginToolBar.hide() def __createLoggerWidget(self): """Creates the logger widget. """ self.loggerDockWindow = QDockWidget(self) self.loggerDockWindow.setObjectName('loggerDockWindow') self.loggerDockWindow.visibilityChanged[bool].connect( self.actionShowLogger.setChecked) self.loggerDockWindow.setWindowTitle(QApplication.translate( 'MainWindow', 'Logger', None, QApplication.UnicodeUTF8)) self.loggerWidget = LoggerWidget(self.loggerDockWindow) self.loggerDockWindow.setWidget(self.loggerWidget) self.addDockWidget(Qt.BottomDockWidgetArea, self.loggerDockWindow) self.loggerDockWindow.hide() def __createLanguageOptions(self): """Creates the language selection in the menubar. """ self.langGroup = QActionGroup(self) self.langGroup.setExclusive(True) self.langGroup.triggered['QAction*'].connect(self.languageChanged) for key, name in self.languages.iteritems(): action = QAction(self) action.setCheckable(True) action.setData(key) action.setText(name[0]) action.setStatusTip(name[1]) action.setActionGroup(self.langGroup) self.menuLanguage.addAction(action) if key == self.currentLanguage: action.setChecked(True) def __loadSettings(self, mainWin=True): """Loads settings from file. :param mainWin: If set to ``False``, neither the values for the window size or the window position will be loaded. This is i.e done when the settings dialog returns 1. :type mainWin: bool """ settings = Settings() # We might want to use these methods to restore the # application state and geometry. if mainWin: self.restoreGeometry(settings.geometry) #self.restoreState(settings.state) # If the geometry saved inticates fullscreen mode, # we need to explicitly set the fullscreen menuaction checkbox if self.isFullScreen(): self.actionFullscreen.setChecked(True) # Logger # Logger # The `allwaysShowLoggerOnStart` a precedence on the # `showLogger` value. if settings.showLoggerOnStart: self.actionShowLogger.setChecked(True) else: self.actionShowLogger.setChecked(settings.showLogger) self.loggerWidget.errorBox.setChecked(settings.showErrors) self.loggerWidget.debugBox.setChecked(settings.showDebug) self.loggerWidget.infoBox.setChecked(settings.showInfo) self.toggleLoggerWindow(self.actionShowLogger.isChecked()) # Language self.loadLanguage(settings.language) #Tabs self.showWelcomeSettings = settings.value("showWelcome", 2).toInt()[0] def __writeSettings(self): """Save settings to file. """ settings = Settings() # We might want to use these methods to restore the # application state and geometry. settings.geometry = self.saveGeometry() #settings.state = self.saveState() # Mainwin #max = self.isMaximized() #settings.maximize = max #if not max: # settings.size = self.size() # settings.position = self.pos() # The global logger settings is managed from the settings dialog. # Logger settings.showLogger = self.actionShowLogger.isChecked() settings.showErrors = self.loggerWidget.errorBox.isChecked() settings.showDebug = self.loggerWidget.debugBox.isChecked() settings.showInfo = self.loggerWidget.infoBox.isChecked() # Language settings.language = self.currentLanguage def __switchTranslator(self, translator, qmFile): """Called when a new language is loaded. :param translator: The translator object to install. :type translator: QTranslator :qmFile: The translation file for the loaded language. :type qmFile: string """ qApp.removeTranslator(translator) if translator.load(qmFile): qApp.installTranslator(translator) def __setTabWidgetStyle(self, stylesheet): self.mainTabs.setStyleSheet(stylesheet) @pyqtSlot('QAction*') @pyqtSlot(int) def languageChanged(self, value): """This slot is called by actions and signals related to application translations. The slot contains validation for those parameters defined by the pyqtSlot meta info in the method header. :param value: Can be either a ``QAction`` or an integer value. I.e. menu actions provide ``QActions`` but a ``QCombobox`` might send it's index. :type value: QAction/int """ locale = None if isinstance(value, int): locale = self.languageSelector.itemData(value).toString() elif isinstance(value, QAction): locale = value.data().toString() #else: # locale = value if locale: self.loadLanguage(locale) def loadLanguage(self, locale): """Loads a language by the given language iso code. :param locale: A twoletter lowercase ISO 639 language code and possibly a twoletter uppercase ISO 3166 country code separeted by a underscore. :type locale: string """ if self.currentLanguage != locale: self.currentLanguage = locale qmFile = self.languageHandler.getQmFile(locale) self.__switchTranslator(self.translator, qmFile) def changeEvent(self, event): """This event is called when a new translator is loaded or the system language (locale) is changed. :param event: The event that generated the `changeEvent`. :type event: QEvent """ if None != event: type = event.type() if QEvent.LanguageChange == type or QEvent.LocaleChange == type: self.retranslateUi(self) self.loggerWidget.retranslateUi(self.loggerWidget) def showAboutLuma(self): """Slot for displaying the about dialog. """ AboutDialog().exec_() @pyqtSlot(bool) def toggleLoggerWindow(self, show): """Slot for toggling the logger window. :param show: a boolean value indicating whether the logger window should be shown or not. :type show: bool """ if show: self.loggerDockWindow.show() else: self.loggerDockWindow.hide() @pyqtSlot(bool) def toggleStatusbar(self, show): """Slot for toggling the logger window. :param show: a boolean value indicating whether the statusbar should be shown or not. :type show: bool """ if show: self.statusBar.show() else: self.statusBar.hide() @pyqtSlot(bool) def toggleFullscreen(self, fullscreen): """Slot for toggling the logger window. :param fullscreen: a boolean value indicating whether to enter fullscreenmode or not. :type fullcreen: bool """ if fullscreen: self.__tmpWinSize = self.size() self.showFullScreen() else: self.showNormal() self.resize(self.__tmpWinSize) def showServerEditor(self): """Slot to display the server editor dialog. """ serverEditor = ServerDialog() r = serverEditor.exec_() if r: #TODO -- only display if plugins open: self.serversChangedMessage.showMessage(QApplication.translate( "MainWindow", "You may need to restart plugins for changes to take effect.")) def showTempPasswordDialog(self): """ Sets overridePassword for a server. Using this one doesn't actually have to enter the password in the ServerDialog (and by extension save to disk). """ serverList = ServerList() # Create a stringlist to be used by the qinputdialog stringList = [] for server in serverList.getTable(): stringList.append(server.name) # Display list of servers (serverString, ok) = QInputDialog.getItem( self, QApplication.translate("MainWindow", "Select server"), QApplication.translate("MainWindow", "Server:"), stringList, editable=False ) if ok: server = serverList.getServerObjectByName(serverString) if server != None: # Ask for password (value, ok) = QInputDialog.getText( self, QApplication.translate("MainWindow", "Temporary password"), QApplication.translate("MainWindow", "Enter password:"******"""Slot to display the settings dialog. If the settings dialog returns 1, i.e. the user has clicked the ok button, the loadSettings method is called with mainWin=False, to load the (assumed) newly changed settings. :param tab: The index of the tab to display in the settings dialog. :type tab: int """ #settingsDialog = SettingsDialog(self.currentLanguage, self.languages) settingsDialog = SettingsDialog() if tab < 0: tab = 0 settingsDialog.tabWidget.setCurrentIndex(tab) if settingsDialog.exec_(): self.reloadPlugins() # # We assume that some settings is changed # # if the user clicked the ok button, and # # reloads the application settings # self.__loadSettings(mainWin=False) # # A Hack but it'll do for now # for a in self.langGroup.actions(): # if a.data().toString() == self.currentLanguage: # a.setChecked(True) def configurePlugins(self): """Slot to display the plugins configuration. This currently calls `showSettingsDialog` with tab index set to 2. """ self.showSettingsDialog(1) def reloadPlugins(self): """Slot to reload plugins. """ self.pluginWidget.updatePlugins() def pluginSelected(self, item): """This method will be called from the `PluginListWidget`. """ # Clear the stylesheet when a tab is opened self.__setTabWidgetStyle(self.defaultTabStyle) widget = item.plugin.getPluginWidget(None, self) if platform.system() == "Windows": scroll = QScrollArea() scroll.setWidget(widget) scroll.setWidgetResizable(True) index = self.mainTabs.addTab( scroll, item.icon(), item.plugin.pluginUserString) else: index = self.mainTabs.addTab( widget, item.icon(), item.plugin.pluginUserString) self.mainTabs.setCurrentIndex(index) def tabClose(self, index): """Slot for the signal `tabCloseRequest(int)` for the tabMains. """ widget = self.mainTabs.widget(index) # If the tab closed is one of these, enable the toggle-action if widget == self.pluginWidget: self.actionShowPluginList.setEnabled(True) if widget == self.welcomeTab: self.actionShowWelcomeTab.setEnabled(True) self.mainTabs.removeTab(index) # Unparent the widget since it was reparented by the QTabWidget # so it's garbage collected widget.setParent(None) # In case the widget contained circular references # -- force GC to take care of the objects since there can be # quite many if it was BrowserWidget that was closed. # Can't call it directly since that'll be too soon QTimer.singleShot(1000, self.gc) # Let's do some styling of the tab widget when no tabs are opened if self.mainTabs.currentIndex() == -1: self.__setTabWidgetStyle(self.lumaHeadStyle) def gc(self): """Runs Python's garbage-collection manually. Used to make sure circular references are taken care of *now*. """ gc.collect() def showWelcome(self): """Shows the Welcome-tab """ self.__setTabWidgetStyle(self.defaultTabStyle) index = self.mainTabs.addTab(self.welcomeTab, QApplication.translate("MainWindow", "Welcome")) self.mainTabs.setCurrentIndex(index) self.actionShowWelcomeTab.setEnabled(False) def showPlugins(self): """Will show the pluginlistwidget-tab """ self.__setTabWidgetStyle(self.defaultTabStyle) if self.mainTabs.indexOf(self.pluginWidget) == -1: index = self.mainTabs.addTab( self.pluginWidget, QApplication.translate( "MainWindow", "Plugins")) self.mainTabs.setCurrentIndex(index) self.actionShowPluginList.setEnabled(False) def closeEvent(self, e): """Overrides the ``QMainWindow.closeEvent`` slot to save settings before we tear down the application. """ self.__writeSettings() QMainWindow.closeEvent(self, e)
class ComposerWrapper(QObject): """ Embeds custom STDM tools in a QgsComposer instance for managing map-based STDM document templates. """ dataSourceSelected = pyqtSignal(str) def __init__(self,composerView): QObject.__init__(self,composerView) self._compView = composerView self._stdmTB = self.mainWindow().addToolBar("STDM") self._selectMoveAction = None #Container for custom editor widgets self._widgetMappings = {} #Create dock widget for configuring STDM data source self._stdmDataSourceDock = QDockWidget(QApplication.translate("ComposerWrapper","STDM Data Source"),self.mainWindow()) self._stdmDataSourceDock.setObjectName("STDMDataSourceDock") self._stdmDataSourceDock.setMinimumWidth(300) self._stdmDataSourceDock.setFeatures(QDockWidget.DockWidgetMovable|QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea,self._stdmDataSourceDock) dataSourceWidget = ComposerDataSourceSelector() self._stdmDataSourceDock.setWidget(dataSourceWidget) self._stdmDataSourceDock.show() #Create dock widget for configuring STDM item properties self._stdmItemPropDock = QDockWidget(QApplication.translate("ComposerWrapper","STDM data properties"),self.mainWindow()) self._stdmItemPropDock.setObjectName("STDMItemDock") self._stdmItemPropDock.setMinimumWidth(300) self._stdmItemPropDock.setFeatures(QDockWidget.DockWidgetMovable|QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea,self._stdmItemPropDock) self._stdmItemPropDock.show() if self.itemDock() != None: self.mainWindow().tabifyDockWidget(self.itemDock(),self._stdmItemPropDock) if self.atlasDock() != None: self.atlasDock().hide() if self.generalDock() != None: self.generalDock().raise_() #Connect signals self.composition().itemRemoved.connect(self._onItemRemoved) dataSourceWidget.cboDataSource.currentIndexChanged[str].connect(self.propagateDataSourceSelection) self.composerView().selectedItemChanged.connect(self._onItemSelected) #Current template document file self._currDocFile = None def _removeActions(self): """ Remove inapplicable actions and their corresponding toolbars and menus. """ removeActions = ["mActionSaveProject","mActionNewComposer","mActionDuplicateComposer"] composerToolbar = self.composerMainToolBar() if composerToolbar != None: saveProjectAction = None for itemAction in composerToolbar.actions(): if itemAction.objectName() == "mActionSaveProject": saveProjectAction = itemAction break if saveProjectAction != None: composerMenu = saveProjectAction.menu() def configure(self): #Create instances of custom STDM composer item configurations for ciConfig in ComposerItemConfig.itemConfigurations: ciConfigObj = ciConfig(self) def addWidgetMapping(self,uniqueIdentifier,widget): """ Add custom STDM editor widget based on the unique identifier of the composer item """ self._widgetMappings[uniqueIdentifier] = widget def widgetMappings(self): """ Returns a dictionary containing uuid values of composer items linked to STDM widgets. """ return self._widgetMappings def clearWidgetMappings(self): """ Resets the widget mappings collection. """ self._widgetMappings = {} def mainWindow(self): """ Returns the QMainWindow used by the composer view. """ return self._compView.composerWindow() def stdmToolBar(self): """ Returns the instance of the STDM toolbar added to the QgsComposer. """ return self._stdmTB def composerView(self): """ Returns the composer view. """ return self._compView def composition(self): """ Returns the QgsComposition instance used in the composer view. """ return self._compView.composition() def composerItemToolBar(self): """ Returns the toolbar containing actions for adding composer items. """ return self.mainWindow().findChild(QToolBar,"mItemToolbar") def composerMainToolBar(self): """ Returns the toolbar containing actions for managing templates. """ return self.mainWindow().findChild(QToolBar,"mComposerToolbar") def selectMoveAction(self): """ Returns the QAction for selecting or moving composer items. """ if self.composerItemToolBar() != None: if self._selectMoveAction == None: for itemAction in self.composerItemToolBar().actions(): if itemAction.objectName() == "mActionSelectMoveItem": self._selectMoveAction = itemAction break return self._selectMoveAction def checkedItemAction(self): """ Returns the currently selected composer item action. """ if self.selectMoveAction() != None: return self.selectMoveAction().actionGroup().checkedAction() return None def itemDock(self): """ Get the 'Item Properties' dock widget. """ return self.mainWindow().findChild(QDockWidget,"ItemDock") def atlasDock(self): """ Get the 'Atlas generation' dock widget. """ return self.mainWindow().findChild(QDockWidget,"AtlasDock") def generalDock(self): """ Get the 'Composition' dock widget. """ return self.mainWindow().findChild(QDockWidget,"CompositionDock") def stdmDataSourceDock(self): """ Returns the STDM data source dock widget. """ return self._stdmDataSourceDock def stdmItemDock(self): """ Returns the STDM item dock widget. """ return self._stdmItemPropDock def documentFile(self): """ Returns the QFile instance associated with the current document. 'None' will be returned for new, unsaved documents. """ return self._currDocFile def setDocumentFile(self,docFile): """ Sets the document file. """ if not isinstance(docFile,QFile): return self._currDocFile = docFile def selectedDataSource(self): """ Returns the name of the data source specified by the user. """ return self._stdmDataSourceDock.widget().cboDataSource.currentText() def selectedDataSourceCategory(self): """ Returns the category (view or table) that the data source belongs to. """ if self.stdmDataSourceDock().widget() != None: return self.stdmDataSourceDock().widget().category() return "" def propagateDataSourceSelection(self,dataSourceName): """ Propagates the signal when a user select a data source. Listening objects can hook on to it. """ self.dataSourceSelected.emit(dataSourceName) def loadTemplate(self,filePath): """ Loads a document template into the view and updates the necessary STDM-related controls. """ if not QFile.exists(filePath): QMessageBox.critical(self.composerView(), QApplication.translate("OpenTemplateConfig","Open Template Error"), \ QApplication.translate("OpenTemplateConfig","The specified template does not exist.")) return templateFile = QFile(filePath) if not templateFile.open(QIODevice.ReadOnly): QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Open Operation Error"), \ "{0}\n{1}".format(QApplication.translate("ComposerWrapper","Cannot read template file."), \ templateFile.errorString() )) return templateDoc = QDomDocument() if templateDoc.setContent(templateFile): #Load items into the composition and configure STDM data controls self.composition().loadFromTemplate(templateDoc) self.clearWidgetMappings() #Load data controls composerDS = ComposerDataSource.create(templateDoc) self._configureDataControls(composerDS) #Load symbol editors spatialFieldsConfig = SpatialFieldsConfiguration.create(templateDoc) self._configureSpatialSymbolEditor(spatialFieldsConfig) def saveTemplate(self): """ Creates and saves a new document template. """ #Validate if the user has specified the data source if self.selectedDataSource() == "": QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), \ QApplication.translate("ComposerWrapper","Please specify the " \ "data source name for the document composition.")) return #If it is a new unsaved document template then prompt for the document name. docFile = self.documentFile() if docFile == None: docName,ok = QInputDialog.getText(self.composerView(), \ QApplication.translate("ComposerWrapper","Template Name"), \ QApplication.translate("ComposerWrapper","Please enter the template name below"), \ ) if ok and docName != "": templateDir = self._composerTemplatesPath() if templateDir == None: QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), \ QApplication.translate("ComposerWrapper","Directory for document templates could not be found.")) return absPath = templateDir + "/" + docName + ".sdt" docFile= QFile(absPath) else: return docFileInfo = QFileInfo(docFile) if not docFile.open(QIODevice.WriteOnly): QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Save Operation Error"), \ "{0}\n{1}".format(QApplication.translate("ComposerWrapper","Could not save template file."), \ docFile.errorString() )) return templateDoc = QDomDocument() self._writeXML(templateDoc,docFileInfo.completeBaseName()) if docFile.write(templateDoc.toByteArray()) == -1: QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Save Error"), \ QApplication.translate("ComposerWrapper","Could not save template file.")) return docFile.close() self.setDocumentFile(docFile) def _writeXML(self,xmlDoc,docName): """ Write the template configuration into the XML document. """ #Write default composer configuration composerElement = xmlDoc.createElement("Composer") composerElement.setAttribute("title",docName) composerElement.setAttribute("visible",1) xmlDoc.appendChild(composerElement) self.composition().writeXML(composerElement,xmlDoc) #Write STDM data field configurations dataSourceElement = ComposerDataSource.domElement(self, xmlDoc) composerElement.appendChild(dataSourceElement) #Write spatial field configurations spatialColumnsElement = SpatialFieldsConfiguration.domElement(self, xmlDoc) dataSourceElement.appendChild(spatialColumnsElement) def _configureDataControls(self,composerDataSource): """ Configure the data source and data field controls based on the composer data source configuration. """ if self.stdmDataSourceDock().widget() != None: #Set data source dataSourceWidget = self.stdmDataSourceDock().widget() dataSourceWidget.setCategory(composerDataSource.category()) dataSourceWidget.setSelectedSource(composerDataSource.name()) #Set data field controls for composerId in composerDataSource.dataFieldMappings().reverse: #Use composer item id since the uuid is stripped off composerItem = self.composition().getComposerItemById(composerId) if composerItem != None: compFieldSelector = ComposerFieldSelector(self,composerItem,self.composerView()) compFieldSelector.selectFieldName(composerDataSource.dataFieldName(composerId)) #Add widget to the collection but now use the current uuid of the composition item self.addWidgetMapping(composerItem.uuid(),compFieldSelector) def _configureSpatialSymbolEditor(self,spatialFieldConfig): """ Configure symbol editor controls. """ if self.stdmDataSourceDock().widget() != None: for itemId,spFieldsMappings in spatialFieldConfig.spatialFieldsMapping().iteritems(): mapItem = self.composition().getComposerItemById(itemId) if mapItem != None: composerSymbolEditor = ComposerSymbolEditor(self,self.composerView()) composerSymbolEditor.addSpatialFieldMappings(spFieldsMappings) #Add widget to the collection but now use the current uuid of the composer map self.addWidgetMapping(mapItem.uuid(),composerSymbolEditor) def _composerTemplatesPath(self): """ Reads the path of composer templates in the registry. """ regConfig = RegistryConfig() keyName = "ComposerTemplates" valueCollection = regConfig.read([keyName]) if len(valueCollection) == 0: return None else: return valueCollection[keyName] def _onItemRemoved(self,item): """ Slot raised when a composer item is removed from the scene. """ """ Code will not work since a QObject instance is returned instead of a QgsComposerItem if item.uuid() in self._widgetMappings: del self._widgetMappings[item.uuid()] """ pass def _onItemSelected(self,item): """ Slot raised when a composer item is selected. Load the corresponding field selector if the selection is an STDM data field label. QComposerLabel is returned as a QObject in the slot argument hence, we have resorted to capturing the currently selected items in the composition. """ selectedItems = self.composition().selectedComposerItems() if len(selectedItems) == 0: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) == 1: composerItem = selectedItems[0] if composerItem.uuid() in self._widgetMappings: stdmWidget = self._widgetMappings[composerItem.uuid()] if stdmWidget == self._stdmItemPropDock.widget(): return else: self._stdmItemPropDock.setWidget(stdmWidget) #Playing it safe in applying the formatting for the editor controls where applicable itemFormatter = None if isinstance(composerItem,QgsComposerArrow): itemFormatter = LineFormatter() elif isinstance(composerItem,QgsComposerLabel): itemFormatter = DataLabelFormatter() elif isinstance(composerItem,QgsComposerMap): itemFormatter = MapFormatter() if itemFormatter != None: itemFormatter.apply(composerItem,self,True) else: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) > 1: self._stdmItemPropDock.setWidget(None)
class MainWindow(KXmlGuiWindow): "Class which displays the main Danbooru Client window." def __init__(self, *args): "Initialize a new main window." super(MainWindow, self).__init__(*args) self.cache = KPixmapCache("danbooru") self.preferences = preferences.Preferences() self.api = None self.__ratings = None self.__step = 0 self.url_list = self.preferences.boards_list self.max_retrieve = self.preferences.thumbnail_no self.statusbar = self.statusBar() self.progress = QProgressBar() self.thumbnailarea = None self.tag_dock = None self.pool_dock = None self.first_fetch_widget = None self.progress.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) # FIXME: Hackish, but how to make it small otherwise? self.progress.setMinimumSize(100, 1) self.statusbar.addPermanentWidget(self.progress) self.progress.hide() self.setup_welcome_widget() self.setup_actions() def reload_config(self): """Reload configuration after a change""" urls = self.preferences.boards_list if self.first_fetch_widget is not None: self.first_fetch_widget.setup_urls(urls) if self.thumbnailarea is not None: max_thumbnail = self.preferences.thumbnail_no max_rating = self.preferences.max_allowed_rating self.thumbnailarea.fetchwidget.limit = max_thumbnail self.thumbnailarea.fetchwidget.rating = max_rating self.thumbnailarea.fetchwidget.update_values() self.thumbnailarea.connectwidget.setup_urls(urls) self.url_list = self.preferences.boards_list self.max_retrieve = self.preferences.thumbnail_no def setup_welcome_widget(self): """Load the welcome widget at startup.""" widget = QWidget() layout = QVBoxLayout() welcome = QLabel(parent=self) pix = QPixmap(KStandardDirs.locate("appdata","logo.png")) welcome.setPixmap(pix) welcome.setAlignment(Qt.AlignCenter) self.first_fetch_widget = connectwidget.ConnectWidget( self.preferences.boards_list, self) self.statusbar.addPermanentWidget(self.first_fetch_widget, 300) self.first_fetch_widget.connectionEstablished.connect( self.handle_connection) self.first_fetch_widget.rejected.connect( self.first_fetch_widget.hide) self.first_fetch_widget.hide() self.first_fetch_widget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) layout.addWidget(self.first_fetch_widget) layout.addWidget(welcome) widget.setLayout(layout) self.setCentralWidget(widget) def setup_tooltips(self): """Set tooltips for the actions.""" self.connect_action.setToolTip(i18n("Connect to a Danbooru board")) self.fetch_action.setToolTip( i18n("Fetch thumbnails from a Danbooru board") ) self.batch_download_action.setToolTip(i18n("Batch download images")) def create_actions(self): """Create actions for the main window.""" self.connect_action = KAction(KIcon("document-open-remote"), i18n("Connect"), self) self.fetch_action = KAction(KIcon("download"), i18n("Download"), self) self.clean_action = KAction(KIcon("trash-empty"), i18n("Clear thumbnail cache"), self) self.batch_download_action = KAction(KIcon("download"), i18n("Batch download"), self) self.pool_toggle_action = KToggleAction(KIcon("image-x-generic"), i18n("Pools"), self) self.tag_display_action = KDualAction(i18n("Show tags"), i18n("Hide tags"), self) self.tag_display_action.setIconForStates(KIcon("image-x-generic")) self.tag_display_action.setEnabled(False) # Shortcuts connect_default = KAction.ShortcutTypes(KAction.DefaultShortcut) connect_active = KAction.ShortcutTypes(KAction.ActiveShortcut) self.connect_action.setShortcut(KStandardShortcut.open()) self.fetch_action.setShortcut(KStandardShortcut.find()) self.fetch_action.setEnabled(False) self.batch_download_action.setEnabled(False) self.pool_toggle_action.setEnabled(False) def setup_action_collection(self): """Set up the action collection by adding the actions.""" action_collection = self.actionCollection() # Addition to the action collection action_collection.addAction("connect", self.connect_action) action_collection.addAction("fetch", self.fetch_action) action_collection.addAction("clean", self.clean_action) action_collection.addAction("batchDownload", self.batch_download_action) action_collection.addAction("poolDownload", self.pool_toggle_action) action_collection.addAction("tagDisplay", self.tag_display_action) KStandardAction.quit (self.close, action_collection) KStandardAction.preferences(self.show_preferences, action_collection) action_collection.removeAction( action_collection.action("help_contents")) action_collection.actionHovered.connect(self.setup_action_tooltip) def setup_actions(self): """Set up the relevant actions, tooltips, and load the RC file.""" self.create_actions() self.setup_tooltips() self.setup_action_collection() # Connect signals self.connect_action.triggered.connect(self.connect) self.fetch_action.triggered.connect(self.get_posts) self.clean_action.triggered.connect(self.clean_cache) self.batch_download_action.triggered.connect(self.batch_download) self.pool_toggle_action.toggled.connect(self.pool_toggle) self.tag_display_action.activeChanged.connect(self.tag_display) window_options = self.StandardWindowOption(self.ToolBar| self.Keys | self.Create | self.Save | self.StatusBar) setupGUI_args = [ QSize(500, 400), self.StandardWindowOption(window_options) ] #Check first in standard locations for danbooruui.rc rc_file = KStandardDirs.locate("appdata", "danbooruui.rc") if rc_file.isEmpty(): setupGUI_args.append(os.path.join(sys.path [0], "danbooruui.rc")) else: setupGUI_args.append(rc_file) self.setupGUI(*setupGUI_args) def setup_action_tooltip(self, action): "Show statusbar help when actions are hovered." if action.isEnabled(): self.statusBar().showMessage(action.toolTip(), 2000) def setup_connections(self): """Set up connections for post and tag retrieval.""" if self.api is None: return self.api.postRetrieved.connect(self.update_progress) self.api.postDownloadFinished.connect(self.download_finished) self.api.tagRetrieved.connect(self.tag_dock.widget().add_tags) self.tag_dock.widget().itemDoubleClicked.connect( self.fetch_tagged_items) def show_preferences(self): "Show the preferences dialog." if KConfigDialog.showDialog("Preferences dialog"): return else: dialog = preferences.PreferencesDialog(self, "Preferences dialog", self.preferences) dialog.show() dialog.settingsChanged.connect(self.reload_config) def connect(self, ok): "Connect to a Danbooru board." if self.thumbnailarea is None: self.first_fetch_widget.show() else: self.thumbnailarea.connectwidget.show() def restore(self): self.statusbar.removeWidget(self.connect_widget) def handle_connection(self, connection): self.api = None self.api = connection self.api.cache = self.cache if self.pool_dock is not None: self.pool_dock.hide() self.pool_dock.widget().clear() self.pool_toggle_action.setChecked(False) if self.thumbnailarea is not None: #TODO: Investigate usability self.clear(clear_pool=True) self.thumbnailarea.clear() self.thumbnailarea.api_data = self.api self.setup_connections() else: self.first_fetch_widget.connectionEstablished.disconnect() self.first_fetch_widget.rejected.disconnect() self.statusbar.removeWidget(self.first_fetch_widget) self.setup_area() self.api.cache = self.cache self.statusBar().showMessage(i18n("Connected to %s" % self.api.url), 3000) self.fetch_action.setEnabled(True) # Set up pool widget pool_widget = poolwidget.DanbooruPoolWidget(self.api) self.pool_dock = QDockWidget("Pools", self) self.pool_dock.setObjectName("PoolDock") self.pool_dock.setAllowedAreas(Qt.BottomDockWidgetArea) self.pool_dock.setWidget(pool_widget) #self.pool_dock.setFeatures(QDockWidget.NoDockWidgetFeatures) self.addDockWidget(Qt.BottomDockWidgetArea, self.pool_dock) self.pool_dock.widget().poolDownloadRequested.connect( self.pool_prepare) self.pool_dock.hide() self.pool_toggle_action.setEnabled(True) self.clear() # Needed to show properly the stuff after connecting self.api.get_post_list(tags="", limit=self.thumbnailarea.post_limit, rating=self.preferences.max_allowed_rating, blacklist=list(self.preferences.tag_blacklist)) self.api.get_tag_list(name="",blacklist=list(self.preferences.tag_blacklist), limit=20) def get_posts(self, ok): "Get posts from the connected Danbooru board." if not self.api: return self.thumbnailarea.fetchwidget.show() def handle_fetching(self, tags, max_rating, limit): """Slot connected to the dataSent signal of the fetch widget. The widgets are set up if they don't exist, and the API is queried to do the actual downloading of tags and """ self.clear() self.thumbnailarea.fetchwidget.hide() if self.tag_dock is not None: self.tag_dock.widget().clear() self.thumbnailarea.post_limit = limit blacklist= list(self.preferences.tag_blacklist) self.api.get_post_list(tags=tags, limit=limit, rating=max_rating, blacklist=blacklist) tags = [item for item in tags if item] if not tags: # No related tags, fetch the most recent 20 tags = "" self.api.get_tag_list(name=tags,blacklist=blacklist, limit=20) else: self.api.get_related_tags(tags=tags, blacklist=blacklist) def fetch_tagged_items(self, item): """Fetch items found in the tag list widget.""" tag_name = unicode(item.text()) self.clear() blacklist = self.preferences.tag_blacklist limit = self.preferences.thumbnail_no rating = self.preferences.max_allowed_rating self.api.get_post_list(page=1, tags=[tag_name], blacklist=blacklist, limit=limit, rating=rating) self.api.get_related_tags(tags=[tag_name], blacklist=blacklist) def pool_toggle(self, checked): "Toggle the presence/absence of the pool dock." if not self.api: return if not checked: self.pool_dock.hide() else: self.pool_dock.show() def pool_prepare(self, pool_id): """Prepare the central area for pool image loading.""" if self.thumbnailarea is None: self.setup_area() else: self.clear(clear_pool=False) self.api.get_pool(pool_id, blacklist=self.preferences.tag_blacklist, rating=self.preferences.max_allowed_rating) def batch_download(self, ok): "Download images in batch." selected_items = self.thumbnailarea.selected_images() if not selected_items: return start_url = KUrl("kfiledialog:///danbooru") caption = i18n("Select a directory to save the images to") directory = KFileDialog.getExistingDirectoryUrl(start_url, self, caption) if directory.isEmpty(): return for item in selected_items: file_url = item.url_label.url() tags = item.data.tags # Make a local copy to append paths as addPath works in-place destination = KUrl(directory) file_name = KUrl(file_url).fileName() destination.addPath(file_name) job = KIO.file_copy(KUrl(file_url), destination, -1) job.setProperty("tags", QVariant(tags)) job.result.connect(self.batch_download_slot) def setup_area(self): "Set up the central widget to display thumbnails." self.thumbnailarea = thumbnailarea.DanbooruTabWidget(self.api, self.preferences, self.preferences.thumbnail_no, self) self.setCentralWidget(self.thumbnailarea) self.thumbnailarea.connectwidget.connectionEstablished.connect( self.handle_connection, type=Qt.UniqueConnection) self.thumbnailarea.connectwidget.rejected.connect( self.thumbnailarea.connectwidget.hide, type=Qt.UniqueConnection) self.thumbnailarea.fetchwidget.dataSent.connect( self.handle_fetching, type=Qt.UniqueConnection) self.thumbnailarea.fetchwidget.rejected.connect( self.thumbnailarea.fetchwidget.hide, type=Qt.UniqueConnection) # Set up tag widget blacklist = self.preferences.tag_blacklist tag_widget = tagwidget.DanbooruTagWidget(blacklist, self) self.tag_display_action.setActive(True) self.tag_display_action.setEnabled(True) self.tag_dock = QDockWidget("Similar tags", self) self.tag_dock.setObjectName("TagDock") self.tag_dock.setAllowedAreas(Qt.RightDockWidgetArea) self.tag_dock.setWidget(tag_widget) #self.tag_dock.setFeatures(QDockWidget.NoDockWidgetFeatures) self.addDockWidget(Qt.RightDockWidgetArea, self.tag_dock) self.tag_dock.hide() # Container signal-slot connections self.setup_connections() def download_finished(self): """Slot called when all the data has been completed. Clears the progress bar and resets it to 0.""" if not self.batch_download_action.isEnabled(): self.batch_download_action.setEnabled(True) self.__step = 0 self.progress.hide() def update_progress(self): "Update the progress bar." if not self.progress.isVisible(): self.progress.show() self.__step += 1 self.progress.setValue(self.__step) def clear(self, clear_pool=True): "Clear the central widget." if self.thumbnailarea is None: return self.thumbnailarea.clear() self.tag_dock.widget().clear() if clear_pool: self.pool_dock.widget().clear() self.batch_download_action.setEnabled(False) def clean_cache(self): "Purge the thumbnail cache." self.cache.discard() self.statusBar().showMessage(i18n("Thumbnail cache cleared.")) def batch_download_slot(self, job): """Slot called when doing batch download, for each file retrieved. If Nepomuk tagging is enabled, each file is tagged using the item's respective tags. """ if job.error(): job.ui().showErrorMessage() else: if self.preferences.nepomuk_enabled: tags = job.property("tags").toPyObject() #danbooru2nepomuk.tag_danbooru_item(job.destUrl().path(), # tags) def tag_display(self, state): """Display or hide the tag dock.""" if self.tag_dock is None: self.tag_display_action.setActive(False) return if state: self.tag_dock.show() else: self.tag_dock.hide()
class fdtm: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(self.plugin_dir, 'i18n', 'fdtm_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) # Declare instance attributes self.actions = [] self.menu = self.tr(u'&fdtm') # TODO: We are going to let the user set this up in a future iteration self.toolbar = self.iface.addToolBar(u'fdtm') self.toolbar.setObjectName(u'fdtm') self.pluginIsActive = False # noinspection PyMethodMayBeStatic def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('fdtm', message) def add_action(self, icon_path, text, callback, enabled_flag=True, add_to_menu='toolbar', add_to_toolbar=True, status_tip=None, whats_this=None, isMenu=None, checkable=None, checked=None, parent=None): if not parent: parent = self.iface.mainWindow() icon = QIcon(icon_path) if add_to_menu == 'toolbar': action = QAction(icon, text, parent) else: action = self.tools[add_to_menu].menu().addAction(text) action.setActionGroup(self.tools[add_to_menu].actionGroup) add_to_toolbar = False if checkable: action.setCheckable(True) if checked: action.setChecked(True) if callback: action.toggled.connect(callback) else: if callback: action.triggered.connect(callback) action.setEnabled(enabled_flag) if status_tip is not None: action.setStatusTip(status_tip) if whats_this is not None: action.setWhatsThis(whats_this) if add_to_toolbar: self.toolbar.addAction(action) if isMenu: newMenu = QMenu() action.setMenu(newMenu) newActionGroup = QActionGroup(newMenu) action.actionGroup = newActionGroup if add_to_menu == 'toolbar': self.iface.addPluginToMenu(self.menu, action) self.actions.append(action) return action def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" self.tools = { 'about': self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_00_about.png'), text=self.tr(u'about FDTM plugin'), callback=self.run_about, ), 'settings': self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_01_settings.png'), text=self.tr(u'settings'), callback=self.run_settings, ), 'print': self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_07_print.png'), text=self.tr(u'print'), callback=self.run_print, enabled_flag=False, isMenu=True, ), 'summary': self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_08_summary.png'), text=self.tr(u'summary'), callback=self.toggle_summary, enabled_flag=False, ), 'info': self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_06_info.png'), text=self.tr(u'info summary'), callback=self.run_info, enabled_flag=False, checkable=True, ), 'EP': self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_02_EP.png'), text=self.tr(u'input/edit strategies'), callback=self.run_EP, enabled_flag=False, checkable=True, ), 'EA': self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_03_EA.png'), text=self.tr(u'input/edit strategies'), callback=self.run_EA, enabled_flag=False, checkable=True, ), 'WR': self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_04_WR.png'), text=self.tr(u'input/edit water_rec'), callback=self.run_WR, enabled_flag=False, checkable=True, isMenu=True, ), 'WDS': self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_05_WDS.png'), text=self.tr(u'input/edit water_ds'), callback=self.run_WDS, enabled_flag=False, checkable=True, ), 'DSV': self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_09_DSV.png'), text=self.tr(u'rebuild dranaige system valve'), callback=self.run_DSV, enabled_flag=False, checkable=False, ), } self.tools['WRp'] = self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_04_WR.png'), text=self.tr(u'point water_rec'), callback=None, #self.check_WR, enabled_flag=True, checkable=True, add_to_menu='WR', ) self.tools['WRg'] = self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_04_WR.png'), text=self.tr(u'green roof water_rec'), callback=None, #self.check_WR, enabled_flag=True, checkable=True, add_to_menu='WR') self.tools['WRl'] = self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_04_WR.png'), text=self.tr(u'limited water_rec'), callback=None, #self.check_WR, enabled_flag=True, checked=True, checkable=True, add_to_menu='WR') self.tools['Print project report'] = self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_04_WR.png'), text=self.tr(u'print global report'), callback=self.print_global, enabled_flag=True, add_to_menu='print') self.tools['Print EP report'] = self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_04_WR.png'), text=self.tr(u'print EP report'), callback=self.print_EP, enabled_flag=True, add_to_menu='print') self.tools['Print EA report'] = self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_04_WR.png'), text=self.tr(u'print EA report'), callback=self.print_EA, enabled_flag=True, add_to_menu='print') self.tools['Print WR report'] = self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_04_WR.png'), text=self.tr(u'print WR report'), callback=self.print_WR, enabled_flag=True, add_to_menu='print') self.tools['Print WDS report'] = self.add_action( os.path.join(self.plugin_dir, 'res', 'icon_04_WR.png'), text=self.tr(u'print WDS report'), callback=self.print_WDS, enabled_flag=True, add_to_menu='print') self.dlg_EA = fdtmEADialog() self.dlg_EP = fdtmEPDialog() self.dlg_WR = fdtmWRDialog() self.dlg_WDS = fdtmWDSDialog() self.dlg_info = fdtmInfoDialog() self.dlg_print = fdtmPrintDialog() self.dlg_settings = fdtmSettingsDialog(self) self.dlg_about = fdtmAboutDialog() self.dlg_about.webView.page().setNetworkAccessManager( QgsNetworkAccessManager.instance()) # --------------------------------------------------------------------- self.dlg_settings.validated.connect(self.enableTools) self.dlg_summary = self.dlg_settings.getSummaryWidget() self.fdtmDockwidget = QDockWidget("FDTM summary", self.iface.mainWindow()) self.fdtmDockwidget.setObjectName("fdtmSummary") self.fdtmDockwidget.setWidget(self.dlg_summary) self.iface.addDockWidget(Qt.BottomDockWidgetArea, self.fdtmDockwidget) self.fdtmDockwidget.hide() #-------------------------------------------------------------------------- def enableTools(self, validated): tool_set = { 'print': 'icon_07_print', 'EP': 'icon_02_EP', 'EA': 'icon_03_EA', 'info': 'icon_06_info', 'WR': 'icon_04_WR', 'WDS': 'icon_05_WDS', 'DSV': 'icon_09_DSV', 'summary': 'icon_08_summary' } #'print':'icon_07_print','WR':'icon_04_WR','WDS':'icon_05_WDS' if validated: disabled_sufx = '' self.fdtmDockwidget.show() else: disabled_sufx = '_disabled' self.fdtmDockwidget.hide() for t, f in tool_set.items(): self.tools[t].setEnabled(validated) icon = QIcon( os.path.join(self.plugin_dir, 'res', f + disabled_sufx + '.png')) self.tools[t].setIcon(icon) if self.dlg_settings.optionalDrainageSystemLayer.isChecked( ) and self.dlg_settings.optionalDrainageSystemLayer.DSVLayer(): self.tools['DSV'].setEnabled(True) self.tools['DSV'].setIcon( QIcon(os.path.join(self.plugin_dir, 'res', 'icon_09_DSV.png'))) else: self.tools['DSV'].setEnabled(False) self.tools['DSV'].setIcon( QIcon( os.path.join(self.plugin_dir, 'res', 'icon_09_DSV_disabled.png'))) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginMenu(self.tr(u'&fdtm'), action) self.iface.removeToolBarIcon(action) # remove the toolbar self.dlg_settings.deactivate() self.iface.removeDockWidget(self.fdtmDockwidget) del self.toolbar #-------------------------------------------------------------------------- def setUncheckedActions(self, uncheckedActions): for action in uncheckedActions: self.tools[action].setChecked(False) def run_EP(self, checked): if checked: self.setUncheckedActions(['EA', 'WR', 'WDS']) self.dlg_settings.datasetWrapper.EPpWrapper.fdtm_startEditing() else: self.dlg_settings.datasetWrapper.EPpWrapper.confirmEdits() def run_EA(self, checked): if checked: self.setUncheckedActions(['EP', 'WR', 'WDS']) self.dlg_settings.datasetWrapper.EApWrapper.fdtm_startEditing() else: self.dlg_settings.datasetWrapper.EApWrapper.confirmEdits() def check_WR(self): self.tools['WR'].setChecked(True) def run_WR(self, checked): if checked: self.setUncheckedActions(['EA', 'EP', 'WDS']) if self.tools['WR'].actionGroup.checkedAction( ) == self.tools['WRp']: WRType = 'point' elif self.tools['WR'].actionGroup.checkedAction( ) == self.tools['WRl']: WRType = 'limited' elif self.tools['WR'].actionGroup.checkedAction( ) == self.tools['WRg']: WRType = 'greenroof' self.dlg_settings.datasetWrapper.WRWrapper.fdtm_startEditing( WRType) self.tools['WR'].actionGroup.setEnabled(False) else: self.dlg_settings.datasetWrapper.WRWrapper.confirmEdits() self.tools['WR'].actionGroup.setEnabled(True) def run_WDS(self, checked): if checked: self.setUncheckedActions(['EA', 'WR', 'EP']) self.dlg_settings.datasetWrapper.WDSWrapper.fdtm_startEditing() else: self.dlg_settings.datasetWrapper.WDSWrapper.confirmEdits() def toggle_summary(self): if self.fdtmDockwidget.isVisible(): self.fdtmDockwidget.hide() else: self.fdtmDockwidget.show() def run_info(self, checked): if checked: self.backupMapTool = self.iface.mapCanvas().mapTool() layerList = [ self.dlg_settings.EApLayer, self.dlg_settings.EPpLayer, self.dlg_settings.EPlLayer, self.dlg_settings.WRLayer, self.dlg_settings.WDSLayer ] if self.dlg_settings.optionalDrainageSystemLayer.DSVLayer(): self.drainageSystemLayerManagement = True layerList.append( self.dlg_settings.datasetWrapper.DSVWrapper.lyr) else: self.drainageSystemLayerManagement = False #print layerList self.fdtm_infoMapTool = IdentifyGeometry( self.iface.mapCanvas(), layerList, self.dlg_settings.DEMLayer) self.fdtm_infoMapTool.geomIdentified.connect(self.editFeature) self.iface.mapCanvas().setMapTool(self.fdtm_infoMapTool) self.iface.mapCanvas().mapToolSet.connect(self.restoreInfoAction) else: if self.backupMapTool: self.iface.mapCanvas().mapToolSet.disconnect( self.restoreInfoAction) self.iface.mapCanvas().setMapTool(self.backupMapTool) def restoreInfoAction(self, MT): self.iface.mapCanvas().mapToolSet.disconnect(self.restoreInfoAction) self.backupMapTool = None self.tools['info'].setChecked(False) def editFeature(self, selLayer, selFeature, identifyPoint): if selLayer == self.dlg_settings.DEMLayer: if self.dlg_settings.datasetWrapper.DEMWrapper.contains( identifyPoint): self.dlg_settings.datasetWrapper.DEMWrapper.viewSample( identifyPoint) else: self.iface.messageBar().pushMessage( "Info tool error", "Sampling outside digital elevation model boundary", level=1, duration=3) # QgsMessageBar.Warning elif self.drainageSystemLayerManagement and selLayer == self.dlg_settings.datasetWrapper.DSVWrapper.lyr: self.dlg_settings.datasetWrapper.DSVWrapper.fdtm_startEditing( changeActiveLayer=False) self.dlg_settings.datasetWrapper.DSVWrapper.editAttributes( selFeature) self.dlg_settings.datasetWrapper.DSVWrapper.fdtm_commitChanges() else: selLayer.fdtm_wrapper.editAttributes(selFeature) def run_settings(self): """Run method that loads and starts the plugin""" self.dlg_settings.show() def run_about(self): """Run method that loads and starts the plugin""" self.dlg_about.webView.load(QUrl("http://brigaid.eu/")) self.dlg_about.show() def run_print(self): """Run method that loads and starts the plugin""" self.tools['print'].menu().exec_( self.toolbar.mapToGlobal(QPoint(0, self.toolbar.height()))) def print_global(self): fdtmPrint.exportGlobalReport(self.dlg_settings) def print_EP(self): fdtmPrint.exportEP(self.dlg_settings) def print_EA(self): fdtmPrint.exportEA(self.dlg_settings) def print_WR(self): fdtmPrint.exportWR(self.dlg_settings) def print_WDS(self): fdtmPrint.exportWDS(self.dlg_settings) def undef(self): pass def run_DSV(self): self.dlg_settings.datasetWrapper.DSVWrapper.rebuildLayer()
class ComposerWrapper(QObject): """ Embeds custom STDM tools in a QgsComposer instance for managing map-based STDM document templates. """ dataSourceSelected = pyqtSignal(str) def __init__(self, composerView, iface): QObject.__init__(self, composerView) self._compView = composerView self._stdmTB = self.mainWindow().addToolBar("STDM") self._selectMoveAction = None self._iface = iface #Container for custom editor widgets self._widgetMappings = {} #Hide default dock widgets if not self.itemDock() is None: self.itemDock().hide() if not self.atlasDock() is None: self.atlasDock().hide() if not self.generalDock() is None: self.generalDock().hide() #Create dock widget for configuring STDM data source self._stdmDataSourceDock = QDockWidget( QApplication.translate("ComposerWrapper","STDM Data Source"), self.mainWindow()) self._stdmDataSourceDock.setObjectName("STDMDataSourceDock") self._stdmDataSourceDock.setMinimumWidth(300) self._stdmDataSourceDock.setFeatures(QDockWidget.DockWidgetMovable|QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self._stdmDataSourceDock) self._dataSourceWidget = ComposerDataSourceSelector() self._stdmDataSourceDock.setWidget(self._dataSourceWidget) self._stdmDataSourceDock.show() #Re-insert dock widgets if not self.generalDock() is None: self.generalDock().show() if not self.itemDock() is None: self.itemDock().show() #Create dock widget for configuring STDM item properties self._stdmItemPropDock = QDockWidget( QApplication.translate("ComposerWrapper","STDM item properties"), self.mainWindow()) self._stdmItemPropDock.setObjectName("STDMItemDock") self._stdmItemPropDock.setMinimumWidth(300) self._stdmItemPropDock.setFeatures(QDockWidget.DockWidgetMovable|QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea,self._stdmItemPropDock) self._stdmItemPropDock.show() #Re-arrange dock widgets and push up STDM data source dock widget if not self.generalDock() is None: self.mainWindow().splitDockWidget(self._stdmDataSourceDock, self.generalDock(),Qt.Vertical) if not self.itemDock() is None: self.mainWindow().splitDockWidget(self._stdmDataSourceDock, self.itemDock(),Qt.Vertical) if not self.generalDock() is None: self.mainWindow().tabifyDockWidget(self.generalDock(), self.itemDock()) if not self.itemDock() is None: self.mainWindow().splitDockWidget(self.itemDock(), self._stdmItemPropDock, Qt.Vertical) #Set focus on composition properties window if not self.generalDock() is None: self.generalDock().activateWindow() self.generalDock().raise_() #Connect signals self.composition().itemRemoved.connect(self._onItemRemoved) self._dataSourceWidget.cboDataSource.currentIndexChanged[str].connect( self.propagateDataSourceSelection ) self.composerView().selectedItemChanged.connect(self._onItemSelected) #Current template document file self._currDocFile = None def _removeActions(self): """ Remove inapplicable actions and their corresponding toolbars and menus. """ removeActions = ["mActionSaveProject","mActionNewComposer","mActionDuplicateComposer"] composerToolbar = self.composerMainToolBar() if composerToolbar != None: saveProjectAction = None for itemAction in composerToolbar.actions(): if itemAction.objectName() == "mActionSaveProject": saveProjectAction = itemAction break if saveProjectAction != None: composerMenu = saveProjectAction.menu() def configure(self): #Create instances of custom STDM composer item configurations for ciConfig in ComposerItemConfig.itemConfigurations: ciConfigObj = ciConfig(self) def addWidgetMapping(self,uniqueIdentifier,widget): """ Add custom STDM editor widget based on the unique identifier of the composer item """ self._widgetMappings[uniqueIdentifier] = widget def widgetMappings(self): """ Returns a dictionary containing uuid values of composer items linked to STDM widgets. """ return self._widgetMappings def clearWidgetMappings(self): """ Resets the widget mappings collection. """ self._widgetMappings = {} def mainWindow(self): """ Returns the QMainWindow used by the composer view. """ return self._compView.composerWindow() def stdmToolBar(self): """ Returns the instance of the STDM toolbar added to the QgsComposer. """ return self._stdmTB def composerView(self): """ Returns the composer view. """ return self._compView def composition(self): """ Returns the QgsComposition instance used in the composer view. """ return self._compView.composition() def composerItemToolBar(self): """ Returns the toolbar containing actions for adding composer items. """ return self.mainWindow().findChild(QToolBar,"mItemToolbar") def composerMainToolBar(self): """ Returns the toolbar containing actions for managing templates. """ return self.mainWindow().findChild(QToolBar,"mComposerToolbar") def selectMoveAction(self): """ Returns the QAction for selecting or moving composer items. """ if self.composerItemToolBar() != None: if self._selectMoveAction == None: for itemAction in self.composerItemToolBar().actions(): if itemAction.objectName() == "mActionSelectMoveItem": self._selectMoveAction = itemAction break return self._selectMoveAction def checkedItemAction(self): """ Returns the currently selected composer item action. """ if self.selectMoveAction() != None: return self.selectMoveAction().actionGroup().checkedAction() return None def itemDock(self): """ Get the 'Item Properties' dock widget. """ return self.mainWindow().findChild(QDockWidget,"ItemDock") def atlasDock(self): """ Get the 'Atlas generation' dock widget. """ return self.mainWindow().findChild(QDockWidget,"AtlasDock") def generalDock(self): """ Get the 'Composition' dock widget. """ return self.mainWindow().findChild(QDockWidget,"CompositionDock") def stdmDataSourceDock(self): """ Returns the STDM data source dock widget. """ return self._stdmDataSourceDock def stdmItemDock(self): """ Returns the STDM item dock widget. """ return self._stdmItemPropDock def documentFile(self): """ Returns the QFile instance associated with the current document. 'None' will be returned for new, unsaved documents. """ return self._currDocFile def setDocumentFile(self,docFile): """ Sets the document file. """ if not isinstance(docFile,QFile): return self._currDocFile = docFile def selectedDataSource(self): """ Returns the name of the data source specified by the user. """ return self._stdmDataSourceDock.widget().cboDataSource.currentText() def selectedDataSourceCategory(self): """ Returns the category (view or table) that the data source belongs to. """ if self.stdmDataSourceDock().widget() != None: return self.stdmDataSourceDock().widget().category() return "" def propagateDataSourceSelection(self, dataSourceName): """ Propagates the signal when a user select a data source. Listening objects can hook on to it. """ self.dataSourceSelected.emit(dataSourceName) def composer_items(self): """ :return: Returns a list of custom composer items. :rtype: list """ return [self.composition().getComposerItemById(uuid) for uuid in self._widgetMappings.keys() if not self.composition().getComposerItemById(uuid) is None] def _clear_composition(self): """ Removes composer items which, otherwise, are causing QGIS to crash when loading a subsequent document template. """ items = self.composition().items() for c_item in items: if isinstance(c_item, QgsComposerItem) and not isinstance(c_item, QgsPaperItem): if c_item.uuid() in self._widgetMappings: #Remove corresponding widget as well as reference in the collection del self._widgetMappings[c_item.uuid()] self.composition().removeItem(c_item) self.composition().itemRemoved.emit(c_item) del c_item self.composition().undoStack().clear() self.composition().itemsModel().clear() def create_new_document_designer(self, file_path): """ Creates a new document designer and loads the document template defined in file path. :param file_path: Path to document template :type file_path: str """ document_designer = self._iface.createNewComposer("STDM Document Designer") #Embed STDM customizations cw = ComposerWrapper(document_designer, self._iface) cw.configure() #Load template cw.loadTemplate(file_path) def loadTemplate(self, filePath): """ Loads a document template into the view and updates the necessary STDM-related composer items. """ if not QFile.exists(filePath): QMessageBox.critical(self.composerView(), QApplication.translate("OpenTemplateConfig", "Open Template Error"), QApplication.translate("OpenTemplateConfig", "The specified template does not exist.")) return templateFile = QFile(filePath) if not templateFile.open(QIODevice.ReadOnly): QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper", "Open Operation Error"), "{0}\n{1}".format(QApplication.translate( "ComposerWrapper", "Cannot read template file."), templateFile.errorString() )) return templateDoc = QDomDocument() if templateDoc.setContent(templateFile): table_config_collection = TableConfigurationCollection.create(templateDoc) ''' First load vector layers for the table definitions in the config collection before loading rhe composition from file. ''' load_table_layers(table_config_collection) #Load items into the composition and configure STDM data controls self.composition().loadFromTemplate(templateDoc) self.clearWidgetMappings() #Load data controls composerDS = ComposerDataSource.create(templateDoc) #Set title by appending template name title = QApplication.translate("STDMPlugin", "STDM Document Designer") composer_el = templateDoc.documentElement() if not composer_el is None: template_name = "" if composer_el.hasAttribute("title"): template_name = composer_el.attribute("title", "") elif composer_el.hasAttribute("_title"): template_name = composer_el.attribute("_title", "") if template_name: win_title = u"{0} - {1}".format(title, template_name) self.mainWindow().setWindowTitle(template_name) self._configure_data_controls(composerDS) #Load symbol editors spatialFieldsConfig = SpatialFieldsConfiguration.create(templateDoc) self._configureSpatialSymbolEditor(spatialFieldsConfig) #Load photo editors photo_config_collection = PhotoConfigurationCollection.create(templateDoc) self._configure_photo_editors(photo_config_collection) #Load table editors self._configure_table_editors(table_config_collection) #Load chart property editors chart_config_collection = ChartConfigurationCollection.create(templateDoc) self._configure_chart_editors(chart_config_collection) self._sync_ids_with_uuids() def saveTemplate(self): """ Creates and saves a new document template. """ #Validate if the user has specified the data source if not self.selectedDataSource(): QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), QApplication.translate("ComposerWrapper","Please specify the " "data source name for the document composition.")) return #If it is a new unsaved document template then prompt for the document name. docFile = self.documentFile() if docFile is None: docName,ok = QInputDialog.getText(self.composerView(), QApplication.translate("ComposerWrapper","Template Name"), QApplication.translate("ComposerWrapper","Please enter the template name below"), ) if ok and docName: templateDir = self._composerTemplatesPath() if templateDir is None: QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), QApplication.translate("ComposerWrapper", "Directory for document templates cannot not be found.")) return absPath = templateDir + "/" + docName + ".sdt" #Check if there is an existing document with the same name caseInsenDic = CaseInsensitiveDict(documentTemplates()) if docName in caseInsenDic: result = QMessageBox.warning(self.composerView(), QApplication.translate("ComposerWrapper", "Existing Template"), u"'{0}' {1}.\nDo you want to replace the " "existing template?".format(docName, QApplication.translate("ComposerWrapper", "already exists")), QMessageBox.Yes|QMessageBox.No) if result == QMessageBox.Yes: #Delete the existing template delFile = QFile(absPath) remStatus = delFile.remove() if not remStatus: QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper", "Delete Error"), "'{0}' {1}.".format(docName, QApplication.translate("ComposerWrapper", "template could not be removed by the system," " please remove it manually from the document templates directory."))) return else: return docFile= QFile(absPath) else: return docFileInfo = QFileInfo(docFile) if not docFile.open(QIODevice.WriteOnly): QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper", "Save Operation Error"), "{0}\n{1}".format(QApplication.translate("ComposerWrapper", "Could not save template file."), docFile.errorString() )) return templateDoc = QDomDocument() template_name = docFileInfo.completeBaseName() self._writeXML(templateDoc, template_name) if docFile.write(templateDoc.toByteArray()) == -1: QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Save Error"), QApplication.translate("ComposerWrapper","Could not save template file.")) return else: self.mainWindow().setWindowTitle(template_name) docFile.close() def _writeXML(self, xml_doc, doc_name): """ Write the template configuration into the XML document. """ #Write default composer configuration composer_element = xml_doc.createElement("Composer") composer_element.setAttribute("title", doc_name) composer_element.setAttribute("visible", 1) xml_doc.appendChild(composer_element) self.composition().writeXML(composer_element, xml_doc) #Write STDM data field configurations dataSourceElement = ComposerDataSource.domElement(self, xml_doc) composer_element.appendChild(dataSourceElement) #Write spatial field configurations spatialColumnsElement = SpatialFieldsConfiguration.domElement(self, xml_doc) dataSourceElement.appendChild(spatialColumnsElement) #Write photo configuration tables_element = PhotoConfigurationCollection.dom_element(self, xml_doc) dataSourceElement.appendChild(tables_element) #Write table configuration tables_element = TableConfigurationCollection.dom_element(self, xml_doc) dataSourceElement.appendChild(tables_element) #Write chart configuration charts_element = ChartConfigurationCollection.dom_element(self, xml_doc) dataSourceElement.appendChild(charts_element) def _configure_data_controls(self, composer_data_source): """ Configure the data source and data field controls based on the composer data source configuration. """ if not self.stdmDataSourceDock().widget() is None: #Set data source dataSourceWidget = self.stdmDataSourceDock().widget() dataSourceWidget.setCategory(composer_data_source.category()) dataSourceWidget.setSelectedSource(composer_data_source.name()) #Set data field controls for composerId in composer_data_source.dataFieldMappings().reverse: #Use composer item id since the uuid is stripped off composerItem = self.composition().getComposerItemById(composerId) if not composerItem is None: compFieldSelector = ComposerFieldSelector(self, composerItem, self.composerView()) compFieldSelector.selectFieldName(composer_data_source.dataFieldName(composerId)) #Add widget to the collection but now use the current uuid of the composition item self.addWidgetMapping(composerItem.uuid(), compFieldSelector) def _configureSpatialSymbolEditor(self,spatial_field_config): """ Configure symbol editor controls. """ if not self.stdmDataSourceDock().widget() is None: for item_id, spFieldsMappings in spatial_field_config.spatialFieldsMapping().iteritems(): mapItem = self.composition().getComposerItemById(item_id) if not mapItem is None: composerSymbolEditor = ComposerSymbolEditor(self, self.composerView()) composerSymbolEditor.add_spatial_field_mappings(spFieldsMappings) #Add widget to the collection but now use the current uuid of the composer map self.addWidgetMapping(mapItem.uuid(), composerSymbolEditor) def _configure_photo_editors(self, photo_config_collection): """ Creates widgets for editing photo data sources. :param photo_config_collection: PhotoConfigurationCollection instance. :type photo_config_collection: PhotoConfigurationCollection """ if self.stdmDataSourceDock().widget() is None: return for item_id, photo_config in photo_config_collection.mapping().iteritems(): pic_item = self.composition().getComposerItemById(item_id) if not pic_item is None: photo_editor = ComposerPhotoDataSourceEditor(self, self.composerView()) photo_editor.set_configuration(photo_config) self.addWidgetMapping(pic_item.uuid(), photo_editor) def _configure_chart_editors(self, chart_config_collection): """ Creates widgets for editing chart properties. :param chart_config_collection: ChartConfigurationCollection instance. :type chart_config_collection: ChartConfigurationCollection """ if self.stdmDataSourceDock().widget() is None: return for item_id, chart_config in chart_config_collection.mapping().iteritems(): chart_item = self.composition().getComposerItemById(item_id) if not chart_item is None: chart_editor = ComposerChartConfigEditor(self, self.composerView()) chart_editor.set_configuration(chart_config) self.addWidgetMapping(chart_item.uuid(), chart_editor) def _configure_table_editors(self, table_config_collection): """ Creates widgets for editing table data sources. :param table_config_collection: TableConfigurationCollection instance. :type table_config_collection: TableConfigurationCollection """ if self.stdmDataSourceDock().widget() is None: return for item_id, table_config in table_config_collection.mapping().iteritems(): table_item = self.composition().getComposerItemById(item_id) if not table_item is None: table_editor = ComposerTableDataSourceEditor(self, table_item, self.composerView()) table_editor.set_configuration(table_config) self.addWidgetMapping(table_item.uuid(), table_editor) def _sync_ids_with_uuids(self): """ Matches IDs of custom STDM items with the corresponding UUIDs. This is applied when loading existing templates so that the saved document contains a matching pair of ID and UUID for each composer item. """ items = self._widgetMappings.keys() for item_uuid in self._widgetMappings.keys(): item = self.composition().getComposerItemByUuid(item_uuid) if not item is None: item.setId(item_uuid) def _composerTemplatesPath(self): """ Reads the path of composer templates in the registry. """ regConfig = RegistryConfig() keyName = "ComposerTemplates" valueCollection = regConfig.read([keyName]) if len(valueCollection) == 0: return None else: return valueCollection[keyName] def _onItemRemoved(self,item): """ Slot raised when a composer item is removed from the scene. """ """ Code will not work since a QObject instance is returned instead of a QgsComposerItem if item.uuid() in self._widgetMappings: del self._widgetMappings[item.uuid()] """ pass def _onItemSelected(self, item): """ Slot raised when a composer item is selected. Load the corresponding field selector if the selection is an STDM data field label. QComposerLabel is returned as a QObject in the slot argument hence, we have resorted to capturing the current selected items in the composition. """ selectedItems = self.composition().selectedComposerItems() if len(selectedItems) == 0: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) == 1: composer_item = selectedItems[0] if composer_item.uuid() in self._widgetMappings: stdmWidget = self._widgetMappings[composer_item.uuid()] if stdmWidget == self._stdmItemPropDock.widget(): return else: self._stdmItemPropDock.setWidget(stdmWidget) #Playing it safe in applying the formatting for the editor controls where applicable itemFormatter = None if isinstance(composer_item, QgsComposerArrow): itemFormatter = LineFormatter() elif isinstance(composer_item, QgsComposerLabel): itemFormatter = DataLabelFormatter() elif isinstance(composer_item, QgsComposerMap): itemFormatter = MapFormatter() elif isinstance(composer_item, QgsComposerPicture): """ Use widget attribute to distinguish type i.e. whether it is a photo, graph etc. """ editor_widget = self._widgetMappings[composer_item.uuid()] if isinstance(editor_widget, ComposerPhotoDataSourceEditor): itemFormatter = PhotoFormatter() elif isinstance(editor_widget, ComposerChartConfigEditor): itemFormatter = ChartFormatter() elif isinstance(composer_item, QgsComposerAttributeTable): itemFormatter = TableFormatter() if not itemFormatter is None: itemFormatter.apply(composer_item, self, True) else: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) > 1: self._stdmItemPropDock.setWidget(None)
canvas = iface.mapCanvas() main_window = iface.mainWindow() new_dock_widget = QDockWidget(u"My doc widget") layout = QVBoxLayout() map_canvas_overview = QgsMapOverviewCanvas(new_dock_widget, canvas) layerset = [layer.id()] map_canvas_overview.setLayerSet(layerset) map_canvas_overview.setBackgroundColor(QColor(255, 127, 0)) map_canvas_overview.enableAntiAliasing(True) map_canvas_overview.setMinimumWidth(380) map_canvas_overview.setMinimumHeight(280) new_dock_widget.resize(400, 300) layout.addWidget(map_canvas_overview) new_dock_widget.setLayout(layout) main_window.addDockWidget(Qt.RightDockWidgetArea, new_dock_widget) new_dock_widget.show() map_canvas_overview.refresh() # Make the background color disappear? # Layout optional playground layout.setContentsMargins(0, 0, 0, 0) # To clean unuseful reference widgets # [main_window.removeDockWidget(i) for i in QObject.findChildren(main_window, QDockWidget) if i.windowTitle() == u'My doc widget']
class gazetteerSearch: def __init__(self, iface): self.dock = None self.results = [] # Save reference to the QGIS interface self.iface = iface self.iface.newProjectCreated.connect(self._hideMarker) self.iface.projectRead.connect(self._hideMarker) self.canvas = self.iface.mapCanvas() self.marker = QgsVertexMarker(self.iface.mapCanvas()) self.marker.setIconSize(20) self.marker.setPenWidth(3) self.marker.setIconType(QgsVertexMarker.ICON_CROSS) self.marker.hide() # Create the dialog and keep reference self.widget = gazetteerSearchDialog() self.widget.runSearch.connect(self.runSearch) self.widget.ui.clearButton.pressed.connect(self.clearResults) self.widget.zoomRequested.connect(self.zoomTo) # initialize plugin directory self.plugin_dir = QFileInfo(QgsApplication.qgisUserDbFilePath()).path( ) + "/python/plugins/gazetteersearch" # initialize locale localePath = "" locale = QSettings().value("locale/userLocale").toString()[0:2] if QFileInfo(self.plugin_dir).exists(): localePath = self.plugin_dir + "/i18n/gazetteersearch_" + locale + ".qm" if QFileInfo(localePath).exists(): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) def initGui(self): # Create action that will start plugin configuration self.action = QAction(QIcon(":/plugins/gazetteersearch/icon.png"), \ u"Gazetteer Search", self.iface.mainWindow()) # connect the action to the run method self.action.triggered.connect(self.run) # Add toolbar button and menu item self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu(u"&Gazetteer Search", self.action) def unload(self): # Remove the plugin menu item and icon self.iface.removePluginMenu(u"&Gazetteer Search", self.action) self.iface.removeToolBarIcon(self.action) self.iface.mapCanvas().scene().removeItem(self.marker) self.marker = None def _hideMarker(self): self.marker.hide() # run method that performs all the real work def run(self): if not self.dock: self.dock = QDockWidget("Gazetteer Search", self.iface.mainWindow()) self.dock.setWidget(self.widget) self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dock) self.gazetteers = common.getGazetteers() for gazetter in self.gazetteers.iterkeys(): self.widget.addGazetter(gazetter) if len(self.gazetteers) == 1: self.widget.hideGazetteers() else: self.dock.show() def runSearch(self, searchString, selectedGazetteer): gazetteer_config = self.gazetteers[str(selectedGazetteer)] gazetteer = self.getGazetteerModule(gazetteer_config) url = common.prepareURL(gazetteer.url, gazetteer.params, searchString) data = common.search(url) try: self.results = list(gazetteer.parseRequestResults(data)) except ValueError: self.results = [] if len(self.results) == 0: self.widget.addError('No results found for "%s"' % searchString) for res in self.results: self.widget.addResult(res.description) def clearResults(self): self.widget.clearResults() self.marker.hide() def getGazetteerModule(self, config): gazetteer_module = config['gazetteer'] imported_gazetteer = import_module('gazetteers.%s' % gazetteer_module) return imported_gazetteer def zoomTo(self, name): for res in self.results: if unicode(res.description) == unicode(name): dest_crs = self.canvas.mapRenderer().destinationCrs() src_crs = QgsCoordinateReferenceSystem() src_crs.createFromEpsg(res.epsg) transform = QgsCoordinateTransform(src_crs, dest_crs) new_point = transform.transform(res.x, res.y) x = new_point.x() y = new_point.y() self.canvas.setExtent(QgsRectangle(x, y, x, y)) self.canvas.zoomScale(res.zoom) self.canvas.refresh() self.marker.setCenter(new_point) self.marker.show() return
class MainWindow(KXmlGuiWindow): "Class which displays the main Danbooru Client window." def __init__(self, *args): "Initialize a new main window." super(MainWindow, self).__init__(*args) self.cache = KPixmapCache("danbooru") self.preferences = preferences.Preferences() self.api = None self.__ratings = None self.__step = 0 self.url_list = self.preferences.boards_list self.max_retrieve = self.preferences.thumbnail_no self.statusbar = self.statusBar() self.progress = QProgressBar() self.thumbnailarea = None self.tag_dock = None self.pool_dock = None self.first_fetch_widget = None self.progress.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) # FIXME: Hackish, but how to make it small otherwise? self.progress.setMinimumSize(100, 1) self.statusbar.addPermanentWidget(self.progress) self.progress.hide() self.setup_welcome_widget() self.setup_actions() def reload_config(self): """Reload configuration after a change""" urls = self.preferences.boards_list if self.first_fetch_widget is not None: self.first_fetch_widget.setup_urls(urls) if self.thumbnailarea is not None: max_thumbnail = self.preferences.thumbnail_no max_rating = self.preferences.max_allowed_rating self.thumbnailarea.fetchwidget.limit = max_thumbnail self.thumbnailarea.fetchwidget.rating = max_rating self.thumbnailarea.fetchwidget.update_values() self.thumbnailarea.connectwidget.setup_urls(urls) self.url_list = self.preferences.boards_list self.max_retrieve = self.preferences.thumbnail_no def setup_welcome_widget(self): """Load the welcome widget at startup.""" widget = QWidget() layout = QVBoxLayout() welcome = QLabel(parent=self) pix = QPixmap(KStandardDirs.locate("appdata", "logo.png")) welcome.setPixmap(pix) welcome.setAlignment(Qt.AlignCenter) self.first_fetch_widget = connectwidget.ConnectWidget( self.preferences.boards_list, self) self.statusbar.addPermanentWidget(self.first_fetch_widget, 300) self.first_fetch_widget.connectionEstablished.connect( self.handle_connection) self.first_fetch_widget.rejected.connect(self.first_fetch_widget.hide) self.first_fetch_widget.hide() self.first_fetch_widget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) layout.addWidget(self.first_fetch_widget) layout.addWidget(welcome) widget.setLayout(layout) self.setCentralWidget(widget) def setup_tooltips(self): """Set tooltips for the actions.""" self.connect_action.setToolTip(i18n("Connect to a Danbooru board")) self.fetch_action.setToolTip( i18n("Fetch thumbnails from a Danbooru board")) self.batch_download_action.setToolTip(i18n("Batch download images")) def create_actions(self): """Create actions for the main window.""" self.connect_action = KAction(KIcon("document-open-remote"), i18n("Connect"), self) self.fetch_action = KAction(KIcon("download"), i18n("Download"), self) self.clean_action = KAction(KIcon("trash-empty"), i18n("Clear thumbnail cache"), self) self.batch_download_action = KAction(KIcon("download"), i18n("Batch download"), self) self.pool_toggle_action = KToggleAction(KIcon("image-x-generic"), i18n("Pools"), self) self.tag_display_action = KDualAction(i18n("Show tags"), i18n("Hide tags"), self) self.tag_display_action.setIconForStates(KIcon("image-x-generic")) self.tag_display_action.setEnabled(False) # Shortcuts connect_default = KAction.ShortcutTypes(KAction.DefaultShortcut) connect_active = KAction.ShortcutTypes(KAction.ActiveShortcut) self.connect_action.setShortcut(KStandardShortcut.open()) self.fetch_action.setShortcut(KStandardShortcut.find()) self.fetch_action.setEnabled(False) self.batch_download_action.setEnabled(False) self.pool_toggle_action.setEnabled(False) def setup_action_collection(self): """Set up the action collection by adding the actions.""" action_collection = self.actionCollection() # Addition to the action collection action_collection.addAction("connect", self.connect_action) action_collection.addAction("fetch", self.fetch_action) action_collection.addAction("clean", self.clean_action) action_collection.addAction("batchDownload", self.batch_download_action) action_collection.addAction("poolDownload", self.pool_toggle_action) action_collection.addAction("tagDisplay", self.tag_display_action) KStandardAction.quit(self.close, action_collection) KStandardAction.preferences(self.show_preferences, action_collection) action_collection.removeAction( action_collection.action("help_contents")) action_collection.actionHovered.connect(self.setup_action_tooltip) def setup_actions(self): """Set up the relevant actions, tooltips, and load the RC file.""" self.create_actions() self.setup_tooltips() self.setup_action_collection() # Connect signals self.connect_action.triggered.connect(self.connect) self.fetch_action.triggered.connect(self.get_posts) self.clean_action.triggered.connect(self.clean_cache) self.batch_download_action.triggered.connect(self.batch_download) self.pool_toggle_action.toggled.connect(self.pool_toggle) self.tag_display_action.activeChanged.connect(self.tag_display) window_options = self.StandardWindowOption(self.ToolBar | self.Keys | self.Create | self.Save | self.StatusBar) setupGUI_args = [ QSize(500, 400), self.StandardWindowOption(window_options) ] #Check first in standard locations for danbooruui.rc rc_file = KStandardDirs.locate("appdata", "danbooruui.rc") if rc_file.isEmpty(): setupGUI_args.append(os.path.join(sys.path[0], "danbooruui.rc")) else: setupGUI_args.append(rc_file) self.setupGUI(*setupGUI_args) def setup_action_tooltip(self, action): "Show statusbar help when actions are hovered." if action.isEnabled(): self.statusBar().showMessage(action.toolTip(), 2000) def setup_connections(self): """Set up connections for post and tag retrieval.""" if self.api is None: return self.api.postRetrieved.connect(self.update_progress) self.api.postDownloadFinished.connect(self.download_finished) self.api.tagRetrieved.connect(self.tag_dock.widget().add_tags) self.tag_dock.widget().itemDoubleClicked.connect( self.fetch_tagged_items) def show_preferences(self): "Show the preferences dialog." if KConfigDialog.showDialog("Preferences dialog"): return else: dialog = preferences.PreferencesDialog(self, "Preferences dialog", self.preferences) dialog.show() dialog.settingsChanged.connect(self.reload_config) def connect(self, ok): "Connect to a Danbooru board." if self.thumbnailarea is None: self.first_fetch_widget.show() else: self.thumbnailarea.connectwidget.show() def restore(self): self.statusbar.removeWidget(self.connect_widget) def handle_connection(self, connection): self.api = None self.api = connection self.api.cache = self.cache if self.pool_dock is not None: self.pool_dock.hide() self.pool_dock.widget().clear() self.pool_toggle_action.setChecked(False) if self.thumbnailarea is not None: #TODO: Investigate usability self.clear(clear_pool=True) self.thumbnailarea.clear() self.thumbnailarea.api_data = self.api self.setup_connections() else: self.first_fetch_widget.connectionEstablished.disconnect() self.first_fetch_widget.rejected.disconnect() self.statusbar.removeWidget(self.first_fetch_widget) self.setup_area() self.api.cache = self.cache self.statusBar().showMessage(i18n("Connected to %s" % self.api.url), 3000) self.fetch_action.setEnabled(True) # Set up pool widget pool_widget = poolwidget.DanbooruPoolWidget(self.api) self.pool_dock = QDockWidget("Pools", self) self.pool_dock.setObjectName("PoolDock") self.pool_dock.setAllowedAreas(Qt.BottomDockWidgetArea) self.pool_dock.setWidget(pool_widget) #self.pool_dock.setFeatures(QDockWidget.NoDockWidgetFeatures) self.addDockWidget(Qt.BottomDockWidgetArea, self.pool_dock) self.pool_dock.widget().poolDownloadRequested.connect( self.pool_prepare) self.pool_dock.hide() self.pool_toggle_action.setEnabled(True) self.clear() # Needed to show properly the stuff after connecting self.api.get_post_list(tags="", limit=self.thumbnailarea.post_limit, rating=self.preferences.max_allowed_rating, blacklist=list(self.preferences.tag_blacklist)) self.api.get_tag_list(name="", blacklist=list(self.preferences.tag_blacklist), limit=20) def get_posts(self, ok): "Get posts from the connected Danbooru board." if not self.api: return self.thumbnailarea.fetchwidget.show() def handle_fetching(self, tags, max_rating, limit): """Slot connected to the dataSent signal of the fetch widget. The widgets are set up if they don't exist, and the API is queried to do the actual downloading of tags and """ self.clear() self.thumbnailarea.fetchwidget.hide() if self.tag_dock is not None: self.tag_dock.widget().clear() self.thumbnailarea.post_limit = limit blacklist = list(self.preferences.tag_blacklist) self.api.get_post_list(tags=tags, limit=limit, rating=max_rating, blacklist=blacklist) tags = [item for item in tags if item] if not tags: # No related tags, fetch the most recent 20 tags = "" self.api.get_tag_list(name=tags, blacklist=blacklist, limit=20) else: self.api.get_related_tags(tags=tags, blacklist=blacklist) def fetch_tagged_items(self, item): """Fetch items found in the tag list widget.""" tag_name = unicode(item.text()) self.clear() blacklist = self.preferences.tag_blacklist limit = self.preferences.thumbnail_no rating = self.preferences.max_allowed_rating self.api.get_post_list(page=1, tags=[tag_name], blacklist=blacklist, limit=limit, rating=rating) self.api.get_related_tags(tags=[tag_name], blacklist=blacklist) def pool_toggle(self, checked): "Toggle the presence/absence of the pool dock." if not self.api: return if not checked: self.pool_dock.hide() else: self.pool_dock.show() def pool_prepare(self, pool_id): """Prepare the central area for pool image loading.""" if self.thumbnailarea is None: self.setup_area() else: self.clear(clear_pool=False) self.api.get_pool(pool_id, blacklist=self.preferences.tag_blacklist, rating=self.preferences.max_allowed_rating) def batch_download(self, ok): "Download images in batch." selected_items = self.thumbnailarea.selected_images() if not selected_items: return start_url = KUrl("kfiledialog:///danbooru") caption = i18n("Select a directory to save the images to") directory = KFileDialog.getExistingDirectoryUrl( start_url, self, caption) if directory.isEmpty(): return for item in selected_items: file_url = item.url_label.url() tags = item.data.tags # Make a local copy to append paths as addPath works in-place destination = KUrl(directory) file_name = KUrl(file_url).fileName() destination.addPath(file_name) job = KIO.file_copy(KUrl(file_url), destination, -1) job.setProperty("tags", QVariant(tags)) job.result.connect(self.batch_download_slot) def setup_area(self): "Set up the central widget to display thumbnails." self.thumbnailarea = thumbnailarea.DanbooruTabWidget( self.api, self.preferences, self.preferences.thumbnail_no, self) self.setCentralWidget(self.thumbnailarea) self.thumbnailarea.connectwidget.connectionEstablished.connect( self.handle_connection, type=Qt.UniqueConnection) self.thumbnailarea.connectwidget.rejected.connect( self.thumbnailarea.connectwidget.hide, type=Qt.UniqueConnection) self.thumbnailarea.fetchwidget.dataSent.connect( self.handle_fetching, type=Qt.UniqueConnection) self.thumbnailarea.fetchwidget.rejected.connect( self.thumbnailarea.fetchwidget.hide, type=Qt.UniqueConnection) # Set up tag widget blacklist = self.preferences.tag_blacklist tag_widget = tagwidget.DanbooruTagWidget(blacklist, self) self.tag_display_action.setActive(True) self.tag_display_action.setEnabled(True) self.tag_dock = QDockWidget("Similar tags", self) self.tag_dock.setObjectName("TagDock") self.tag_dock.setAllowedAreas(Qt.RightDockWidgetArea) self.tag_dock.setWidget(tag_widget) #self.tag_dock.setFeatures(QDockWidget.NoDockWidgetFeatures) self.addDockWidget(Qt.RightDockWidgetArea, self.tag_dock) self.tag_dock.hide() # Container signal-slot connections self.setup_connections() def download_finished(self): """Slot called when all the data has been completed. Clears the progress bar and resets it to 0.""" if not self.batch_download_action.isEnabled(): self.batch_download_action.setEnabled(True) self.__step = 0 self.progress.hide() def update_progress(self): "Update the progress bar." if not self.progress.isVisible(): self.progress.show() self.__step += 1 self.progress.setValue(self.__step) def clear(self, clear_pool=True): "Clear the central widget." if self.thumbnailarea is None: return self.thumbnailarea.clear() self.tag_dock.widget().clear() if clear_pool: self.pool_dock.widget().clear() self.batch_download_action.setEnabled(False) def clean_cache(self): "Purge the thumbnail cache." self.cache.discard() self.statusBar().showMessage(i18n("Thumbnail cache cleared.")) def batch_download_slot(self, job): """Slot called when doing batch download, for each file retrieved. If Nepomuk tagging is enabled, each file is tagged using the item's respective tags. """ if job.error(): job.ui().showErrorMessage() else: if self.preferences.nepomuk_enabled: tags = job.property("tags").toPyObject() #danbooru2nepomuk.tag_danbooru_item(job.destUrl().path(), # tags) def tag_display(self, state): """Display or hide the tag dock.""" if self.tag_dock is None: self.tag_display_action.setActive(False) return if state: self.tag_dock.show() else: self.tag_dock.hide()
class ComposerWrapper(QObject): """ Embeds custom STDM tools in a QgsComposer instance for managing map-based STDM document templates. """ dataSourceSelected = pyqtSignal(str) def __init__(self, composerView): QObject.__init__(self, composerView) self._compView = composerView self._stdmTB = self.mainWindow().addToolBar("STDM") self._selectMoveAction = None #Container for custom editor widgets self._widgetMappings = {} #Create dock widget for configuring STDM data source self._stdmDataSourceDock = QDockWidget( QApplication.translate("ComposerWrapper", "STDM Data Source"), self.mainWindow()) self._stdmDataSourceDock.setObjectName("STDMDataSourceDock") self._stdmDataSourceDock.setMinimumWidth(300) self._stdmDataSourceDock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self._stdmDataSourceDock) dataSourceWidget = ComposerDataSourceSelector() self._stdmDataSourceDock.setWidget(dataSourceWidget) self._stdmDataSourceDock.show() #Create dock widget for configuring STDM item properties self._stdmItemPropDock = QDockWidget( QApplication.translate("ComposerWrapper", "STDM data properties"), self.mainWindow()) self._stdmItemPropDock.setObjectName("STDMItemDock") self._stdmItemPropDock.setMinimumWidth(300) self._stdmItemPropDock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self._stdmItemPropDock) self._stdmItemPropDock.show() if self.itemDock() != None: self.mainWindow().tabifyDockWidget(self.itemDock(), self._stdmItemPropDock) if self.atlasDock() != None: self.atlasDock().hide() if self.generalDock() != None: self.generalDock().raise_() #Connect signals self.composition().itemRemoved.connect(self._onItemRemoved) dataSourceWidget.cboDataSource.currentIndexChanged[str].connect( self.propagateDataSourceSelection) self.composerView().selectedItemChanged.connect(self._onItemSelected) #Current template document file self._currDocFile = None def _removeActions(self): """ Remove inapplicable actions and their corresponding toolbars and menus. """ removeActions = [ "mActionSaveProject", "mActionNewComposer", "mActionDuplicateComposer" ] composerToolbar = self.composerMainToolBar() if composerToolbar != None: saveProjectAction = None for itemAction in composerToolbar.actions(): if itemAction.objectName() == "mActionSaveProject": saveProjectAction = itemAction break if saveProjectAction != None: composerMenu = saveProjectAction.menu() def configure(self): #Create instances of custom STDM composer item configurations for ciConfig in ComposerItemConfig.itemConfigurations: ciConfigObj = ciConfig(self) def addWidgetMapping(self, uniqueIdentifier, widget): """ Add custom STDM editor widget based on the unique identifier of the composer item """ self._widgetMappings[uniqueIdentifier] = widget def widgetMappings(self): """ Returns a dictionary containing uuid values of composer items linked to STDM widgets. """ return self._widgetMappings def clearWidgetMappings(self): """ Resets the widget mappings collection. """ self._widgetMappings = {} def mainWindow(self): """ Returns the QMainWindow used by the composer view. """ return self._compView.composerWindow() def stdmToolBar(self): """ Returns the instance of the STDM toolbar added to the QgsComposer. """ return self._stdmTB def composerView(self): """ Returns the composer view. """ return self._compView def composition(self): """ Returns the QgsComposition instance used in the composer view. """ return self._compView.composition() def composerItemToolBar(self): """ Returns the toolbar containing actions for adding composer items. """ return self.mainWindow().findChild(QToolBar, "mItemToolbar") def composerMainToolBar(self): """ Returns the toolbar containing actions for managing templates. """ return self.mainWindow().findChild(QToolBar, "mComposerToolbar") def selectMoveAction(self): """ Returns the QAction for selecting or moving composer items. """ if self.composerItemToolBar() != None: if self._selectMoveAction == None: for itemAction in self.composerItemToolBar().actions(): if itemAction.objectName() == "mActionSelectMoveItem": self._selectMoveAction = itemAction break return self._selectMoveAction def checkedItemAction(self): """ Returns the currently selected composer item action. """ if self.selectMoveAction() != None: return self.selectMoveAction().actionGroup().checkedAction() return None def itemDock(self): """ Get the 'Item Properties' dock widget. """ return self.mainWindow().findChild(QDockWidget, "ItemDock") def atlasDock(self): """ Get the 'Atlas generation' dock widget. """ return self.mainWindow().findChild(QDockWidget, "AtlasDock") def generalDock(self): """ Get the 'Composition' dock widget. """ return self.mainWindow().findChild(QDockWidget, "CompositionDock") def stdmDataSourceDock(self): """ Returns the STDM data source dock widget. """ return self._stdmDataSourceDock def stdmItemDock(self): """ Returns the STDM item dock widget. """ return self._stdmItemPropDock def documentFile(self): """ Returns the QFile instance associated with the current document. 'None' will be returned for new, unsaved documents. """ return self._currDocFile def setDocumentFile(self, docFile): """ Sets the document file. """ if not isinstance(docFile, QFile): return self._currDocFile = docFile def selectedDataSource(self): """ Returns the name of the data source specified by the user. """ return self._stdmDataSourceDock.widget().cboDataSource.currentText() def selectedDataSourceCategory(self): """ Returns the category (view or table) that the data source belongs to. """ if self.stdmDataSourceDock().widget() != None: return self.stdmDataSourceDock().widget().category() return "" def propagateDataSourceSelection(self, dataSourceName): """ Propagates the signal when a user select a data source. Listening objects can hook on to it. """ self.dataSourceSelected.emit(dataSourceName) def loadTemplate(self, filePath): """ Loads a document template into the view and updates the necessary STDM-related controls. """ if not QFile.exists(filePath): QMessageBox.critical(self.composerView(), QApplication.translate("OpenTemplateConfig","Open Template Error"), \ QApplication.translate("OpenTemplateConfig","The specified template does not exist.")) return templateFile = QFile(filePath) if not templateFile.open(QIODevice.ReadOnly): QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Open Operation Error"), \ "{0}\n{1}".format(QApplication.translate("ComposerWrapper","Cannot read template file."), \ templateFile.errorString() )) return templateDoc = QDomDocument() if templateDoc.setContent(templateFile): #Load items into the composition and configure STDM data controls self.composition().loadFromTemplate(templateDoc) self.clearWidgetMappings() #Load data controls composerDS = ComposerDataSource.create(templateDoc) self._configureDataControls(composerDS) #Load symbol editors spatialFieldsConfig = SpatialFieldsConfiguration.create( templateDoc) self._configureSpatialSymbolEditor(spatialFieldsConfig) def saveTemplate(self): """ Creates and saves a new document template. """ #Validate if the user has specified the data source if self.selectedDataSource() == "": QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), \ QApplication.translate("ComposerWrapper","Please specify the " \ "data source name for the document composition.")) return #If it is a new unsaved document template then prompt for the document name. docFile = self.documentFile() if docFile == None: docName,ok = QInputDialog.getText(self.composerView(), \ QApplication.translate("ComposerWrapper","Template Name"), \ QApplication.translate("ComposerWrapper","Please enter the template name below"), \ ) if ok and docName != "": templateDir = self._composerTemplatesPath() if templateDir == None: QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), \ QApplication.translate("ComposerWrapper","Directory for document templates could not be found.")) return absPath = templateDir + "/" + docName + ".sdt" docFile = QFile(absPath) else: return docFileInfo = QFileInfo(docFile) if not docFile.open(QIODevice.WriteOnly): QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Save Operation Error"), \ "{0}\n{1}".format(QApplication.translate("ComposerWrapper","Could not save template file."), \ docFile.errorString() )) return templateDoc = QDomDocument() self._writeXML(templateDoc, docFileInfo.completeBaseName()) if docFile.write(templateDoc.toByteArray()) == -1: QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Save Error"), \ QApplication.translate("ComposerWrapper","Could not save template file.")) return docFile.close() self.setDocumentFile(docFile) def _writeXML(self, xmlDoc, docName): """ Write the template configuration into the XML document. """ #Write default composer configuration composerElement = xmlDoc.createElement("Composer") composerElement.setAttribute("title", docName) composerElement.setAttribute("visible", 1) xmlDoc.appendChild(composerElement) self.composition().writeXML(composerElement, xmlDoc) #Write STDM data field configurations dataSourceElement = ComposerDataSource.domElement(self, xmlDoc) composerElement.appendChild(dataSourceElement) #Write spatial field configurations spatialColumnsElement = SpatialFieldsConfiguration.domElement( self, xmlDoc) dataSourceElement.appendChild(spatialColumnsElement) def _configureDataControls(self, composerDataSource): """ Configure the data source and data field controls based on the composer data source configuration. """ if self.stdmDataSourceDock().widget() != None: #Set data source dataSourceWidget = self.stdmDataSourceDock().widget() dataSourceWidget.setCategory(composerDataSource.category()) dataSourceWidget.setSelectedSource(composerDataSource.name()) #Set data field controls for composerId in composerDataSource.dataFieldMappings().reverse: #Use composer item id since the uuid is stripped off composerItem = self.composition().getComposerItemById( composerId) if composerItem != None: compFieldSelector = ComposerFieldSelector( self, composerItem, self.composerView()) compFieldSelector.selectFieldName( composerDataSource.dataFieldName(composerId)) #Add widget to the collection but now use the current uuid of the composition item self.addWidgetMapping(composerItem.uuid(), compFieldSelector) def _configureSpatialSymbolEditor(self, spatialFieldConfig): """ Configure symbol editor controls. """ if self.stdmDataSourceDock().widget() != None: for itemId, spFieldsMappings in spatialFieldConfig.spatialFieldsMapping( ).iteritems(): mapItem = self.composition().getComposerItemById(itemId) if mapItem != None: composerSymbolEditor = ComposerSymbolEditor( self, self.composerView()) composerSymbolEditor.addSpatialFieldMappings( spFieldsMappings) #Add widget to the collection but now use the current uuid of the composer map self.addWidgetMapping(mapItem.uuid(), composerSymbolEditor) def _composerTemplatesPath(self): """ Reads the path of composer templates in the registry. """ regConfig = RegistryConfig() keyName = "ComposerTemplates" valueCollection = regConfig.read([keyName]) if len(valueCollection) == 0: return None else: return valueCollection[keyName] def _onItemRemoved(self, item): """ Slot raised when a composer item is removed from the scene. """ """ Code will not work since a QObject instance is returned instead of a QgsComposerItem if item.uuid() in self._widgetMappings: del self._widgetMappings[item.uuid()] """ pass def _onItemSelected(self, item): """ Slot raised when a composer item is selected. Load the corresponding field selector if the selection is an STDM data field label. QComposerLabel is returned as a QObject in the slot argument hence, we have resorted to capturing the currently selected items in the composition. """ selectedItems = self.composition().selectedComposerItems() if len(selectedItems) == 0: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) == 1: composerItem = selectedItems[0] if composerItem.uuid() in self._widgetMappings: stdmWidget = self._widgetMappings[composerItem.uuid()] if stdmWidget == self._stdmItemPropDock.widget(): return else: self._stdmItemPropDock.setWidget(stdmWidget) #Playing it safe in applying the formatting for the editor controls where applicable itemFormatter = None if isinstance(composerItem, QgsComposerArrow): itemFormatter = LineFormatter() elif isinstance(composerItem, QgsComposerLabel): itemFormatter = DataLabelFormatter() elif isinstance(composerItem, QgsComposerMap): itemFormatter = MapFormatter() if itemFormatter != None: itemFormatter.apply(composerItem, self, True) else: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) > 1: self._stdmItemPropDock.setWidget(None)