class SortedListByThread(QObject):
    def __init__(self):
        super(SortedListByThread, self).__init__()
        self.thread = self.worker = None

    def run(self, lst, key, reverse):
        def finished(l_sorted):
            self.sortedList = l_sorted
            loop.exit()

        self.worker = WorkerSorted(lst, key, reverse)
        self.thread = QThread(self)
        loop = QEventLoop()
        self.sortedList = None

        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.worker.finished.connect(finished)
        self.thread.start()
        loop.exec_()
        self._finishThread()

        return self.sortedList

    def kill(self):
        if not self.thread is None:
            self._finishThread()

    def _finishThread(self):
        self.thread.quit()
        self.thread.wait()
        self.thread.deleteLater()
        self.thread = self.worker = None
示例#2
0
class SentinelDownloader:
    '''
    QGIS Plugin Implementation.
    '''
    def __init__(self, iface):
        '''
        Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        '''
        #
        # Save reference to the QGIS interface.
        #
        self.iface = iface

        #
        # Initialize class variables.
        #
        self.dlg = None
        self.downloader = None
        self.worker = None
        self.workerD = None
        self.thread = None
        self.threadD = None

        #
        # Initialize plugin directory.
        #
        self.plugin_dir = os.path.dirname(__file__)

        #
        # Initialize locale.
        #
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(self.plugin_dir, 'i18n',
                                   'SentinelDownloader_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        #
        # Declare instance attributes.
        #
        self.actions = []

        #
        # Set plugin label under 'Web' tab in QGIS toolbar.
        #
        self.menu = self.tr(u'&Sentinel Downloader')

        # TODO: We are going to let the user set this up in a future iteration
        self.toolbar = self.iface.addToolBar(u'SentinelDownloader')
        self.toolbar.setObjectName(u'SentinelDownloader')

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        '''
        Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        '''
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('SentinelDownloader', message)

    def add_action(self,
                   icon_path,
                   text,
                   callback,
                   enabled_flag=True,
                   add_to_menu=True,
                   add_to_toolbar=True,
                   status_tip=None,
                   whats_this=None,
                   parent=None):
        '''
        Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        '''

        #
        # Create the dialog (after translation) and keep reference.
        #
        self.dlg = SentinelDownloaderDialog()

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToWebMenu(self.menu, action)

        self.actions.append(action)
        '''
        TODO: Need to add threading for button actions.
        '''

        self.downloader = SentinelSearch(self.dlg)

        #
        # Button actions.
        #
        self.dlg.writeDir_toolButton.clicked.connect(self.downloader.open)
        self.dlg.btnExtent.clicked.connect(self.get_extent)
        self.dlg.btnSearch.clicked.connect(self.search_thread)
        self.dlg.btnSearchCancel.clicked.connect(self.kill)
        self.dlg.btnReset.clicked.connect(self.reset_parameters)
        self.dlg.btnTileSearch.clicked.connect(self.downloader.get_tile_coords)
        self.dlg.btnClearSelected.clicked.connect(
            self.downloader.remove_selected)
        self.dlg.btnDownload.clicked.connect(self.download_thread)
        # self.dlg.btnDownload.clicked.connect(self.download_S1thread)
        self.dlg.btnDownloadCancel.clicked.connect(self.kill)
        self.dlg.btnClear.clicked.connect(self.downloader.clearTable)

        return action

    def initGui(self):
        '''
        Create the menu entries and toolbar icons inside the QGIS GUI.
        '''

        icon_path = ':/plugins/SentinelDownloader/icon.png'
        self.add_action(icon_path,
                        text=self.tr(u'Sentinel Downloader'),
                        callback=self.run,
                        parent=self.iface.mainWindow())

    def unload(self):
        '''
        Removes the plugin menu item and icon from QGIS GUI.
        '''

        for action in self.actions:
            self.iface.removePluginWebMenu(self.tr(u'&Sentinel Downloader'),
                                           action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar

    def run(self):
        '''
        Run method that performs all the real work.
        '''

        #
        # Show the dialog.
        #
        self.dlg.show()

        # # Run the dialog event loop
        # result = self.dlg.exec_()
        # # See if OK was pressed
        # if result:
        #     # Do something useful here - delete the line containing pass and
        #     # substitute with your code.
        #     pass

    def get_extent(self):
        '''
        Returns current extent shown in QGIS as upper and lower limits for both
        X and Y axis in the current projection.
        '''

        #
        # Get current extent coordinates.
        #
        e = self.iface.mapCanvas().extent()

        ULX = e.xMaximum()
        ULY = e.yMaximum()
        LLX = e.xMinimum()
        LLY = e.yMinimum()

        #
        # Determine origin CRS and define destination CRS (EPSG:4326).
        #
        canvas = self.iface.mapCanvas()
        epsg_code = canvas.mapRenderer().destinationCrs().authid()
        inProj = pyproj.Proj(init=epsg_code)
        outProj = pyproj.Proj(init='epsg:4326')

        #
        # Transform coordinates
        #
        ULX_new, ULY_new = pyproj.transform(inProj, outProj, ULX, ULY)
        LLX_new, LLY_new = pyproj.transform(inProj, outProj, LLX, LLY)

        #
        # Set values.
        #
        self.dlg.LLX_lineEdit.setText(str('{0:.2f}'.format(LLX_new)))
        self.dlg.ULX_lineEdit.setText(str('{0:.2f}'.format(ULX_new)))
        self.dlg.LLY_lineEdit.setText(str('{0:.2f}'.format(LLY_new)))
        self.dlg.ULY_lineEdit.setText(str('{0:.2f}'.format(ULY_new)))

    def search_thread(self):
        '''
        Creates Qt thread for searching so that the GUI does not freeze and
        QGIS does not crash.
        '''

        #
        # Disable search button while searching.
        #
        self.dlg.btnSearch.setEnabled(False)
        self.dlg.btnSearchCancel.setEnabled(True)

        self.worker = SentinelSearch(self.dlg)
        self.thread = QThread()
        self.worker.moveToThread(self.thread)

        self.thread.start()
        self.thread.started.connect(self.worker.get_query_xml)

        #
        # Signal handelling from thread.
        #
        self.worker.query_check.connect(self.query_search_check)
        self.worker.connecting_message.connect(self.set_search_label)
        self.worker.searching_message.connect(self.set_search_label)
        self.worker.enable_btnSearch.connect(self.enable_btnSearch)
        self.worker.search_progress_max.connect(self.set_progress_max)
        self.worker.search_progress_set.connect(self.set_progress)
        self.worker.set_message.connect(self.set_messageBar)
        self.worker.finished.connect(self.search_finished)

    def query_search_check(self, header):

        if header == 'Inconsistent geometries.':
            self.text_to_messagebox(header,
                                    'Pick one polygon, point or no geometry.')

        elif header == 'Incomplete point.':

            self.text_to_messagebox(header,
                                    'Point is missing one coordinate value.')

        elif header == 'Incomplete polygon.':

            self.text_to_messagebox(
                header, 'Polygon is missing at least one coordinate value.')

        elif header == 'No parameters.':

            self.text_to_messagebox(header,
                                    'Please enter at least one parameter.')

        elif header == 'Missing authorization credentials.':

            self.text_to_messagebox(
                header, 'Please enter both a username and password.')

        elif header == 'Tile extraction error':

            self.text_to_messagebox(
                header,
                'Tile extraction option can only be used for Sentinel-2')

        elif header == 'Error with connection.':

            message = (
                'Error with connection.\n'
                'Please check credentials, try another hub or try again '
                'later.')

            self.text_to_messagebox(header, message)

        elif header == 'Error with hub.':

            message = ('Error with connection.\n'
                       'Please try another hub or try again later.')

            self.text_to_messagebox(header, message)

        elif header == 'No write directory.':

            self.text_to_messagebox(
                header, 'Please enter a directory to download the data to!')

        else:
            self.text_to_messagebox('Error.', header)

    def set_search_label(self, text):

        self.dlg.search_label.setText(text)

        if text == 'Connecting . . .':
            #
            # This makes progressBar move until real max is set.
            #
            self.dlg.search_progressBar.setMinimum(0)
            self.dlg.search_progressBar.setMaximum(0)

    def set_progress_max(self, max_value):

        if max_value == 0:
            #
            # If there are no entries, reset bar to avoid problems.
            #
            self.dlg.search_progressBar.setMinimum(0)
            self.dlg.search_progressBar.reset()

        else:
            self.dlg.search_progressBar.setMinimum(0)
            self.dlg.search_progressBar.setMaximum(100)

    def set_progress(self, percent):

        if percent > 100:
            percent = 100

        self.dlg.search_progressBar.setValue(percent)

    def enable_btnSearch(self):

        self.dlg.btnSearch.setEnabled(True)
        self.dlg.btnSearchCancel.setEnabled(False)

    def search_finished(self, killed=False):

        if killed is False:

            self.text_to_messagebox('Done!', 'Done fetching search results!')

        #
        # Clean up Thread.
        #
        self.worker.deleteLater()
        self.thread.quit()
        self.thread.deleteLater()

        #
        # Enable search button after searching.
        #
        self.dlg.btnSearch.setEnabled(True)
        self.dlg.btnSearchCancel.setEnabled(False)

        #
        # Clear progress bar widget and label.
        #
        self.dlg.search_progressBar.setMinimum(0)
        self.dlg.search_progressBar.setMaximum(100)
        self.dlg.search_progressBar.reset()
        self.set_search_label('')

    def kill(self):
        '''
        Starts to kill either the search or download processes, which are
        both initiated by Cancel buttons. It also disables the Cancel
        buttons such that the user does not crash QGIS.
        '''

        if self.worker:

            self.worker.killed = True
            self.dlg.btnSearchCancel.setEnabled(False)
            self.dlg.search_label.setText('Cancelling . . .')

        if self.workerD:

            self.workerD.killed = True
            self.dlg.btnDownloadCancel.setEnabled(False)
            self.dlg.download_label.setText('Cancelling . . .')

    def reset_parameters(self):
        '''
        Sets all user input back to default values. Initaited by the user
        clicking the reset button.
        '''

        self.dlg.sensor_comboBox.setCurrentIndex(0)

        self.dlg.maxRecords_spinBox.setValue(100)
        self.dlg.orderBy_comboBox.setCurrentIndex(0)
        self.dlg.LLX_lineEdit.clear()
        self.dlg.ULX_lineEdit.clear()
        self.dlg.LLY_lineEdit.clear()
        self.dlg.ULY_lineEdit.clear()
        self.dlg.lat_lineEdit.clear()
        self.dlg.lon_lineEdit.clear()
        self.dlg.s2Tile_lineEdit.clear()
        self.dlg.s2Extract_checkBox.setChecked(False)

        today = QDate.currentDate()
        self.dlg.ingestFrom_dateEdit.setEnabled(False)
        self.dlg.ingestFrom_dateEdit.setDate(QDate(2013, 04, 13))
        self.dlg.ingestTo_dateEdit.setEnabled(False)
        self.dlg.ingestTo_dateEdit.setDate(today)
        self.dlg.ingest_enable.setChecked(False)
        self.dlg.dateFrom_dateEdit.setEnabled(False)
        self.dlg.dateFrom_dateEdit.setDate(QDate(2013, 04, 13))
        self.dlg.dateTo_dateEdit.setEnabled(False)
        self.dlg.dateTo_dateEdit.setDate(today)
        self.dlg.date_enable.setChecked(False)

        self.dlg.orbit_lineEdit.clear()
        self.dlg.relOrbit_radioButton.setChecked(True)
        self.dlg.orbitDir_comboBox.setCurrentIndex(0)

        self.dlg.s1Mode_comboBox.setCurrentIndex(0)
        self.dlg.s1Polar_comboBox.setCurrentIndex(0)
        self.dlg.s1Product_comboBox.setCurrentIndex(0)

        self.dlg.s2Product_comboBox.setCurrentIndex(0)
        self.dlg.cloudCover_spinBox.setValue(10)
        self.dlg.cloudCover_enable.setChecked(False)

    def download_thread(self):
        '''
        Starts a download thread. Initiated by the user clicking the download
        button.
        '''

        self.dlg.btnDownload.setEnabled(False)
        self.dlg.btnDownloadCancel.setEnabled(True)

        self.workerD = SentinelSearch(self.dlg)
        self.threadD = QThread()
        self.workerD.moveToThread(self.threadD)

        self.threadD.start()
        self.threadD.started.connect(self.workerD.download_results)
        self.workerD.query_check.connect(self.query_search_check)
        self.workerD.download_progress_set.connect(self.set_download_progress)
        self.workerD.download_message.connect(self.set_download_label)
        self.workerD.connecting_message.connect(self.set_download_label)
        self.workerD.download_progress_max.connect(self.set_download_max)

        self.workerD.finished_download.connect(self.download_finished)

    def set_download_progress(self, percent):
        '''
        Sets the download progress bar for the overall download progress in
        percent, based on the total bits to download according to the products
        sizes in the GUI tables.
        '''

        if percent > 100:
            percent = 100

        self.dlg.download_progressBar.setValue(percent)

    def set_download_max(self, max_value):

        if max_value == 0:
            #
            # If there are no entries, reset bar to avoid problems.
            #
            self.dlg.download_progressBar.setMinimum(0)
            self.dlg.download_progressBar.reset()

        else:
            self.dlg.download_progressBar.setMinimum(0)
            self.dlg.download_progressBar.setMaximum(100)

    def set_download_label(self, text):

        self.dlg.download_label.setText(text)

        if text == 'Connecting . . .':
            #
            # This makes progressBar move until real max is set.
            #
            self.dlg.download_progressBar.setMinimum(0)
            self.dlg.download_progressBar.setMaximum(0)

    def download_finished(self, killed=False):
        '''
        Cleans up the download thread once downloading is complete, or if the
        process was killed by the user.
        '''

        if killed is False:

            self.text_to_messagebox('Done!',
                                    'Done downloading Sentinel products!')

        #
        # Clean up Thread.
        #
        self.workerD.deleteLater()
        self.threadD.quit()
        self.threadD.deleteLater()

        #
        # Enable search button after searching.
        #
        self.dlg.btnDownload.setEnabled(True)
        self.dlg.btnDownloadCancel.setEnabled(False)

        #
        # Clear progress bar widget and label.
        #
        self.dlg.download_progressBar.setMinimum(0)
        self.dlg.download_progressBar.setMaximum(100)
        self.dlg.download_progressBar.reset()
        self.dlg.download_label.setText('')

    @staticmethod
    def text_to_messagebox(header, message, long_text=None):
        '''
        Prints a given message to a pop-up message box.
        '''

        msg_txt = QMessageBox()
        msg_txt.setIcon(QMessageBox.Information)
        msg_txt.setText(message)
        # msg_txt.setInformativeText("This is additional information")
        msg_txt.setWindowTitle(header)
        if long_text is not None:
            msg_txt.setDetailedText(long_text)
        msg_txt.exec_()

    def set_messageBar(self, message):
        '''
        Prints the total size of all products in the GUI tables to the QGIS
        message bar.
        '''

        self.iface.messageBar().pushMessage('Results', message, duration=30)
示例#3
0
class XdkWindow(QMainWindow):
    """ """
    loadFileRequested = Signal(str)
    
    def __init__( self, parent = None ):
        super(XdkWindow, self).__init__( parent )
        
        # load the user interface
        projexui.loadUi(__file__, self)
        
        # define custom properties
        self._currentContentsIndex = -1
        self._worker = XdkWorker()
        self._workerThread = QThread()
        self._worker.moveToThread(self._workerThread)
        self._workerThread.start()
        
        # set default properties
        self.setAcceptDrops(True)
        self.setAttribute( Qt.WA_DeleteOnClose )
        self.uiFindNextBTN.setDefaultAction(self.uiFindNextACT)
        self.uiFindPrevBTN.setDefaultAction(self.uiFindPrevACT)
        self.uiFindWIDGET.setVisible(False)
        self.uiSearchWEB.page().setLinkDelegationPolicy(
                                            QWebPage.DelegateAllLinks)
        
        self.refreshUi()
        
        # connect widgets
        self.uiContentsTAB.currentChanged.connect(self.refreshUi)
        self.uiContentsTAB.tabCloseRequested.connect(self.closeContentsWidget)
        self.uiContentsTREE.itemExpanded.connect(self.loadItem)
        self.uiContentsTREE.itemSelectionChanged.connect(self.refreshContents)
        self.uiSearchTXT.returnPressed.connect(self.search)
        self.uiSearchWEB.linkClicked.connect(self.gotoUrl)
        self.uiIndexTREE.itemSelectionChanged.connect(self.refreshFromIndex)
        
        # connect find actions
        self.uiBackACT.triggered.connect(self.goBack)
        self.uiForwardACT.triggered.connect(self.goForward)
        self.uiHomeACT.triggered.connect(self.goHome )
        self.uiFindTXT.textChanged.connect( self.findNext )
        self.uiFindTXT.returnPressed.connect( self.findNext )
        self.uiFindNextACT.triggered.connect( self.findNext )
        self.uiFindPrevACT.triggered.connect( self.findPrev )
        self.uiFindACT.triggered.connect(self.showFind)
        self.uiFindCloseBTN.clicked.connect(self.uiFindWIDGET.hide)
        self.uiCopyTextACT.triggered.connect( self.copyText )
        
        # connect zoom actions
        self.uiZoomResetACT.triggered.connect( self.zoomReset )
        self.uiZoomInACT.triggered.connect( self.zoomIn )
        self.uiZoomOutACT.triggered.connect( self.zoomOut )
        
        # connect file actions
        self.uiLoadACT.triggered.connect( self.loadFilename )
        self.uiNewTabACT.triggered.connect( self.addContentsWidget )
        self.uiCloseTabACT.triggered.connect( self.closeContentsWidget )
        self.uiQuitACT.triggered.connect( self.close )
        
        # connect the signals
        self.loadFileRequested.connect(self._worker.loadFile)
        self._worker.loadingFinished.connect(self.__addXdkItem)
        QApplication.instance().aboutToQuit.connect(self.__cleanupWorker)
    
    def __del__(self):
        self.__cleanupWorker()
    
    def __addXdkItem(self, filename):
        item = XdkItem(filename)
        
        # add the xdk content item
        self.uiContentsTREE.addTopLevelItem(item)
        
        # add the index list items
        self.uiIndexTREE.blockSignals(True)
        self.uiIndexTREE.setUpdatesEnabled(False)
        for name, url in item.indexlist():
            item = XTreeWidgetItem([name])
            item.setToolTip(0, url)
            item.setFixedHeight(22)
            self.uiIndexTREE.addTopLevelItem(item)
        self.uiIndexTREE.blockSignals(False)
        self.uiIndexTREE.setUpdatesEnabled(True)
        self.uiIndexTREE.sortByColumn(0, Qt.AscendingOrder)
        
        self.unsetCursor()
    
    def __cleanupWorker(self):
        if self._workerThread is None:
            return
            
        self._workerThread.quit()
        self._workerThread.wait()
        
        self._worker.deleteLater()
        self._workerThread.deleteLater()
        
        self._worker = None
        self._workerThread = None
    
    def __gotoUrl(self, url):
        if url.toLocalFile():
            self.gotoUrl(url.toString())
        else:
            webbrowser.open(str(url.toString()))
    
    def addContentsWidget( self ):
        """
        Adds a new contents widget tab into the contents tab.
        
        :return     <QWebView>
        """
        curr_widget = self.currentContentsWidget()
        widget = QWebView(self)
        page = widget.page()
        page.setLinkDelegationPolicy(page.DelegateAllLinks)
            
        self.uiContentsTAB.blockSignals(True)
        self.uiContentsTAB.addTab(widget, 'Documentation')
        self.uiContentsTAB.setCurrentIndex(self.uiContentsTAB.count() - 1)
        self.uiContentsTAB.blockSignals(False)
        
        self._currentContentsIndex = self.uiContentsTAB.count() - 1
        
        if curr_widget:
            widget.setUrl(curr_widget.url())
        
        widget.titleChanged.connect(self.refreshUi)
        widget.linkClicked.connect(self.__gotoUrl)
        
        return widget
    
    def closeContentsWidget( self ):
        """
        Closes the current contents widget.
        """
        widget = self.currentContentsWidget()
        if ( not widget ):
            return
            
        widget.close()
        widget.setParent(None)
        widget.deleteLater()
    
    def copyText( self ):
        """
        Copies the selected text to the clipboard.
        """
        view = self.currentWebView()
        QApplication.clipboard().setText(view.page().selectedText())
    
    def currentContentsIndex( self ):
        """
        Returns the last index used for the contents widgets.
        
        :return     <int>
        """
        return self._currentContentsIndex
    
    def currentContentsWidget( self, autoadd = False ):
        """
        Returns the current contents widget based on the cached index.  If \
        no widget is specified and autoadd is True, then a new widget will \
        be added to the tab.
        
        :param      autoadd | <bool>
        
        :return     <QWebView>
        """
        widget = self.uiContentsTAB.widget(self.currentContentsIndex())
        if ( not isinstance(widget, QWebView) ):
            widget = None
        
        if ( not widget and autoadd ):
            widget = self.addContentsWidget()
        
        return widget
    
    def currentWebView(self):
        return self.uiContentsTAB.currentWidget()
    
    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
    
    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
    
    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            for url in event.mimeData().urls():
                self.loadFilename(str(url.toLocalFile()))
    
    def findNext( self ):
        """
        Looks for the previous occurance of the current search text.
        """
        text = self.uiFindTXT.text()
        view = self.currentWebView()
        
        options =  QWebPage.FindWrapsAroundDocument
        
        if ( self.uiCaseSensitiveCHK.isChecked() ):
            options |= QWebPage.FindCaseSensitively
        
        view.page().findText(text, options)
    
    def findPrev( self ):
        """
        Looks for the previous occurance of the current search text.
        """
        text = self.uiFindTXT.text()
        view = self.currentWebView()
        
        options =  QWebPage.FindWrapsAroundDocument
        options |= QWebPage.FindBackward
        
        if ( self.uiCaseSensitiveCHK.isChecked() ):
            options |= QWebPage.FindCaseSensitively
        
        view.page().findText(text, options)
    
    def findXdk( self, name ):
        """
        Looks up the xdk item based on the current name.
        
        :param      name | <str>
        
        :return     <XdkItem> || None
        """
        for i in range(self.uiContentsTREE.topLevelItemCount()):
            item = self.uiContentsTREE.topLevelItem(i)
            if ( item.text(0) == name ):
                return item
        return None
    
    def goBack( self ):
        widget = self.currentContentsWidget()
        if ( widget ):
            widget.page().history().back()
    
    def goForward( self ):
        widget = self.currentContentsWidget()
        if ( widget ):
            widget.page().history().forward()
    
    def goHome( self ):
        widget = self.currentContentsWidget()
        if ( widget ):
            widget.history().goHome()
    
    def gotoUrl(self, url):
        if not QApplication.keyboardModifiers() == Qt.ControlModifier:
            widget = self.currentContentsWidget(autoadd = True)
        else:
            widget = self.addContentsWidget()
        
        index = self.uiContentsTAB.indexOf(widget)
        self.uiContentsTAB.setCurrentIndex(index)
        widget.setUrl(QUrl(url))
    
    def gotoItem(self, path):
        """
        Goes to a particular path within the XDK.
        
        :param      path | <str>
        """
        if not path:
            return
        
        sections = str(path).split('/')
        check = projex.text.underscore(sections[0])
        
        for i in range(self.uiContentsTREE.topLevelItemCount()):
            item = self.uiContentsTREE.topLevelItem(i)
            if projex.text.underscore(item.text(1)) == check:
                item.gotoItem('/'.join(sections[1:]))
                break
    
    def loadItem( self, item ):
        """
        Loads the inputed item.
        
        :param      item | <QTreeWidgetItem>
        """
        if isinstance(item, XdkEntryItem):
            item.load()
    
    def loadedFilenames( self ):
        """
        Returns a list of all the xdk files that are currently loaded.
        
        :return     [<str>, ..]
        """
        output = []
        for i in range(self.uiContentsTREE.topLevelItemCount()):
            item = self.uiContentsTREE.topLevelItem(i)
            output.append(str(item.filepath()))
        return output
    
    def loadFilename( self, filename = '' ):
        """
        Loads a new XDK file into the system.
        
        :param      filename | <str>
        
        :return     <bool> | success
        """
        if ( not (filename and isinstance(filename, basestring)) ):
            filename = QFileDialog.getOpenFileName( self,
                                                    'Open XDK File',
                                                    QDir.currentPath(),
                                                    'XDK Files (*.xdk)' )
            
            if type(filename) == tuple:
                filename = str(filename[0])
            
            if not filename:
                return False
        
        if not (filename and os.path.exists(filename)):
            return False
        
        elif filename in self.loadedFilenames():
            return False
        
        self.loadFileRequested.emit(filename)
        self.setCursor(Qt.WaitCursor)
        
        return True
    
    def refreshFromIndex( self ):
        """
        Refreshes the documentation from the selected index item.
        """
        item = self.uiIndexTREE.currentItem()
        if ( not item ):
            return
        
        self.gotoUrl(item.toolTip(0))
    
    def refreshContents( self ):
        """
        Refreshes the contents tab with the latest selection from the browser.
        """
        item = self.uiContentsTREE.currentItem()
        if not isinstance(item, XdkEntryItem):
           return
        
        item.load()
        url = item.url()
        if url:
            self.gotoUrl(url)
    
    def refreshUi( self ):
        """
        Refreshes the interface based on the current settings.
        """
        widget      = self.uiContentsTAB.currentWidget()
        is_content  = isinstance(widget, QWebView)
        
        if is_content:
            self._currentContentsIndex = self.uiContentsTAB.currentIndex()
            history = widget.page().history()
        else:
            history = None
        
        self.uiBackACT.setEnabled(is_content and history.canGoBack())
        self.uiForwardACT.setEnabled(is_content and history.canGoForward())
        self.uiHomeACT.setEnabled(is_content)
        self.uiNewTabACT.setEnabled(is_content)
        self.uiCopyTextACT.setEnabled(is_content)
        self.uiCloseTabACT.setEnabled(is_content and
                                      self.uiContentsTAB.count() > 2)
        
        for i in range(1, self.uiContentsTAB.count()):
            widget = self.uiContentsTAB.widget(i)
            self.uiContentsTAB.setTabText(i, widget.title())
    
    def search( self ):
        """
        Looks up the current search terms from the xdk files that are loaded.
        """
        QApplication.instance().setOverrideCursor(Qt.WaitCursor)
        
        terms = str(self.uiSearchTXT.text())
        
        html = []
        
        entry_html = '<a href="%(url)s">%(title)s</a><br/>'\
                     '<small>%(url)s</small>'
        
        for i in range(self.uiContentsTREE.topLevelItemCount()):
            item = self.uiContentsTREE.topLevelItem(i)
            
            results = item.search(terms)
            results.sort(lambda x, y: cmp(y['strength'], x['strength']))
            for item in results:
                html.append( entry_html % item )
        
        if ( not html ):
            html.append('<b>No results were found for %s</b>' % terms)
        
        self.uiSearchWEB.setHtml(SEARCH_HTML % '<br/><br/>'.join(html))
        
        QApplication.instance().restoreOverrideCursor()
    
    def showFind( self ):
        self.uiFindWIDGET.show()
        self.uiFindTXT.setFocus()
        self.uiFindTXT.selectAll()
    
    def zoomIn( self ):
        view = self.currentWebView()
        view.setZoomFactor(view.zoomFactor() + 0.1)
    
    def zoomOut( self ):
        view = self.currentWebView()
        view.setZoomFactor(view.zoomFactor() - 0.1)
    
    def zoomReset( self ):
        view = self.currentWebView()
        view.setZoomFactor(1)
    
    @staticmethod
    def browse( parent, filename = '' ):
        """
        Creates a new XdkWidnow for browsing an XDK file.
        
        :param      parent      | <QWidget>
                    filename    | <str>
        """
        dlg = XdkWindow(parent)
        dlg.show()
        
        if ( filename ):
            dlg.loadFilename(filename)
class SensitiveLegendRaster(QObject):
    def __init__(self):
        super(SensitiveLegendRaster, self).__init__()  # QThread
        self.layer = self.worker = self.thread = None
        self.canvas = iface.mapCanvas()
        self.msgBar = iface.messageBar()
        self.legend = iface.legendInterface()
        self.nameModulus = "Script_Sensitive_Legend"
        #
        self.initThread()
        self._connect()
        #
        self.selectLayer(iface.activeLayer())

    def __del__(self):
        self.finishThread()
        self._connect(False)

    def printMsgBar(self, msg, typeMsg=QgsMessageBar.INFO):
        self.msgBar.popWidget()
        if typeMsg == QgsMessageBar.INFO:
            self.msgBar.pushMessage("SensitiveLegendRaster Script", msg, typeMsg)
        else:
            self.msgBar.pushMessage("SensitiveLegendRaster Script", msg, typeMsg, 5)

    def initThread(self):
        self.thread = QThread(self)
        self.thread.setObjectName(self.nameModulus)
        self.worker = WorkerSensitiveLegendRaster()
        self.worker.moveToThread(self.thread)
        self._connectWorker()

    def finishThread(self):
        self._connectWorker(False)
        self.worker.deleteLater()
        self.thread.wait()
        self.thread.deleteLater()
        self.thread = self.worker = None

    def _connectWorker(self, isConnect=True):
        ss = [
            {"signal": self.thread.started, "slot": self.worker.run},
            {"signal": self.worker.finished, "slot": self.finishedWorker},
            {"signal": self.worker.messageResult, "slot": self.messageResultWorker},
            {"signal": self.worker.messageStatus, "slot": self.messageStatusWorker},
        ]
        if isConnect:
            for item in ss:
                item["signal"].connect(item["slot"])
        else:
            for item in ss:
                item["signal"].disconnect(item["slot"])

    def _connect(self, isConnect=True):
        ss = [
            {"signal": self.legend.currentLayerChanged, "slot": self.selectLayer},
            {"signal": QgsMapLayerRegistry.instance().layerWillBeRemoved, "slot": self.unselectLayer},
            {"signal": self.canvas.extentsChanged, "slot": self.changeSensitiveLegend},
        ]
        if isConnect:
            for item in ss:
                item["signal"].connect(item["slot"])
        else:
            for item in ss:
                item["signal"].disconnect(item["slot"])

    @pyqtSlot()
    def finishedWorker(self):
        self.thread.quit()
        if self.worker.isKilled:  # When PAN/ZOOM/...
            self.thread.wait()
            self.changeSensitiveLegend()

    @pyqtSlot(str)
    def messageResultWorker(self, msg):
        self.printMsgBar(msg)

    @pyqtSlot(str)
    def messageStatusWorker(self, msg):
        self.printMsgBar(msg)

    @pyqtSlot("QgsMapLayer")
    def selectLayer(self, layer):

        if self.thread.isRunning():
            return

        isOk = True
        msg = ""
        typeMsg = QgsMessageBar.WARNING
        if not layer is None and layer.type() == QgsMapLayer.RasterLayer:
            legendColorAll = layer.legendSymbologyItems()
            if len(legendColorAll) > 0:  # Had a classification
                self.layer = layer
                self.worker.setLegendReadBlock(layer)
                msg = "Raster Layer '%s' actived" % layer.name()
                typeMsg = QgsMessageBar.INFO
            else:
                msg = "Raster Layer '%s' need be a classification" % layer.name()
                isOk = False
        else:
            if layer is None:
                msg = "Active a Raster layer"
            else:
                msg = "Layer '%s' need be a Raster" % layer.name()
            isOk = False

        self.printMsgBar(msg, typeMsg)

        return isOk

    @pyqtSlot(str)
    def unselectLayer(self, idLayer):
        if idLayer == self.layer.id():
            if self.thread.isRunning():
                self.worker.isKilled = True
            msg = "Raster Layer '%s' was removed" % self.layer.name()
            self.printMsgBar(msg, QgsMessageBar.WARNING)
            self.layer = None

    @pyqtSlot()
    def changeSensitiveLegend(self):

        if self.layer is None:
            return

        if self.thread.isRunning():
            self.worker.isKilled = True
            return

        mapSettings = self.canvas.mapSettings()
        crsCanvas = mapSettings.destinationCrs()
        extentCanvas = self.canvas.extent()

        extentLayer = self.layer.extent()
        resX = self.layer.rasterUnitsPerPixelX()
        resY = self.layer.rasterUnitsPerPixelY()

        if self.layer.crs() != crsCanvas:
            extentCanvas = mapSettings.mapToLayerCoordinates(self.layer, extentCanvas)

        if not extentCanvas.intersects(extentLayer):
            self.printMsgBar("View not intersects Raster '%s'" % self.layer.name, QgsMessageBar.WARNING)
            return

        keysLegend = range(len(self.worker.legendAll))  # [ idClass1, idClass2, ... ] [ 0..N-1]

        if extentCanvas == extentLayer or extentCanvas.contains(extentLayer):
            legendsView = map(lambda x: "(%s)%s" % (x, self.worker.legendAll[x]), keysLegend)
            msg = "[%d] = %s" % (len(legendsView), " ".join(legendsView))
            self.printMsgBar(msg)
            return

        extent = extentCanvas.intersect(extentLayer)
        widthRead = int(extent.width() / resX) + 1
        heightRead = int(extent.height() / resY) + 1

        self.worker.setProcessImage(extent, widthRead, heightRead)
        self.thread.start()
示例#5
0
class remote_pictures(iface_gui_plugin):
    VERSION_DB = 0
    VERSION_CURRENT = VERSION_DB
    
    def __init__(self):
        super(remote_pictures, self).__init__()
        self.options = [((u"min_opacity", u"Minimum opacity of controls:", self._minOpacityChanged), 20),
                        ((u"max_opacity", u"Maximum opacity of controls:", self._maxOpacityChanged), 80),
                        ((u"thumbnail_size", u"Thumbnail Size:", self._thumbnailSizeChanged), 150),
                        ((u"smooth_scaling", u"Smooth scaling", self._smoothScalingChanged), False),
                        ((u"store_locally", u"Store pictures locally", self._storeLocallyChanged), True)]
        self._gui = None
        self._handler = None
        self._rpAction = None
        
    def _handleOpacity(self, newValue, signal):
        if newValue < 0:
            newValue = 0
        elif newValue > 100:
            newValue = 100
            
        if signal is not None:
            signal.emit(float(newValue) / 100.)
        return newValue
        
    def _minOpacityChanged(self, _setting, newValue):
        return self._handleOpacity(newValue, None if self._gui is None else self._gui.minOpacityChanged)
    
    def _maxOpacityChanged(self, _setting, newValue):
        return self._handleOpacity(newValue, None if self._gui is None else self._gui.maxOpacityChanged)
    
    def _thumbnailSizeChanged(self, _setting, newValue):
        from remote_pictures.remote_pictures_category_model import CategoriesModel
        if newValue < CategoriesModel.MIN_THUMBNAIL_SIZE:
            newValue = CategoriesModel.MIN_THUMBNAIL_SIZE
        elif newValue > CategoriesModel.MAX_THUMBNAIL_SIZE:
            newValue = CategoriesModel.MAX_THUMBNAIL_SIZE
        
        if self._gui is not None:
            self._gui.thumbnailSizeChanged(newValue)
        if self._handler is not None:
            self._handler.thumbnailSizeChanged(newValue)
        return newValue
    
    def _storeLocallyChanged(self, _setting, newValue):
        self._handler.storeLocallyChanged(newValue)
        return newValue
        
    def _smoothScalingChanged(self, _setting, newValue):
        if self._gui is not None:
            self._gui.setSmoothScaling(newValue)
        
    def create_widget(self, parent):
        from PyQt4.QtCore import QThread
        from remote_pictures.remote_pictures_gui import RemotePicturesGui
        from remote_pictures.remote_pictures_handler import RemotePicturesHandler
        super(remote_pictures, self).create_widget(parent)
        self._gui = RemotePicturesGui(parent,
                                      self.logger,
                                      self.get_option(u"smooth_scaling"),
                                      self.get_option(u"min_opacity"),
                                      self.get_option(u"max_opacity"))
        
        if canUseBackgroundQThreads():
            self._messagesThread = QThread()
        else:
            self._messagesThread = None
        self._handler = RemotePicturesHandler(self.logger,
                                              self.get_option(u"thumbnail_size"),
                                              self.get_option(u"store_locally"),
                                              self._gui)
        if self._messagesThread is not None:
            self._handler.moveToThread(self._messagesThread)
            self._messagesThread.start()
        
        self._gui.openCategory.connect(self._handler.openCategory)
        self._gui.displayPrev.connect(self._handler.displayPrev)
        self._gui.displayNext.connect(self._handler.displayNext)
        self._gui.pictureDownloaded.connect(self._handler.pictureDownloaded)
        self._gui.setCategoryThumbnail.connect(self._handler.setCategoryThumbnail)
        
        self._handler.addCategory.connect(self._gui.categoryModel.addCategory)
        self._handler.categoryThumbnailChanged.connect(self._gui.categoryModel.categoryThumbnailChanged) 
        self._handler.displayImageInGui.connect(self._gui.displayImage)
        
        self._gui.categoryModel.categoriesChanged.connect(self._privacySettingsChanged)
        self._handler.categoriesChanged.connect(self._privacySettingsChanged)
        
        self._handler.loadPictures()
        
        return self._gui
    
    def destroy_widget(self):
        if self._gui is not None and self._handler is not None:
            self._gui.openCategory.disconnect(self._handler.openCategory)
            self._gui.displayPrev.disconnect(self._handler.displayPrev)
            self._gui.displayNext.disconnect(self._handler.displayNext)
            self._gui.pictureDownloaded.disconnect(self._handler.pictureDownloaded)
            self._gui.setCategoryThumbnail.disconnect(self._handler.setCategoryThumbnail)
            
            self._handler.addCategory.disconnect(self._gui.categoryModel.addCategory)
            self._handler.categoryThumbnailChanged.disconnect(self._gui.categoryModel.categoryThumbnailChanged)
            self._handler.displayImageInGui.disconnect(self._gui.displayImage)
            
        if self._gui is not None:
            self._gui.categoryModel.categoriesChanged.disconnect(self._privacySettingsChanged)
            self._gui.destroyWidget()
        
        if self._handler is not None:
            self._handler.categoriesChanged.disconnect(self._privacySettingsChanged)
            self._handler.finish()
            
        if self._messagesThread is not None:
            self._messagesThread.quit()
            self._messagesThread.wait()
            self._messagesThread.deleteLater()
        
        iface_gui_plugin.destroy_widget(self)

    def extendsInfoDict(self):
        return lunchinator_has_gui()
        
    def extendInfoDict(self, infoDict):
        infoDict[u"RP_v"] = self.VERSION_CURRENT
        
    def get_peer_actions(self):
        if lunchinator_has_gui():
            self._rpAction = _RemotePictureAction()
            return [self._rpAction]
        else:
            return None
            
    def checkCategory(self, cat):
        if self._handler is not None:
            self._handler.checkCategory(cat)
            
    def process_event(self, cmd, value, ip, _info, _prep):
        if cmd=="HELO_REMOTE_PIC":
            if self._handler is not None:
                self._handler.processRemotePicture(value, ip)
                
    def getCategories(self):
        if self._handler is None:
            self.logger.error("Remote Pictures not initialized")
            return []
    
        return self._handler.getCategoryNames(alsoEmpty=True)
    
    def getCategoryIcon(self, category):
        if self._gui is None:
            self.logger.error("Remote Pictures not initialized")
            return None
        return self._gui.getCategoryIcon(category)
    
    def willIgnorePeerAction(self, category, url):
        return self._handler.willIgnorePeerAction(category, url)
    
    def sendRemotePicture(self, peerID, peerInfo, parent):
        from remote_pictures.remote_pictures_dialog import RemotePicturesDialog
        dialog = RemotePicturesDialog(parent, peerID, peerInfo)
        result = dialog.exec_()
        if result == RemotePicturesDialog.Accepted:
            data = [dialog.getURL().encode('utf-8')]
            if dialog.getDescription():
                data.append(dialog.getDescription().encode('utf-8'))
                if dialog.getCategory():
                    data.append(dialog.getCategory().encode('utf-8'))
            with contextlib.closing(StringIO()) as strOut:
                writer = csv.writer(strOut, delimiter = ' ', quotechar = '"')
                writer.writerow(data)
                get_server().call("HELO_REMOTE_PIC " + strOut.getvalue(), peerIDs=[peerID])
    
    @loggingFunc
    def _privacySettingsChanged(self):
        get_notification_center().emitPrivacySettingsChanged(self._rpAction.getPluginName(), self._rpAction.getName())
示例#6
0
class private_messages(iface_gui_plugin):
    VERSION_INITIAL = 0
    VERSION_CURRENT = VERSION_INITIAL
    
    def __init__(self):
        super(private_messages, self).__init__()
        self.options = [((u"prev_messages", u"Number of previous messages to display"), 5),
                        ((u"enable_markdown", u"Enable Markdown annotations", self._enableMarkdownChanged), False)]
        self.hidden_options = {u"ack_timeout" : 3, # seconds until message delivery is marked as timed out
                               u"next_msgid" : -1} # next free message ID. -1 = not initialized
        
        self._storage = None
        
    def get_displayed_name(self):
        return u"Chat"
    
    def activate(self):
        iface_gui_plugin.activate(self)
        if lunchinator_has_gui():
            self._sendMessageAction = _SendMessageAction()
            self._openChatAction = _OpenChatAction(self._sendMessageAction)
            self._peerActions = [self._openChatAction, _BlockAction(self._sendMessageAction), self._sendMessageAction]
        else:
            self._peerActions = None
        
    def create_widget(self, parent):
        from private_messages.chat_history_view import ChatHistoryWidget
        
        self._lock = loggingMutex("Private Messages", logging=get_settings().get_verbose())
        
        from PyQt4.QtCore import QThread
        from private_messages.chat_messages_handler import ChatMessagesHandler
        if canUseBackgroundQThreads():
            self._messagesThread = QThread()
        else:
            self._messagesThread = None
            
        self._messagesHandler = ChatMessagesHandler(self.logger, self, self.hidden_options[u"ack_timeout"], self.hidden_options[u"next_msgid"])
        if self._messagesThread is not None:
            self._messagesHandler.moveToThread(self._messagesThread)
            self._messagesThread.start()
        
        self._messagesHandler.delayedDelivery.connect(self._delayedDelivery)
        self._messagesHandler.messageIDChanged.connect(self._messageIDChanged)
        self._messagesHandler.displayOwnMessage.connect(self._displayOwnMessage)
        self._messagesHandler.newMessage.connect(self._displayMessage)
        
        self._openChats = {} # mapping peer ID -> ChatDockWidget
        self._history = ChatHistoryWidget(self, parent, self.logger)
        return self._history
    
    def destroy_widget(self):
        for chatWindow in self._openChats.values():
            chatWindow.close()
            
        self.set_hidden_option(u"next_msgid", self._messagesHandler.getNextMessageIDForStorage(), convert=False)
        self._messagesHandler.deactivate()
        if self._messagesThread is not None:
            self._messagesThread.quit()
            self._messagesThread.wait()
            self._messagesThread.deleteLater()
            self._messagesThread = None
        self._messagesHandler = None
        self._storage = None
        self._lock = None
            
        iface_gui_plugin.destroy_widget(self)
        
    def _enableMarkdownChanged(self, _setting, newVal):
        for chatWindow in self._openChats.values():
            chatWindow.getChatWidget().setMarkdownEnabled(newVal)
    
    def extendsInfoDict(self):
        return lunchinator_has_gui()
        
    def extendInfoDict(self, infoDict):
        infoDict[u"PM_v"] = self.VERSION_CURRENT
        
    def get_peer_actions(self):
        return self._peerActions
        
    def process_event(self, cmd, value, _ip, peerInfo, _prep):
        if not cmd.startswith(u"HELO_PM"):
            return
        
        peerID = peerInfo[u"ID"]
        
        subcmd = cmd[7:]
        if subcmd == u"_ACK":
            self._messagesHandler.processAck(peerID, value)
        elif subcmd == u"_TYPING":
            if peerID in self._openChats:
                self._openChats[peerID].getChatWidget().otherIsTyping()
        elif subcmd == u"_CLEARED":
            if peerID in self._openChats:
                self._openChats[peerID].getChatWidget().otherCleared()
        elif subcmd == u"_ERROR":
            self._messagesHandler.processAck(peerID, value, error=True)
        elif subcmd == u"":
            self._messagesHandler.processMessage(peerID, value)
    
    def getStorage(self):
        if self._storage == None:
            with self._lock:
                if self._storage == None:
                    from private_messages.chat_messages_storage import ChatMessagesStorage
                    self._storage = ChatMessagesStorage(self.logger)
        return self._storage
    
    @loggingFunc
    def _displayOwnMessage(self, otherID, msgID, recvTime, msgHTML, msgTime, status, errorMsg):
        otherID = convert_string(otherID)
        msgHTML = convert_string(msgHTML)
        errorMsg = convert_string(errorMsg)
        if recvTime == -1:
            recvTime = None
        if not errorMsg:
            errorMsg = None
        
        if otherID in self._openChats:
            chatWindow = self._openChats[otherID]
            chatWindow.getChatWidget().addOwnMessage(msgID, recvTime, msgHTML, msgTime, status, errorMsg)
    
    def _activateChat(self, chatWindow, forceForeground=True):
        chatWindow.showNormal()
        if forceForeground:
            chatWindow.raise_()
            chatWindow.activateWindow()
        return chatWindow
    
    def _openChat(self, myName, otherName, myAvatar, otherAvatar, otherID):
        from private_messages.chat_window import ChatWindow
        newWindow = ChatWindow(None, self.logger, myName, otherName, myAvatar, otherAvatar, otherID, self._sendMessageAction)
        newWindow.getChatWidget().setMarkdownEnabled(self.get_option(u"enable_markdown"))
        
        newWindow.windowClosing.connect(self._chatClosed)
        newWindow.getChatWidget().sendMessage.connect(self._messagesHandler.sendMessage)
        newWindow.getChatWidget().typing.connect(partial(self._messagesHandler.sendTyping, otherID))
        newWindow.getChatWidget().cleared.connect(partial(self._messagesHandler.sendCleared, otherID))
        self._openChats[otherID] = newWindow
        
        prevMessages = self.getStorage().getPreviousMessages(otherID, self.get_option(u"prev_messages"))
        from private_messages.chat_messages_storage import ChatMessagesStorage
        for row in reversed(prevMessages):
            # partner, ID, own, time, status, text
            isOwnMessage = row[ChatMessagesStorage.MSG_IS_OWN_MESSAGE_COL] != 0
            if isOwnMessage:
                newWindow.getChatWidget().addOwnMessage(row[ChatMessagesStorage.MSG_ID_COL],
                                                        row[ChatMessagesStorage.MSG_RECV_TIME_COL],
                                                        row[ChatMessagesStorage.MSG_TEXT_COL],
                                                        row[ChatMessagesStorage.MSG_TIME_COL],
                                                        row[ChatMessagesStorage.MSG_STATUS_COL])
            else:
                newWindow.getChatWidget().addOtherMessage(row[ChatMessagesStorage.MSG_TEXT_COL],
                                                          row[ChatMessagesStorage.MSG_TIME_COL],
                                                          row[ChatMessagesStorage.MSG_RECV_TIME_COL])
        return self._activateChat(newWindow)
        
    @loggingFunc
    def _chatClosed(self, pID):
        pID = convert_string(pID)
        if pID in self._openChats:
            chatWindow = self._openChats[pID]
            chatWindow.deleteLater()
            del self._openChats[pID]
        else:
            self.logger.error("Closed chat window was not maintained: %s", pID)
        
    def getOpenChatAction(self):
        return self._openChatAction
    
    def openChat(self, pID, forceForeground=True):
        pID = convert_string(pID)
        
        if pID in self._openChats:
            return self._activateChat(self._openChats[pID], forceForeground)
        
        otherName = get_peers().getDisplayedPeerName(pID=pID)
        if otherName == None:
            self.logger.error("Could not get info of chat partner %s", pID)
            return
        otherAvatar = get_peers().getPeerAvatarFile(pID=pID)
        
        myName = get_settings().get_user_name()
        myAvatar = get_peers().getPeerAvatarFile(pID=get_settings().get_ID())
        
        return self._openChat(myName, otherName, myAvatar, otherAvatar, pID)

    @loggingFunc
    def _delayedDelivery(self, otherID, msgID, recvTime, error, errorMessage):
        otherID = convert_string(otherID)
        errorMessage = convert_string(errorMessage)
        
        if otherID in self._openChats:
            chatWindow = self._openChats[otherID]
            chatWindow.getChatWidget().delayedDelivery(msgID, recvTime, error, errorMessage)

    @loggingFunc
    def _messageIDChanged(self, otherID, oldID, newID):
        otherID = convert_string(otherID)
        if otherID in self._openChats:
            chatWindow = self._openChats[otherID]
            chatWindow.getChatWidget().messageIDChanged(oldID, newID)

    @loggingFunc
    def _displayMessage(self, otherID, msgHTML, msgTime, msgDict):
        try:
            recvTime = time()
            chatWindow = self.openChat(otherID, False)
            chatWindow.getChatWidget().addOtherMessage(msgHTML, msgTime, recvTime)
            self._messagesHandler.receivedSuccessfully(otherID, msgHTML, msgTime, msgDict, recvTime)
        except:
            excType, excValue, _tb = sys.exc_info()
            errorMsg = u"Error processing message (%s: %s)" % (unicode(excType.__name__), unicode(excValue))
            self._messagesHandler.errorReceivingMessage(otherID, msgDict, errorMsg)
        
        if not chatWindow.isActiveWindow():
            from PyQt4.QtGui import QTextDocument
            doc = QTextDocument()
            doc.setHtml(msgHTML)
            displayNotification(chatWindow.getChatWidget().getOtherName(),
                                convert_string(doc.toPlainText()),
                                self.logger,
                                chatWindow.getChatWidget().getOtherIconPath())
示例#7
0
class RasterLegendSensitive(QObject):
    def __init__(self, iface, treeView, ckEnabled):
        super(RasterLegendSensitive, self).__init__()
        self.tree = TreeLegend(treeView)
        self.ckEnabled = ckEnabled
        #
        self.layer = self.worker = self.thread = self.transparencyLayer = None
        self.isExtentLayer = self.valuesFullExtent = None
        self.hasConnect = self.hasConnectTree = None
        self.iface = iface
        self.legend = iface.legendInterface()
        self.canvas = iface.mapCanvas()
        self.msgBar = iface.messageBar()
        self.nameModulus = "RasterLegendSensitive"
        #
        self.initThread()
        self._connect()
        self._connectTree()

    def __del__(self):
        del self.tree
        self.finishThread()
        if not self.hasConnect:
            self._connect(False)
        if not self.layer is None:
            self.setTransparenceLayer([])

    def initThread(self):
        self.thread = QThread(self)
        self.thread.setObjectName(self.nameModulus)
        self.worker = WorkerRasterLegendSensitive()
        self.worker.moveToThread(self.thread)
        self._connectWorker()

    def finishThread(self):
        self._connectWorker(False)
        self.worker.deleteLater()
        self.thread.wait()
        self.thread.deleteLater()
        self.thread = self.worker = None

    def _connectWorker(self, isConnect=True):
        ss = [{
            'signal': self.thread.started,
            'slot': self.worker.run
        }, {
            'signal': self.worker.finished,
            'slot': self.finishedWorker
        }, {
            'signal': self.worker.messageStatus,
            'slot': self.messageStatusWorker
        }]
        if isConnect:
            for item in ss:
                item['signal'].connect(item['slot'])
        else:
            for item in ss:
                item['signal'].disconnect(item['slot'])

    def _connect(self, isConnect=True):
        ss = [{
            'signal': self.legend.currentLayerChanged,
            'slot': self.selectLayer
        }, {
            'signal': QgsMapLayerRegistry.instance().layerWillBeRemoved,
            'slot': self.removeLayer
        }, {
            'signal': self.canvas.extentsChanged,
            'slot': self.changeSensitiveLegend
        }]
        if isConnect:
            self.hasConnect = True
            for item in ss:
                item['signal'].connect(item['slot'])
        else:
            self.hasConnect = False
            for item in ss:
                item['signal'].disconnect(item['slot'])

    def _connectTree(self, isConnect=True):
        ss = [{
            'signal': self.tree.toggledLegend,
            'slot': self.setTransparenceLayer
        }, {
            'signal': self.tree.descriptionLegend,
            'slot': self.sendClipboard
        }]
        if isConnect:
            self.hasConnectTree = True
            for item in ss:
                item['signal'].connect(item['slot'])
        else:
            self.hasConnectTree = False
            for item in ss:
                item['signal'].disconnect(item['slot'])

    def _resetLayer(self):
        if self.thread.isRunning():
            self.worker.isKilled = True
        self.layer = None
        self.tree.setHeader()
        self.tree.layer = None

    def setEnabled(self, isEnabled=True):
        if not isEnabled and self.thread.isRunning():
            self.worker.isKilled = True
        #
        self._connect(isEnabled)
        self._connectTree(isEnabled)
        self.tree.setEnabled(isEnabled)
        #
        if isEnabled:
            activeLayer = self.iface.activeLayer()
            if activeLayer == self.layer:
                if activeLayer is None:
                    return
                self.changeSensitiveLegend()
            else:
                self.selectLayer(activeLayer)

    @pyqtSlot(list)
    def finishedWorker(self, values):
        self.thread.quit()
        self.msgBar.popWidget()
        if not self.worker.isKilled:
            if len(values) > 0:  # Never Happing otherwise...
                self.tree.setLegend(values)
                if self.isExtentLayer:
                    self.valuesFullExtent = values
        else:  # When PAN/ZOOM/...
            self.thread.wait()
            if self.ckEnabled.checkState() == Qt.Checked:
                self.changeSensitiveLegend()

    @pyqtSlot(str)
    def messageStatusWorker(self, msg):
        self.msgBar.popWidget()
        self.msgBar.pushMessage(self.nameModulus, msg, QgsMessageBar.INFO)

    @pyqtSlot(list)
    def setTransparenceLayer(self, visibleItems):
        def refreshLayer():
            if hasattr(self.layer, "setCacheImage"):
                self.layer.setCacheImage(None)  # Refresh
            else:
                self.layer.triggerRepaint()

        def setTransparence(value, visible):
            t = QgsRasterTransparency.TransparentSingleValuePixel()
            t.min = t.max = value
            percent = 100.0 if not visible else 0.0
            t.percentTransparent = percent

            return t

        valuesTransparent = []
        item = 0
        for visible in visibleItems:
            valuesTransparent.append(setTransparence(item, visible))
            item += 1
        self.transparencyLayer.setTransparentSingleValuePixelList(
            valuesTransparent)
        refreshLayer()
        del valuesTransparent[:]

    @pyqtSlot(str)
    def sendClipboard(self, description):

        mapSettings = self.canvas.mapSettings()
        crsCanvas = mapSettings.destinationCrs()
        extentCanvas = self.canvas.extent()
        extentLayer = self.layer.extent()

        if self.layer.crs() != crsCanvas:
            extentCanvas = mapSettings.mapToLayerCoordinates(
                self.layer, extentCanvas)

        if extentCanvas == extentLayer or extentCanvas.contains(extentLayer):
            msg = "Calculate for all extent of layer '%s'\n\n%s" % (
                self.layer.name(), description)
        else:
            msg = "Calculate for subset of layer '%s' (extend: %s)\n\n%s" % (
                self.layer.name(), extentCanvas.toString(), description)
        clip = QApplication.clipboard()
        clip.setText(msg)
        self.msgBar.pushMessage(self.nameModulus, "Copy to Clipboard",
                                QgsMessageBar.INFO, 5)

    @pyqtSlot('QgsMapLayer')
    def selectLayer(self, layer):
        def processLegend():
            self.tree.setLayer(layer)
            if not self.valuesFullExtent is None:  # Save legend with all extent layer
                del self.valuesFullExtent[:]
                self.valuesFullExtent = None
            (self.layer,
             self.transparencyLayer) = (layer,
                                        layer.renderer().rasterTransparency())
            self.worker.setLegendReadBlock(layer)
            self.changeSensitiveLegend()

        if not self.layer is None:
            if not self.layer in self.legend.layers():
                self.removeLayer(self.layer.id())
            else:
                self.setTransparenceLayer([])
                self._resetLayer()

        if not layer is None and layer.type() == QgsMapLayer.RasterLayer:
            legendItems = layer.legendSymbologyItems()
            total = len(legendItems)
            if total > 0:  # Had a classification
                processLegend()

    @pyqtSlot(str)
    def removeLayer(self, idLayer):
        if not self.layer is None and self.layer.id() == idLayer:
            msg = "Layer '%s' was removed" % self.tree.getLayerName()
            self.msgBar.pushMessage(self.nameModulus, msg,
                                    QgsMessageBar.WARNING, 5)
            self._resetLayer()

    @pyqtSlot()
    def changeSensitiveLegend(self):
        if self.layer is None:
            return

        if self.thread.isRunning():
            self.worker.isKilled = True
            return

        mapSettings = self.canvas.mapSettings()
        crsCanvas = mapSettings.destinationCrs()
        extentCanvas = self.canvas.extent()

        extentLayer = self.layer.extent()
        resX = self.layer.rasterUnitsPerPixelX()
        resY = self.layer.rasterUnitsPerPixelY()

        if self.layer.crs() != crsCanvas:
            extentCanvas = mapSettings.mapToLayerCoordinates(
                self.layer, extentCanvas)

        if not extentCanvas.intersects(extentLayer):
            self.msgBar.popWidget()
            self.msgBar.pushMessage(
                self.nameModulus,
                "View not intersects Raster '%s'" % self.layer.name(),
                QgsMessageBar.WARNING, 5)
            self.tree.setLegend([])
            return

        if extentCanvas == extentLayer or extentCanvas.contains(extentLayer):
            self.isExtentLayer = True
            if not self.valuesFullExtent is None:
                self.tree.setLegend(self.valuesFullExtent)
                return
            extent = extentLayer
            delta = 0
        else:
            self.isExtentLayer = False
            extent = extentCanvas.intersect(extentLayer)
            delta = 1

        widthRead = int(extent.width() / resX) + delta
        heightRead = int(extent.height() / resY) + delta

        self.worker.setProcessImage(extent, widthRead, heightRead)
        self.thread.start()
示例#8
0
class file_transfer(iface_gui_plugin):
    VERSION_INITIAL = 0
    VERSION_CURRENT = VERSION_INITIAL
    
    def __init__(self):
        super(file_transfer, self).__init__()
        self.options = [((u"download_dir", u"Save received files in directory", self._downloadDirChanged), os.path.join(os.path.expanduser("~"), "Downloads")),
                        ((u"overwrite", u"Overwrite existing files", self._overwriteChanged), False),
                        ((u"compression", u"Use compression when sending", self._compressionChanged, (u"No", u"GZip", u"BZip2")), u"No")]
    
    def get_displayed_name(self):
        return u"File Transfer"
        
    def activate(self):
        iface_gui_plugin.activate(self)
        self._sendFileAction = _TransferFileAction()
        
    def deactivate(self):
        iface_gui_plugin.deactivate(self)
    
    def create_widget(self, parent):
        from file_transfer.file_transfer_widget import FileTransferWidget
        from file_transfer.file_transfer_handler import FileTransferHandler
        
        if canUseBackgroundQThreads():
            from PyQt4.QtCore import QThread
            self._handlerThread = QThread()
        else:
            self._handlerThread = None
            
        self._handler = FileTransferHandler(self.logger,
                                            self.get_option(u"download_dir"),
                                            self.get_option(u"overwrite"),
                                            self.get_option(u"compression"))
        if self._handlerThread is not None:
            self._handlerThread.moveToThread(self._handlerThread)
            self._handlerThread.start()
        
        self._gui = FileTransferWidget(parent, self.logger, self)
        self._toolWindow = FileTransferWidget(parent, self.logger, self, asWindow=True)
        self._toolWindow.setWindowTitle("File Transfers")

        for gui in (self._gui, self._toolWindow):        
            gui.retry.connect(self._handler.retrySendFileToPeer)
            gui.cancel.connect(self._handler.cancelOutgoingTransfer)
            self._handler.startOutgoingTransfer.connect(gui.startOutgoingTransfer)
            self._handler.outgoingTransferStarted.connect(gui.outgoingTransferStarted)
            self._handler.outgoingTransferCanceled.connect(gui.outgoingTransferCanceled)
            self._handler.incomingTransferStarted.connect(gui.incomingTransferStarted)
        
        return self._gui
    
    def destroy_widget(self):
        for gui in (self._gui, self._toolWindow):        
            gui.retry.disconnect(self._handler.retrySendFileToPeer)
            gui.cancel.disconnect(self._handler.cancelOutgoingTransfer)
            self._handler.startOutgoingTransfer.disconnect(gui.startOutgoingTransfer)
            self._handler.outgoingTransferStarted.disconnect(gui.outgoingTransferStarted)
            self._handler.outgoingTransferCanceled.disconnect(gui.outgoingTransferCanceled)
            self._handler.incomingTransferStarted.disconnect(gui.incomingTransferStarted)
        
        self._handler.deactivate()
        if self._handlerThread is not None:
            self._handlerThread.quit()
            self._handlerThread.wait()
            self._handlerThread.deleteLater()
            self._handlerThread = None
        self._handler = None
        
        iface_gui_plugin.destroy_widget(self)
    
    def extendsInfoDict(self):
        # do not except file transfers without GUI
        return lunchinator_has_gui()
        
    def extendInfoDict(self, infoDict):
        infoDict[u"FT_v"] = self.VERSION_CURRENT
        
    def get_peer_actions(self):
        return [self._sendFileAction]
        
    def process_event(self, cmd, value, peerIP, peerInfo, preprocessedData=None):
        if not cmd.startswith(u"HELO_FT"):
            return
        
        peerID = peerInfo[u"ID"]
        
        subcmd = cmd[7:]
        if subcmd == u"":
            self._handler.processSendRequest(peerID, peerIP, value, preprocessedData)
        elif subcmd == u"_ACK":
            self._handler.processAck(peerID, peerIP, value)
        elif subcmd == u"_CANCEL":
            self._handler.processCancel(peerID, value)
            
    def getSendFileAction(self):
        return self._sendFileAction
            
    def chooseAndSendFilesToPeer(self, peerID, parent):
        from PyQt4.QtGui import QFileDialog
        selectedFiles = QFileDialog.getOpenFileNames(parent, u"Chooses files to upload")
        if len(selectedFiles) > 0:
            self._handler.sendFilesToPeer([convert_string(f) for f in selectedFiles], peerID)
        
    def sendFilesToPeer(self, toSend, peerID):
        self._handler.sendFilesToPeer([convert_string(f) for f in toSend], peerID)
        
    def _downloadDirChanged(self, _setting, newVal):
        self._handler.downloadDirChanged(newVal)
        return newVal
    
    def _overwriteChanged(self, _setting, newVal):
        self._handler.overwriteChanged(newVal)
        return newVal
    
    def _compressionChanged(self, _setting, newVal):
        self._handler.compressionChanged(newVal)
        return newVal
class CatalogOTF(QObject):

    # Signals
    settedLayer = pyqtSignal("QgsVectorLayer")
    removedLayer = pyqtSignal(str)
    killed = pyqtSignal(str)
    changedNameLayer = pyqtSignal(str, str)
    changedTotal = pyqtSignal(str, str)
    changedIconRun = pyqtSignal(str, bool)

    def __init__(self, iface, tableCOTF):
        def connecTableCOTF():
            self.settedLayer.connect(tableCOTF.insertRow)
            self.removedLayer.connect(tableCOTF.removeRow)
            self.changedNameLayer.connect(tableCOTF.changedNameLayer)
            self.changedTotal.connect(tableCOTF.changedTotal)
            self.changedIconRun.connect(tableCOTF.changedIconRun)
            self.killed.connect(tableCOTF.killed)

        super(CatalogOTF, self).__init__()
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.ltv = iface.layerTreeView()
        self.model = self.ltv.layerTreeModel()
        self.ltgRoot = QgsProject.instance().layerTreeRoot()
        self.msgBar = iface.messageBar()
        self.legendTMS = LegendTMS('Catalog OTF')
        self.legendRaster = LegendRaster('Catalog OTF')

        self._initThread()

        connecTableCOTF()
        self.model.dataChanged.connect(self.dataChanged)
        QgsMapLayerRegistry.instance().layersWillBeRemoved.connect(
            self.layersWillBeRemoved)  # Catalog layer removed

        self.layer = self.layerName = self.nameFieldSource = self.nameFieldDate = None
        self.ltgCatalog = self.ltgCatalogName = self.visibleSourceLayers = self.hasCanceled = None

    def __del__(self):
        self._finishThread()
        del self.legendTMS
        del self.legendRaster
        QgsMapLayerRegistry.instance().layersWillBeRemoved.disconnect(
            self.layersWillBeRemoved)  # Catalog layer removed

    def _initThread(self):
        self.thread = QThread(self)
        self.thread.setObjectName("QGIS_Plugin_%s" %
                                  NAME_PLUGIN.replace(' ', '_'))
        self.worker = WorkerPopulateGroup(self.addLegendLayerWorker)
        self.worker.moveToThread(self.thread)
        self._connectWorker()

    def _finishThread(self):
        self._connectWorker(False)
        self.worker.deleteLater()
        self.thread.wait()
        self.thread.deleteLater()
        self.thread = self.worker = None

    def _connectWorker(self, isConnect=True):
        ss = [{
            'signal': self.thread.started,
            'slot': self.worker.run
        }, {
            'signal': self.worker.finished,
            'slot': self.finishedPG
        }, {
            'signal': self.worker.messageStatus,
            'slot': self.messageStatusPG
        }, {
            'signal': self.worker.messageError,
            'slot': self.messageErrorPG
        }]
        if isConnect:
            for item in ss:
                item['signal'].connect(item['slot'])
        else:
            for item in ss:
                item['signal'].disconnect(item['slot'])

    def addLegendLayerWorker(self, layer):
        if layer.type() == QgsMapLayer.RasterLayer:
            metadata = layer.metadata()
            if metadata.find("GDAL provider") != -1:
                if metadata.find("OGC Web Map Service") != -1:
                    if self.legendTMS.hasTargetWindows(layer):
                        self.legendTMS.setLayer(layer)
                else:
                    self.legendRaster.setLayer(layer)

    def run(self):
        self.hasCanceled = False  # Check in finishedPG

        if self.thread.isRunning():
            self.worker.kill()
            self.hasCanceled = True
            msgtrans = QCoreApplication.translate(
                "CatalogOTF", "Canceled search for image from layer %s")
            msg = msgtrans % self.layerName
            self.msgBar.pushMessage(NAME_PLUGIN, msg, QgsMessageBar.WARNING, 2)
            self.changedTotal.emit(self.layer.id(), "Canceling processing")
            self.killed.emit(self.layer.id())
            return

        if self.layer is None:
            msgtrans = QCoreApplication.translate("CatalogOTF",
                                                  "Need define layer catalog")
            self.msgBar.pushMessage(NAME_PLUGIN, msgtrans,
                                    QgsMessageBar.WARNING, 2)
            return

        self._setGroupCatalog()
        self.ltgCatalogName = self.ltgCatalog.name()

        renderFlag = self.canvas.renderFlag()
        if renderFlag:
            self.canvas.setRenderFlag(False)
            self.canvas.stopRendering()

        self._populateGroupCatalog()

        if renderFlag:
            self.canvas.setRenderFlag(True)
            self.canvas.refresh()

    def _populateGroupCatalog(self):
        def getSourceVisibleLayers():
            def hasVisibleRaster(ltl):
                return ltl.isVisible() == Qt.Checked and ltl.layer().type(
                ) == QgsMapLayer.RasterLayer

            l_ltlVisible = filter(lambda item: hasVisibleRaster(item),
                                  self.ltgCatalog.findLayers())
            return map(lambda item: item.layer().source(), l_ltlVisible)

        def runWorker():
            data = {}
            data['nameFieldDate'] = self.nameFieldDate
            data['nameFieldSource'] = self.nameFieldSource
            data['layer'] = self.layer
            data['ltgCatalog'] = self.ltgCatalog
            self.worker.setData(data)
            self.thread.start()
            #self.worker.run() # DEBUG

        self.visibleSourceLayers = getSourceVisibleLayers()
        self.ltgCatalog.removeAllChildren()
        runWorker()  # See finishPG

    def _setGroupCatalog(self):
        self.ltgCatalogName = "%s - Catalog" % self.layer.name()
        self.ltgCatalog = self.ltgRoot.findGroup(self.ltgCatalogName)
        if self.ltgCatalog is None:
            self.ltgCatalog = self.ltgRoot.addGroup(self.ltgCatalogName)

    @pyqtSlot(bool)
    def finishedPG(self, isKilled):
        def setSourceVisibleLayers():
            l_ltlVisible = filter(
                lambda item: item.layer().source() in self.visibleSourceLayers,
                self.ltgCatalog.findLayers())
            map(lambda item: item.setVisible(Qt.Checked), l_ltlVisible)

        self.thread.quit()

        if not self.layer is None:
            self.changedIconRun.emit(self.layer.id(),
                                     self.layer.selectedFeatureCount() > 0)
            if self.hasCanceled:
                self.changedTotal.emit(self.layer.id(), '0')
            else:
                setSourceVisibleLayers()

        del self.visibleSourceLayers[:]

    @pyqtSlot(str)
    def messageStatusPG(self, msg):
        self.changedTotal.emit(self.layer.id(), msg)

    @pyqtSlot(str)
    def messageErrorPG(self, msg):
        self.msgBar.pushMessage(NAME_PLUGIN, msg, QgsMessageBar.CRITICAL, 8)

    @pyqtSlot('QModelIndex', 'QModelIndex')
    def dataChanged(self, idTL, idBR):
        if idTL != idBR:
            return

        if not self.ltgCatalog is None and self.ltgCatalog == self.model.index2node(
                idBR):
            name = self.ltgCatalog.name()
            if self.ltgCatalogName != name:
                self.ltgCatalogName = name
                return

        if not self.layer is None and self.ltgRoot.findLayer(
                self.layer.id()) == self.model.index2node(idBR):
            name = self.layer.name()
            if self.layerName != name:
                self.changedNameLayer.emit(self.layer.id(), name)
                self.layerName = name

    @pyqtSlot(list)
    def layersWillBeRemoved(self, layerIds):
        if self.layer is None:
            return
        if self.layer.id() in layerIds:
            self.removedLayer.emit(self.layer.id())
            self.removeLayerCatalog()

    @staticmethod
    def getNameFieldsCatalog(layer):
        def getFirstFeature():
            f = QgsFeature()
            #
            fr = QgsFeatureRequest(
            )  # First FID can be 0 or 1 depend of provider type
            it = layer.getFeatures(fr)
            isOk = it.nextFeature(f)
            it.close()
            #
            if not isOk or not f.isValid():
                del f
                return None
            else:
                return f

        def hasAddress(feature, nameField):
            def asValidUrl(url):
                isOk = True
                try:
                    urllib2.urlopen(url)
                except urllib2.HTTPError, e:
                    isOk = False
                except urllib2.URLError, e:
                    isOk = False
                #
                return isOk

            value = feature.attribute(nameField)
            if value is None or type(value) == QPyNullVariant:
                return False

            isUrl = value.find('http://') == 0 or value.find('https://') == 0
            lenSource = len(value)
            isUrl = isUrl and value.rfind(
                'xml', lenSource - len('xml')) == lenSource - len('xml')
            if isUrl:
                return asValidUrl(value)
            #
            fileInfo = QFileInfo(value)
            return fileInfo.isFile()
示例#10
0
class XdkWindow(QMainWindow):
    """ """
    loadFileRequested = Signal(str)

    def __init__(self, parent=None):
        super(XdkWindow, self).__init__(parent)

        # load the user interface
        projexui.loadUi(__file__, self)

        # define custom properties
        self._currentContentsIndex = -1
        self._worker = XdkWorker()
        self._workerThread = QThread()
        self._worker.moveToThread(self._workerThread)
        self._workerThread.start()

        # set default properties
        self.setAcceptDrops(True)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.uiFindNextBTN.setDefaultAction(self.uiFindNextACT)
        self.uiFindPrevBTN.setDefaultAction(self.uiFindPrevACT)
        self.uiFindWIDGET.setVisible(False)
        self.uiSearchWEB.page().setLinkDelegationPolicy(
            QWebPage.DelegateAllLinks)

        self.refreshUi()

        # connect widgets
        self.uiContentsTAB.currentChanged.connect(self.refreshUi)
        self.uiContentsTAB.tabCloseRequested.connect(self.closeContentsWidget)
        self.uiContentsTREE.itemExpanded.connect(self.loadItem)
        self.uiContentsTREE.itemSelectionChanged.connect(self.refreshContents)
        self.uiSearchTXT.returnPressed.connect(self.search)
        self.uiSearchWEB.linkClicked.connect(self.gotoUrl)
        self.uiIndexTREE.itemSelectionChanged.connect(self.refreshFromIndex)

        # connect find actions
        self.uiBackACT.triggered.connect(self.goBack)
        self.uiForwardACT.triggered.connect(self.goForward)
        self.uiHomeACT.triggered.connect(self.goHome)
        self.uiFindTXT.textChanged.connect(self.findNext)
        self.uiFindTXT.returnPressed.connect(self.findNext)
        self.uiFindNextACT.triggered.connect(self.findNext)
        self.uiFindPrevACT.triggered.connect(self.findPrev)
        self.uiFindACT.triggered.connect(self.showFind)
        self.uiFindCloseBTN.clicked.connect(self.uiFindWIDGET.hide)
        self.uiCopyTextACT.triggered.connect(self.copyText)

        # connect zoom actions
        self.uiZoomResetACT.triggered.connect(self.zoomReset)
        self.uiZoomInACT.triggered.connect(self.zoomIn)
        self.uiZoomOutACT.triggered.connect(self.zoomOut)

        # connect file actions
        self.uiLoadACT.triggered.connect(self.loadFilename)
        self.uiNewTabACT.triggered.connect(self.addContentsWidget)
        self.uiCloseTabACT.triggered.connect(self.closeContentsWidget)
        self.uiQuitACT.triggered.connect(self.close)

        # connect the signals
        self.loadFileRequested.connect(self._worker.loadFile)
        self._worker.loadingFinished.connect(self.__addXdkItem)
        QApplication.instance().aboutToQuit.connect(self.__cleanupWorker)

    def __del__(self):
        self.__cleanupWorker()

    def __addXdkItem(self, filename):
        item = XdkItem(filename)

        # add the xdk content item
        self.uiContentsTREE.addTopLevelItem(item)

        # add the index list items
        self.uiIndexTREE.blockSignals(True)
        self.uiIndexTREE.setUpdatesEnabled(False)
        for name, url in item.indexlist():
            item = XTreeWidgetItem([name])
            item.setToolTip(0, url)
            item.setFixedHeight(22)
            self.uiIndexTREE.addTopLevelItem(item)
        self.uiIndexTREE.blockSignals(False)
        self.uiIndexTREE.setUpdatesEnabled(True)
        self.uiIndexTREE.sortByColumn(0, Qt.AscendingOrder)

        self.unsetCursor()

    def __cleanupWorker(self):
        if self._workerThread is None:
            return

        self._workerThread.quit()
        self._workerThread.wait()

        self._worker.deleteLater()
        self._workerThread.deleteLater()

        self._worker = None
        self._workerThread = None

    def __gotoUrl(self, url):
        if url.toLocalFile():
            self.gotoUrl(url.toString())
        else:
            webbrowser.open(str(url.toString()))

    def addContentsWidget(self):
        """
        Adds a new contents widget tab into the contents tab.
        
        :return     <QWebView>
        """
        curr_widget = self.currentContentsWidget()
        widget = QWebView(self)
        page = widget.page()
        page.setLinkDelegationPolicy(page.DelegateAllLinks)

        self.uiContentsTAB.blockSignals(True)
        self.uiContentsTAB.addTab(widget, 'Documentation')
        self.uiContentsTAB.setCurrentIndex(self.uiContentsTAB.count() - 1)
        self.uiContentsTAB.blockSignals(False)

        self._currentContentsIndex = self.uiContentsTAB.count() - 1

        if curr_widget:
            widget.setUrl(curr_widget.url())

        widget.titleChanged.connect(self.refreshUi)
        widget.linkClicked.connect(self.__gotoUrl)

        return widget

    def closeContentsWidget(self):
        """
        Closes the current contents widget.
        """
        widget = self.currentContentsWidget()
        if (not widget):
            return

        widget.close()
        widget.setParent(None)
        widget.deleteLater()

    def copyText(self):
        """
        Copies the selected text to the clipboard.
        """
        view = self.currentWebView()
        QApplication.clipboard().setText(view.page().selectedText())

    def currentContentsIndex(self):
        """
        Returns the last index used for the contents widgets.
        
        :return     <int>
        """
        return self._currentContentsIndex

    def currentContentsWidget(self, autoadd=False):
        """
        Returns the current contents widget based on the cached index.  If \
        no widget is specified and autoadd is True, then a new widget will \
        be added to the tab.
        
        :param      autoadd | <bool>
        
        :return     <QWebView>
        """
        widget = self.uiContentsTAB.widget(self.currentContentsIndex())
        if (not isinstance(widget, QWebView)):
            widget = None

        if (not widget and autoadd):
            widget = self.addContentsWidget()

        return widget

    def currentWebView(self):
        return self.uiContentsTAB.currentWidget()

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()

    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            for url in event.mimeData().urls():
                self.loadFilename(str(url.toLocalFile()))

    def findNext(self):
        """
        Looks for the previous occurance of the current search text.
        """
        text = self.uiFindTXT.text()
        view = self.currentWebView()

        options = QWebPage.FindWrapsAroundDocument

        if (self.uiCaseSensitiveCHK.isChecked()):
            options |= QWebPage.FindCaseSensitively

        view.page().findText(text, options)

    def findPrev(self):
        """
        Looks for the previous occurance of the current search text.
        """
        text = self.uiFindTXT.text()
        view = self.currentWebView()

        options = QWebPage.FindWrapsAroundDocument
        options |= QWebPage.FindBackward

        if (self.uiCaseSensitiveCHK.isChecked()):
            options |= QWebPage.FindCaseSensitively

        view.page().findText(text, options)

    def findXdk(self, name):
        """
        Looks up the xdk item based on the current name.
        
        :param      name | <str>
        
        :return     <XdkItem> || None
        """
        for i in range(self.uiContentsTREE.topLevelItemCount()):
            item = self.uiContentsTREE.topLevelItem(i)
            if (item.text(0) == name):
                return item
        return None

    def goBack(self):
        widget = self.currentContentsWidget()
        if (widget):
            widget.page().history().back()

    def goForward(self):
        widget = self.currentContentsWidget()
        if (widget):
            widget.page().history().forward()

    def goHome(self):
        widget = self.currentContentsWidget()
        if (widget):
            widget.history().goHome()

    def gotoUrl(self, url):
        if not QApplication.keyboardModifiers() == Qt.ControlModifier:
            widget = self.currentContentsWidget(autoadd=True)
        else:
            widget = self.addContentsWidget()

        index = self.uiContentsTAB.indexOf(widget)
        self.uiContentsTAB.setCurrentIndex(index)
        widget.setUrl(QUrl(url))

    def gotoItem(self, path):
        """
        Goes to a particular path within the XDK.
        
        :param      path | <str>
        """
        if not path:
            return

        sections = str(path).split('/')
        check = projex.text.underscore(sections[0])

        for i in range(self.uiContentsTREE.topLevelItemCount()):
            item = self.uiContentsTREE.topLevelItem(i)
            if projex.text.underscore(item.text(1)) == check:
                item.gotoItem('/'.join(sections[1:]))
                break

    def loadItem(self, item):
        """
        Loads the inputed item.
        
        :param      item | <QTreeWidgetItem>
        """
        if isinstance(item, XdkEntryItem):
            item.load()

    def loadedFilenames(self):
        """
        Returns a list of all the xdk files that are currently loaded.
        
        :return     [<str>, ..]
        """
        output = []
        for i in range(self.uiContentsTREE.topLevelItemCount()):
            item = self.uiContentsTREE.topLevelItem(i)
            output.append(str(item.filepath()))
        return output

    def loadFilename(self, filename=''):
        """
        Loads a new XDK file into the system.
        
        :param      filename | <str>
        
        :return     <bool> | success
        """
        if (not (filename and isinstance(filename, basestring))):
            filename = QFileDialog.getOpenFileName(self, 'Open XDK File',
                                                   QDir.currentPath(),
                                                   'XDK Files (*.xdk)')

            if type(filename) == tuple:
                filename = str(filename[0])

            if not filename:
                return False

        if not (filename and os.path.exists(filename)):
            return False

        elif filename in self.loadedFilenames():
            return False

        self.loadFileRequested.emit(filename)
        self.setCursor(Qt.WaitCursor)

        return True

    def refreshFromIndex(self):
        """
        Refreshes the documentation from the selected index item.
        """
        item = self.uiIndexTREE.currentItem()
        if (not item):
            return

        self.gotoUrl(item.toolTip(0))

    def refreshContents(self):
        """
        Refreshes the contents tab with the latest selection from the browser.
        """
        item = self.uiContentsTREE.currentItem()
        if not isinstance(item, XdkEntryItem):
            return

        item.load()
        url = item.url()
        if url:
            self.gotoUrl(url)

    def refreshUi(self):
        """
        Refreshes the interface based on the current settings.
        """
        widget = self.uiContentsTAB.currentWidget()
        is_content = isinstance(widget, QWebView)

        if is_content:
            self._currentContentsIndex = self.uiContentsTAB.currentIndex()
            history = widget.page().history()
        else:
            history = None

        self.uiBackACT.setEnabled(is_content and history.canGoBack())
        self.uiForwardACT.setEnabled(is_content and history.canGoForward())
        self.uiHomeACT.setEnabled(is_content)
        self.uiNewTabACT.setEnabled(is_content)
        self.uiCopyTextACT.setEnabled(is_content)
        self.uiCloseTabACT.setEnabled(is_content
                                      and self.uiContentsTAB.count() > 2)

        for i in range(1, self.uiContentsTAB.count()):
            widget = self.uiContentsTAB.widget(i)
            self.uiContentsTAB.setTabText(i, widget.title())

    def search(self):
        """
        Looks up the current search terms from the xdk files that are loaded.
        """
        QApplication.instance().setOverrideCursor(Qt.WaitCursor)

        terms = str(self.uiSearchTXT.text())

        html = []

        entry_html = '<a href="%(url)s">%(title)s</a><br/>'\
                     '<small>%(url)s</small>'

        for i in range(self.uiContentsTREE.topLevelItemCount()):
            item = self.uiContentsTREE.topLevelItem(i)

            results = item.search(terms)
            results.sort(lambda x, y: cmp(y['strength'], x['strength']))
            for item in results:
                html.append(entry_html % item)

        if (not html):
            html.append('<b>No results were found for %s</b>' % terms)

        self.uiSearchWEB.setHtml(SEARCH_HTML % '<br/><br/>'.join(html))

        QApplication.instance().restoreOverrideCursor()

    def showFind(self):
        self.uiFindWIDGET.show()
        self.uiFindTXT.setFocus()
        self.uiFindTXT.selectAll()

    def zoomIn(self):
        view = self.currentWebView()
        view.setZoomFactor(view.zoomFactor() + 0.1)

    def zoomOut(self):
        view = self.currentWebView()
        view.setZoomFactor(view.zoomFactor() - 0.1)

    def zoomReset(self):
        view = self.currentWebView()
        view.setZoomFactor(1)

    @staticmethod
    def browse(parent, filename=''):
        """
        Creates a new XdkWidnow for browsing an XDK file.
        
        :param      parent      | <QWidget>
                    filename    | <str>
        """
        dlg = XdkWindow(parent)
        dlg.show()

        if (filename):
            dlg.loadFilename(filename)