예제 #1
0
class LabControl(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        # self.ui=uic.loadUi("mainwindow.ui",self)
        self.DelayStageDock = QDockWidget("DelayStage", self)
        self.DelayStageDock.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.DelayStageDock.setWidget(DelayStageWidget(self))
        self.DelayStageDock.widget().setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.addDockWidget(Qt.TopDockWidgetArea, self.DelayStageDock)

        self.DelayStageDock.widget().delaystage.throwMessage.connect(self.getMessage)

    def on_actionDelayStage_triggered(self):
        self.close()

    def getMessage(self, text, level):
        print (text)
예제 #2
0
class LabControl(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        #self.ui=uic.loadUi("mainwindow.ui",self)
        self.DelayStageDock=QDockWidget("DelayStage",self)
        self.DelayStageDock.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed)
        self.DelayStageDock.setWidget(DelayStageWidget(self))
        self.DelayStageDock.widget().setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed)
        self.addDockWidget(Qt.TopDockWidgetArea,self.DelayStageDock)

        self.DelayStageDock.widget().delaystage.throwMessage.connect(self.getMessage)
        
    def on_actionDelayStage_triggered(self):
        self.close()

    def getMessage(self,text,level):
        print(text)
예제 #3
0
class PluginManualIntegrator(MSPlugin):
    def __init__(self, model, view, parent=None):
        MSPlugin.__init__(self, model, view, parent)
        #self.connect(self.view.mdiArea, SIGNAL('subWindowActivated(QMdiSubWindow*)'), self.updateCurrentWindow)
        self.guiWidget = []

        self.view.mdiArea.subWindowActivated.connect(self.updateCurrentWindow)
        self.x1val = 0

        self.dock = QDockWidget("Manual Integrator")
        self.guiWidget.append(self.dock)
        self.window = self.view.mdiArea.activeSubWindow(
        ) if self.view.mdiArea.activeSubWindow() else None
        if self.window is not None:
            #self.connect(self.window.widget().pw,
            #            SIGNAL(self.window.widget().pw.sigMouseReleased),
            #            self.pluginAlgorithm)
            self.window.widget().pw.plotItem.vb.sigClick.connect(
                self.pluginAlgorithm)

        self.label = QLabel('Inactive') if not self.window else QLabel(
            'Active')
        self.dock.setWidget(self.label)
        self._guiUpdate()

    def _guiUpdate(self):
        self.view.addDockWidget(
            0x1, self.dock
        )  #showInformationMessage('test', "Hello From Integrator Plugin")

    def _buildConnections(self):
        pass

    def updateCurrentWindow(self, win):
        if win is None: return
        #print "update current window", win
        self.window = win
        #self.connect(self.window.widget().pw,
        #            SIGNAL(self.window.widget().pw.sigMouseReleased),
        #            self.pluginAlgorithm)
        self.window.widget().pw.plotItem.vb.sigClick.connect(
            self.pluginAlgorithm)
        self.dock.setWidget(Widget(self.dock))

    def pluginAlgorithm(self, e):
        """
        receive the event off release MouseEvent
        
        """
        if not self.x1val:
            self.x1val = e.x()
            self.dock.widget().x1.setText(str(self.x1val))
            self.dock.widget().x2.setText('')
            self.dock.widget().result.setText('')
            return

        x2 = e.x()
        self.dock.widget().x2.setText(str(x2))
        chrom = self.window.widget().data[0]  #take the first one
        res = chrom.integrationBtw(self.x1val, x2)
        self.dock.widget().result.setText(",success:".join(
            [str(res[2]), str(res[3])]))
        self.x1val = 0
        #self.reinitialize()

    def reinitialize(self):
        self.x1val = 0
        self.dock.widget().x2.setText('')
        self.dock.widget().x1.setText('')

    def unload(self):
        for e in self.guiWidget:
            e.setParent(None)
            del e
        self.view.update()
예제 #4
0
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)
예제 #5
0
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)
예제 #6
0
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')))
예제 #7
0
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()
예제 #8
0
class ConflictWindow(QMainWindow):

    mergeCompleted = pyqtSignal()

    def __init__(self, layer, type='conflict', parent=None):
        QMainWindow.__init__(self)

        self.iface = parent.iface
        self.layer = layer
        self.parent = parent
        self.layer_list = parent.layer_list
        settings = QSettings()
        self.tools = PgVersionTools(self)

        # Restore Window Settings
        settings = QSettings()
        try:
            self.restoreGeometry(
                pybytearray(settings.value("/pgVersion/geometry")))
            self.restoreState(
                pybytearray(settings.value("/pgVersion/windowState")))
        except:
            pass

        settings = QSettings()
        self.canvas = QgsMapCanvas()
        self.canvas.setCanvasColor(Qt.white)
        self.canvas.enableAntiAliasing(
            settings.value("/qgis/enable_anti_aliasing", True, type=bool))
        self.canvas.useImageToRender(
            settings.value("/qgis/use_qimage_to_render", True, type=bool))
        action = pyint(settings.value("/qgis/wheel_action", 0))
        zoomFactor = pyint(settings.value("/qgis/zoom_factor", 2))
        self.canvas.setWheelAction(QgsMapCanvas.WheelAction(action),
                                   zoomFactor)

        QgsMapLayerRegistry.instance().addMapLayer(layer)
        self.canvas.setExtent(layer.extent())
        self.canvas.setLayerSet([QgsMapCanvasLayer(layer)])
        self.canvas.zoomToFullExtent()
        self.setCentralWidget(self.canvas)

        actionZoomIn = QAction(
            QIcon(":/plugins/pgversion/icons/mActionZoomIn.png"),
            pystring("Zoom in"), self)
        actionZoomOut = QAction(
            QIcon(":/plugins/pgversion/icons/mActionZoomOut.png"),
            pystring("Zoom out"), self)
        actionZoomFull = QAction(
            QIcon(":/plugins/pgversion/icons/mActionZoomFullExtent.png"),
            pystring("Fullextent"), self)
        actionPan = QAction(QIcon(":/plugins/pgversion/icons/mActionPan.png"),
                            pystring("Pan"), self)

        actionZoomIn.setCheckable(True)
        actionZoomOut.setCheckable(True)
        actionPan.setCheckable(True)

        actionZoomIn.triggered.connect(self.zoomIn)
        actionZoomOut.triggered.connect(self.zoomOut)
        actionZoomFull.triggered.connect(self.zoomFull)
        actionPan.triggered.connect(self.pan)

        self.toolbar = self.addToolBar("Canvas actions")
        self.toolbar.addAction(actionPan)
        self.toolbar.addAction(actionZoomIn)
        self.toolbar.addAction(actionZoomOut)
        self.toolbar.addAction(actionZoomFull)

        self.dockWidget = QDockWidget()
        self.dockWidget.setWidget(QTableWidget())
        self.addDockWidget(Qt.BottomDockWidgetArea, self.dockWidget)
        self.tabView = self.dockWidget.widget()

        # create the map tools
        self.toolPan = QgsMapToolPan(self.canvas)
        self.toolPan.setAction(actionPan)
        self.toolZoomIn = QgsMapToolZoom(self.canvas, False)  # false = in
        self.toolZoomIn.setAction(actionZoomIn)
        self.toolZoomOut = QgsMapToolZoom(self.canvas, True)  # true = out
        self.toolZoomOut.setAction(actionZoomOut)

        self.pan()

        self.conflictLayerList = []

        if type == 'conflict':
            self.cmbMerge = QComboBox()
            self.cmbMerge.addItems(self.tools.confRecords(self.layer))

            self.btnMerge = QPushButton()
            self.cmbMerge.currentIndexChanged.connect(self.toggleBtnMerge)
            self.btnMerge.setText(self.tr('solve conflict'))

            self.toolbar.addWidget(self.cmbMerge)
            self.toolbar.addWidget(self.btnMerge)
            self.manageConflicts()
            self.btnMerge.clicked.connect(self.runMerge)

        self.tabView.itemSelectionChanged.connect(self.showConfObject)
        self.rBand = QgsRubberBand(self.canvas, False)

    def toggleBtnMerge(self):
        #      if not self.btnMerge.isEnabled():
        #          self.btnMerge.setEnabled(False)
        #      else:
        #          self.btnMerge.setEnabled(True)
        return

    def toggleBtnCommit(self):
        #      if not self.btnMerge.isEnabled():
        #          self.btnMerge.setEnabled(False)
        #      else:
        #          self.btnMerge.setEnabled(True)
        return

    def zoomIn(self):
        self.canvas.setMapTool(self.toolZoomIn)

    def zoomOut(self):
        self.canvas.setMapTool(self.toolZoomOut)

    def zoomFull(self):
        self.canvas.zoomToFullExtent()

    def pan(self):
        self.canvas.setMapTool(self.toolPan)

    def closeEvent(self, e):
        """ save window state """
        #      settings = QSettings()
        #      settings.setValue("/pgVersion/windowState", pybytearray(self.saveState()))
        #      settings.setValue("/pgVersion/geometry", pybytearray(self.saveGeometry()))
        QgsMapLayerRegistry.instance().removeMapLayer(
            self.tools.conflictLayer(self.layer).id())
        self.close()

    def showDiffs(self):
        pass

    def showConfObject(self):

        myProject = self.tabView.item(self.tabView.currentRow(), 0).text()
        myId = self.tabView.item(self.tabView.currentRow(), 3).text()

        confLayer = self.tools.conflictLayer(self.layer)
        self.conflictLayerList.append(confLayer)

        self.rBand.reset()
        self.rBand.setColor(QColor(0, 0, 255))
        self.rBand.setWidth(5)

        if confLayer.isValid():
            iter = confLayer.getFeatures()
            for feature in iter:
                geom = feature.geometry()
                attrs = feature.attributes()
                if str(attrs[0]) == myId and attrs[len(attrs) -
                                                   5] == myProject:
                    self.rBand.addGeometry(geom, None)

        return

    def manageConflicts(self):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        vLayer = self.tools.conflictLayer(self.layer)
        if vLayer == None:
            return
        vLayerList = [vLayer, self.layer]
        QgsMapLayerRegistry.instance().addMapLayers(vLayerList, False)
        self.vLayerId = vLayer.id()
        self.canvas.setExtent(vLayer.extent())
        self.canvas.refresh()
        vLayer.triggerRepaint()

        tabData = self.tools.tableRecords(self.layer)

        if tabData <> None:
            self.tools.createGridView(self.tabView, tabData[0], tabData[1],
                                      100, 10)
        else:
            QApplication.restoreOverrideCursor()
            self.mergeCompleted.emit()

        QApplication.restoreOverrideCursor()

    def runMerge(self):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        currentLayer = self.layer
        object = self.cmbMerge.currentText()

        if currentLayer == None:
            QMessageBox.information(
                None, self.tr('Notice'),
                self.tr('Please select a versioned layer for committing'))
            return
        else:
            myDb = self.tools.layerDB('Merge', currentLayer)
            mySchema = self.tools.layerSchema(currentLayer)
            myTable = self.tools.layerTable(currentLayer).replace(
                "_version", "")

        objectArray = object.split(" - ")
        if len(objectArray) == 1:
            return
        elif 'Commit all' in objectArray[0]:
            projectName = objectArray[1].strip()
            sql = "select objectkey from versions.pgvscheck('%s.%s') \
             where myuser = '******' or conflict_user = '******' \
             order by objectkey" % (mySchema, myTable, projectName,
                                    projectName)

            result = myDb.read(sql)
            for i in range(len(result['OBJECTKEY'])):
                sql = "select versions.pgvsmerge('%s.%s',%s,'%s')" % (
                    mySchema, myTable, result['OBJECTKEY'][i], projectName)
                myDb.run(sql)
        else:
            projectName = objectArray[1].strip()
            sql = "select versions.pgvsmerge('%s.%s',%s,'%s')" % (
                mySchema, myTable, objectArray[0], projectName)
            myDb.run(sql)

        self.canvas.refresh()
        self.layer.triggerRepaint()
        self.cmbMerge.clear()
        QApplication.restoreOverrideCursor()

        if self.tools.confRecords(self.layer) == None:
            self.tabView.clear()
            self.tools.setModified(self.parent.layer_list)
            self.close()

        else:
            self.cmbMerge.addItems(self.tools.confRecords(self.layer))
            tabData = self.tools.tableRecords(self.layer)
            self.tools.createGridView(self.tabView, tabData[0], tabData[1],
                                      100, 10)
        self.manageConflicts()

    def loadLayer(self, layer):
        provider = layer.dataProvider()
        uri = provider.dataSourceUri()
        mySchema = self.tools.layerSchema(layer)
        myHost = self.tools.layerHost(layer)
        myDatabase = self.tools.layerDB(layer)
        myPassword = self.tools.layerPassword(layer)
        myPort = self.tools.layerPort(layer)
        myUsername = self.tools.layerUsername(layer)
        myGeometryColumn = self.tools.layerGeomCol(layer)

        myTable = myTable.replace("_version", "")

        myUri = QgsDataSourceURI()
        myUri.setConnection(myHost, myPort, myDatabase, myUsername, myPassword)
        myUri.setDataSource(mySchema, myTable, myGeometryColumn)

        myLayer = QgsVectorLayer(myUri.uri(), "", "postgres")

        QgsMapLayerRegistry.instance().addMapLayer(myLayer, False)
        #      self.canvas.setExtent(myLayer.extent())
        self.canvas.setLayerSet([QgsMapCanvasLayer(myLayer)])
        self.canvas.refresh()
예제 #9
0
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)
예제 #10
0
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()
예제 #11
0
class PluginManualIntegrator(MSPlugin):
    def __init__(self, model, view, parent=None):
        MSPlugin.__init__(self, model, view, parent)
        #self.connect(self.view.mdiArea, SIGNAL('subWindowActivated(QMdiSubWindow*)'), self.updateCurrentWindow)
        self.guiWidget=[]
        
        self.view.mdiArea.subWindowActivated.connect(self.updateCurrentWindow)
        self.x1val=0
        
        self.dock=QDockWidget("Manual Integrator")
        self.guiWidget.append(self.dock)
        self.window=self.view.mdiArea.activeSubWindow() if self.view.mdiArea.activeSubWindow() else None
        if self.window is not None:
            #self.connect(self.window.widget().pw, 
            #            SIGNAL(self.window.widget().pw.sigMouseReleased), 
            #            self.pluginAlgorithm)
            self.window.widget().pw.plotItem.vb.sigClick.connect(self.pluginAlgorithm)            
        
        
        self.label=QLabel('Inactive') if not self.window else QLabel('Active')
        self.dock.setWidget(self.label)
        self._guiUpdate()
        
    
    def _guiUpdate(self):
        self.view.addDockWidget(0x1, self.dock)#showInformationMessage('test', "Hello From Integrator Plugin")
        
    
    def _buildConnections(self):pass
    
    
    
    def updateCurrentWindow(self, win):
        if win is None:return
        #print "update current window", win
        self.window=win
        #self.connect(self.window.widget().pw, 
        #            SIGNAL(self.window.widget().pw.sigMouseReleased), 
        #            self.pluginAlgorithm)
        self.window.widget().pw.plotItem.vb.sigClick.connect(self.pluginAlgorithm)
        self.dock.setWidget(Widget(self.dock))
        

    def pluginAlgorithm(self, e):
        """
        receive the event off release MouseEvent
        
        """
        if not self.x1val:
            self.x1val=e.x()
            self.dock.widget().x1.setText(str(self.x1val))
            self.dock.widget().x2.setText('')
            self.dock.widget().result.setText('')
            return
        
        x2=e.x()
        self.dock.widget().x2.setText(str(x2))
        chrom=self.window.widget().data[0] #take the first one
        res=chrom.integrationBtw(self.x1val, x2)
        self.dock.widget().result.setText(",success:".join([str(res[2]), str(res[3])]))
        self.x1val=0
        #self.reinitialize()
        
    def reinitialize(self):
        self.x1val=0
        self.dock.widget().x2.setText('')
        self.dock.widget().x1.setText('')
    
    
    def unload(self):
        for e in self.guiWidget:
            e.setParent(None)
            del e
        self.view.update()
예제 #12
0
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)