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
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)
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()
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())
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())
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()
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()
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)