def test_task(self): results = [] task = Task(function=QThread.currentThread) task.resultReady.connect(results.append) task.start() self.app.processEvents() self.assertSequenceEqual(results, [QThread.currentThread()]) results = [] thread = QThread() thread.start() task = Task(function=QThread.currentThread) task.moveToThread(thread) self.assertIsNot(task.thread(), QThread.currentThread()) self.assertIs(task.thread(), thread) task.resultReady.connect(results.append, Qt.DirectConnection) task.start() f = task.future() self.assertIsNot(f.result(3), QThread.currentThread()) self.assertIs(f.result(3), results[-1])
def start(self, ser, cmd="", priority=QThread.InheritPriority): self.ser = ser #print(self.ser) print(cmd) self.cmd = str(cmd) + "\r" QThread.start(self, priority) print(self.cmd)
def uploadDoc(self): """ Upload the file to the central repository in a separate thread using the specified file manager """ if isinstance(self.fileManager, NetworkFileManager): self.pgBar.setVisible(True) self._docSize = self.fileInfo.size() ''' Create document transfer helper for multi-threading capabilities. Use of queued connections will guarantee that signals and slots are captured in any thread. ''' workerThread = QThread(self) docWorker = DocumentTransferWorker( self.fileManager, self.fileInfo, "%s"%(self._source_entity), "%s"%(self._doc_type), self ) docWorker.moveToThread(workerThread) workerThread.started.connect(docWorker.transfer) docWorker.blockWrite.connect(self.onBlockWritten) docWorker.complete.connect(self.onCompleteTransfer) workerThread.finished.connect(docWorker.deleteLater) workerThread.finished.connect(workerThread.deleteLater) workerThread.start() # Call transfer() to get fileUUID early #docWorker.transfer() self.fileUUID = docWorker.file_uuid
def start(self): """Reimplemented method QThread.start @param self A ThreadedExport instance """ self.pause = False QThread.start(self)
def startWorker(self): # create a new worker instance if self.worker is None: worker = UpdateRegistryWorker() # configure the QgsMessageBar messageBar = self.iface.messageBar().createMessage(u"Update Image Registry", u"Dieser Vorgang kann einige Minute dauern, bitte haben Sie geduld!") progressBar = QProgressBar() progressBar.setMinimum(0) progressBar.setMaximum(0) progressBar.setAlignment(Qt.AlignLeft|Qt.AlignVCenter) cancelButton = QPushButton() cancelButton.setText('Cancel') cancelButton.clicked.connect(self.killWorker) messageBar.layout().addWidget(progressBar) self.progressBar = progressBar messageBar.layout().addWidget(cancelButton) self.iface.messageBar().pushWidget(messageBar, self.iface.messageBar().INFO) #self.iface.messageBar().widgetRemoved # messageBar self.messageBar = messageBar # start the worker in a new thread thread = QThread() worker.moveToThread(thread) worker.finished.connect(self.workerFinished) worker.error.connect(self.workerError) #worker.progress.connect(progressBar.setValue) thread.started.connect(worker.run) thread.start() self.thread = thread self.worker = worker
def start(self): """ Reimplemented from `QThread.start` """ QThread.start(self) # Need to also handle method invoke from this thread self.moveToThread(self)
def search(self, text): logging.debug('Doing search.') thread = QThread() worker = SearchWorker(text, time.time()) worker.moveToThread(thread) thread.started.connect(worker.process) worker.finished.connect(thread.quit) worker.finished.connect(worker.deleteLater) worker.new_items.connect(functools.partial(self.repopulate, worker)) thread.finished.connect(thread.deleteLater) self._threads.append(thread) self.workers.append(worker) thread.start() # Revert search bar color. palette = main_window.palette() brush = palette.brush(QPalette.Highlight).color() palette.setBrush(QPalette.Base, QBrush(brush)) search_bar.setPalette(palette) # Clean up old threads. self._threads = [thread for thread in self._threads if not sip.isdeleted(thread)] self.workers = [worker for worker in self.workers if not sip.isdeleted(worker)]
def startWorker(self, ExtrairShape, CalcShape, OutShape, Ref, TypeUnits): # create a new worker instance worker = Worker(ExtrairShape, CalcShape, OutShape, Ref, TypeUnits) # configure the QgsMessageBar qgis.utils.iface.messageBar().clearWidgets() progressMessageBar = qgis.utils.iface.messageBar().createMessage('Calculating area...') progressBar = QProgressBar() progressBar.setMaximum(100) progressMessageBar.layout().addWidget(progressBar) qgis.utils.iface.messageBar().pushWidget(progressMessageBar) self.progressMessageBar = progressMessageBar # start the worker in a new thread thread = QThread() worker.moveToThread(thread) worker.finished.connect(self.workerFinished) worker.error.connect(self.workerError) worker.progress.connect(progressBar.setValue) thread.started.connect(worker.runCalc) thread.start() self.thread = thread self.worker = worker self.output = OutShape
class SignalStream(QObject): '''SignalStream is a file-like object that emits a text signal on writing This class is used to provide threadsafe communication of data to the GUI. A SignalStream can be used in place of sys.stdout and the instance's write_signal can be connected to a slot that processes the text to where it ought to go. Since signals and slots are threadsafe, this lets you pass text from anywhere to anywhere reasonably safely SignalStream uses some intelligent buffering to prevent the signalstorm that happened the first time I used it. Signal emit only happens when flush() is called - so an application can force a flush - but in order to make sure that happens reasonable often SignalStream can be initialized with a QTimer on an interval (default: 100ms) and the QTimer will make sure to call flush() every 100ms. ''' write_signal = pyqtSignal(str) def __init__(self, interval_ms=100): '''Create a SignalStream that emits text at least every interval_ms''' super(SignalStream, self).__init__() self.mutex = QMutex() self.data = [] self.thread = QThread() self.pbar_timer = QTimer() self.pbar_timer.moveToThread(self.thread) self.pbar_timer.setInterval(interval_ms) self.pbar_timer.timeout.connect(self.flush) self.thread.started.connect(self.pbar_timer.start) self.thread.start() def __del__(self): self.thread.quit() self.thread.wait() def write(self, m): '''Add the message in m to this stream's cache''' locker = QMutexLocker(self.mutex) self.data.append(m) @pyqtSlot() def flush(self): '''Write all data in the stream and clear the stream's cache''' locker = QMutexLocker(self.mutex) if self.data: self.write_signal.emit(''.join(self.data)) self.data = [] def set_interval(self, interval_ms): '''Alter the pbar_timer period''' self.pbar_timer.setInteval(interval_ms)
class __ThumbnailManager(QObject): def __init__(self): QObject.__init__(self) self.pixmapCache = QPixmapCache() self.pixmapCache.setCacheLimit(61440) self.thread = QThread() self.scaler = Scaler() self.scaler.moveToThread(self.thread) self.connect(self.scaler, SIGNAL("scaledFinished"), self.finished) self.thread.start() self.thumbnailers = [] self.handledRequest = set() self.corruptionHandler = CorruptedPictureHandler() self.pixmapCorrupted = QPixmap(":file_broken.png") def register(self, thumbnailer): self.thumbnailers.append(thumbnailer) def unregister(self, thumbnailer): try: self.thumbnailers.remove(thumbnailer) except: pass def generate(self, config): pixmap = self.pixmapCache.find(str(config)) if pixmap: return pixmap elif self.corruptionHandler.isCorrupted(config.node): return self.pixmapCorrupted else: if config not in self.handledRequest: QApplication.postEvent(self.scaler, ScaleEvent(config)) self.handledRequest.add(config) def finished(self, config, scaledImage): if scaledImage: pixmap = QPixmap().fromImage(scaledImage) self.pixmapCache.insert(str(config), pixmap) self.emitUpdate(config, pixmap) else: self.corruptionHandler.setAttributes(config.node) self.emitUpdate(config, self.pixmapCorrupted) def emitUpdate(self, config, pixmap): for thumbnailer in self.thumbnailers: try: if thumbnailer.request(config): thumbnailer.emit(SIGNAL("ThumbnailUpdate"), config.node, pixmap) thumbnailer.requestRemove(config) except: pass try: self.handledRequest.remove(config) except KeyError: pass
def start(self): if self.isRunning(): assert not self.rendering_all, "Cannot run twice the rendering of all images with the same object." return if parameters.instance.use_thread: log_debug("Starting rendering thread.") QThread.start(self) return False else: self.run() return True
class ProcTableWidget(QTreeView): def __init__(self, model, parent=None): super().__init__(parent) self.setSelectionBehavior(QTreeView.SelectRows) # setting uniform row heights allows optimization of the view self.setUniformRowHeights(True) self.model = model self.setModel(self.model) self.expandAll() # resize the column to fit all process names self.resizeColumnToContents(0) # keeps track of the previous sort order shown by the sort indicator. # this allows for a third state for the sort order: no sort self.prevSortOrder = None self.header().setClickable(True) self.header().sectionClicked.connect(self.setSortIndicator) # this worker thread grabs the latest process properties # so the GUI doesn't lag when it needs to update process data self.refreshThread = QThread(self) self.modelRefresher = ProcTableModelRefresher(self.model) self.modelRefresher.moveToThread(self.refreshThread) self.modelRefresher.modelRefresh.connect(self.model.update) self.refreshThread.started.connect(self.modelRefresher.startRefreshTimer) self.refreshThread.start() @pyqtSlot(int) def setSortIndicator(self, columnIdx): if self.isSortingEnabled(): # the 'no sort' state is only accessible by changing # the sort order of the process name column if columnIdx == 0 and self.prevSortOrder == Qt.DescendingOrder: self.header().setSortIndicatorShown(False) self.setSortingEnabled(False) self.model.removeSort() # setClickable() has to be set to True again after # calling setSortingEnabled(False) otherwise, the headers # cannot be clicked and the columns cannot be sorted self.header().setClickable(True) else: self.header().setSortIndicatorShown(True) # force the sort indicator to start with ascendingorder # so the cycling of sort order is predictable otherwise, # the sort order can start with DescendingOrder and vice versa # which makes it hard to detect when it's time to set the # 'no sort' state self.header().setSortIndicator(columnIdx, Qt.AscendingOrder) self.setSortingEnabled(True) self.prevSortOrder = self.header().sortIndicatorOrder()
class TargetThread(QObject): """ Simple QThread subclass made to support the ThreadPool interface, this thread uses the standard python approach to threading, of passing a callable to a Thread class. """ finished = pyqtSignal() def __init__(self, target, *args, **kwargs): """ TargetThread constructor. :param target: callable that gets called in the new thread of execution. :param args: var-args to pass to the target callable. :param kwargs: keyword-args to pass to the target callable. """ QObject.__init__(self) self.target = target self.args = args self.kwargs = kwargs self._thread = QThread() self.moveToThread(self._thread) self._connect_slots() def _connect_slots(self): self._thread.started.connect(self.run) def wait(self, timeout): """ Wait a given time, in seconds, for our thread to terminate. :param timeout: The time, in seconds, to wait. """ self._thread.wait(timeout) def start(self): """ Start our QThread instance, beginning execution of our target thread. """ self._thread.start() def run(self): """ Override of QThread run method that gets called from the new thread of execution, this is responsible for evoking the target callable with the args and kwargs given in the constructor. Lastly, it emits the finished signal to notify whom it may concern that our job is done. """ self.target(*self.args, **self.kwargs) self.finished.emit() self._thread.finished.emit() def terminate(self): """ Terminate this thread of execution abruptly. """ self._thread.terminate()
class Window(QtGui.QMainWindow): def __init__(self): super(Window, self).__init__() self.commandDialog = CommandDialog() self.exitAction = self.create_action('Q', self.close_me) self.test_launcher = QThread() self.test_launcher.run = keyM.monitor keyM.launcher = self.test_launcher keyM.launcher.connect(keyM.launcher, QtCore.SIGNAL('unhook(int)'), keyM.unhook) keyM.launcher.connect(keyM.launcher, QtCore.SIGNAL('keyPress(QString)'), self.key_press) self.test_launcher.start() self.trayIconMenu = QtGui.QMenu(self) self.trayIconMenu.addAction(self.exitAction) self.trayIcon = QtGui.QSystemTrayIcon(self) self.trayIcon.setContextMenu(self.trayIconMenu) self.trayIcon.setIcon(QtGui.QIcon('images/heart.png')) self.trayIcon.activated.connect(self.on_activated) self.trayIcon.show() State.trayIconDisplayed=True def key_press(self, value): print value def close_me(self): print 'close test_launcher..' self.test_launcher.emit(QtCore.SIGNAL('unhook(int)'), 0) print 'close application..' self.commandDialog.close() self.close() # ctypes.windll.user32.PostQuitMessage(0) def create_action(self, name, handler): _v = QAction(name, self) _v.triggered.connect(handler) return _v def on_activated(self, e): print type(e), e if e == 3: self.commandDialog.show() self.commandDialog.activateWindow() self.commandDialog.textEdit.setFocus() # self.commandDialog.textEdit.clear() self.commandDialog.textEdit.setEditable(True)
def start(self, tiling): self._queue = deque() self._tiling = tiling self._stopped = False self._numLayers = len(self._stackedIms) shape = (self._numLayers, len(self._tiling)) self._imageLayersNext = numpy.ndarray(shape, dtype = object) self._compositeCurrent = numpy.ndarray((len(self._tiling),), dtype = object) self._compositeNext = numpy.ndarray((len(self._tiling),), dtype = object) QThread.start(self)
class Analyzer(object): def __init__(self, controller): self.controller = controller self.analyzer_runner = AnalyzerRunner(controller) self.analyzer_runner_thread = QThread() self.finish_function = None self.send_result_function = None def make_analyze(self, finish_function, result_function): self.finish_function = finish_function self.send_result_function = result_function if self.analyzer_runner.is_running: print("cancel old analyze") self.cancel_analyz() print("start new analyze") #self.analyzer_runner.whole_scene = whole_scene self.analyzer_runner.moveToThread(self.analyzer_runner_thread) self.analyzer_runner_thread.started.connect(self.analyzer_runner.start_analyze) self.analyzer_runner.finished.connect(self.set_finished_read) self.analyzer_runner.send_result.connect(self.set_result) self.analyzer_runner.is_running = True self.analyzer_runner_thread.start() def cancel_analyz(self): self.analyzer_runner.is_running = False self.analyzer_runner_thread.quit() self.analyzer_runner_thread.wait() self.analyzer_runner_thread = QThread() self.analyzer_runner = AnalyzerRunner(self.controller) def set_finished_read(self): print("analyze done") if self.finish_function: self.finish_function() def set_result(self, result): print(result) if self.send_result_function: self.send_result_function(result) '''
def add_projector(self, projector, start=False): """ Builds manager list item, projector thread, and timer for projector instance. :param projector: Projector instance to add :param start: Start projector if True """ item = ProjectorItem(link=self._add_projector(projector)) item.db_item = projector icon = QtGui.QIcon(QtGui.QPixmap(STATUS_ICONS[S_NOT_CONNECTED])) item.icon = icon widget = QtGui.QListWidgetItem(icon, item.link.name, self.projector_list_widget ) widget.setData(QtCore.Qt.UserRole, item) item.link.db_item = item.db_item item.widget = widget thread = QThread(parent=self) thread.my_parent = self item.moveToThread(thread) thread.started.connect(item.link.thread_started) thread.finished.connect(item.link.thread_stopped) thread.finished.connect(thread.deleteLater) item.link.projectorNetwork.connect(self.update_status) item.link.changeStatus.connect(self.update_status) item.link.projectorAuthentication.connect(self.authentication_error) item.link.projectorNoAuthentication.connect(self.no_authentication_error) item.link.projectorUpdateIcons.connect(self.update_icons) timer = QtCore.QTimer(self) timer.setInterval(self.poll_time) timer.timeout.connect(item.link.poll_loop) item.timer = timer # Timeout in case of brain-dead projectors or cable disconnected socket_timer = QtCore.QTimer(self) socket_timer.setInterval(11000) socket_timer.timeout.connect(item.link.socket_abort) item.socket_timer = socket_timer thread.start() item.thread = thread item.link.timer = timer item.link.socket_timer = socket_timer item.link.widget = item.widget self.projector_list.append(item) if start: item.link.connect_to_host() for item in self.projector_list: log.debug('New projector list - item: (%s) %s' % (item.link.ip, item.link.name))
class NativePocketSphinx(AsrBackend): recognizedToFile = QtCore.pyqtSignal(str) def __init__(self): super().__init__() logger.info("Initializing PocketSphinx backend") self.ps = PocketSphinx() self.enablePocketSphinx() self.contWorker = ContinuousAsrWorker() self.workerThread = QThread(self) self.workerThread.started.connect(self.contWorker.start) self.workerThread.finished.connect(self.contWorker.stop) self.contWorker.moveToThread(self.workerThread) def enablePocketSphinx(self): logger.debug("Initializing PocketSphinx decoder") self.ps.initializeDecoder() logger.debug("Initializing audio recording") self.ps.initializeAudio() def disablePocketSphinx(self): logger.debug("Shutting down audio recording") self.ps.shutdownAudio() logger.debug("Shutting down PocketSphinx decoder") self.ps.shutdownDecoder() def recognizeFromMicrophone(self, sinkFileName): logger.debug("[NativePocketSphinx] Recognizing from microphone to " + sinkFileName) self.ps.recognizeFromMicrophone(sinkFileName) self.recognizedToFile.emit(sinkFileName) logger.debug("[NativePocketSphinx] Recognition done") def startContinuousRecognition(self): logger.info("Starting continuous speech recognition") self.disablePocketSphinx() self.workerThread.start() def stopContinuousRecognition(self): logger.info("Stopping continuous speech recognition") self.workerThread.quit() self.enablePocketSphinx() #TODO: Implement actual checking of availability of the backend @classmethod def supported(self): return True
def parserFinished(self, success): if success: #Create a thread to parse the data thread = QThread(self) worker = self.worker = ffaWorker() worker.moveToThread(thread) thread.started.connect(worker.run) #Print the periodic status messages to the text browser worker.status.connect(self.dlg.addToTextBrowser) worker.error.connect(self.workerError) #Connect these signals so we know when the thread finishes worker.finished.connect(self.ffaFinished) worker.finished.connect(worker.deleteLater) thread.finished.connect(thread.deleteLater) thread.finished.connect(thread.quit) thread.start()
class MyWindowClass(QtGui.QMainWindow, form_class): def __init__(self, parent=None): QtGui.QMainWindow.__init__(self, parent) self.setupUi(self) self.StartButton.clicked.connect(self.ButtonUpdate_start_clicked)# Bind the event handlers self.comboSerialBox.addItems(serial_ports('freq')) #Gets a list of avaliable serial ports to connect to and adds to combo box self.comboSerialBox_2.addItems(serial_ports('detector')) self.StopButton.clicked.connect(self.ButtonUpdate_stop_clicked) self.pen = pg.mkPen(color=(0,0,0), width=2) self.plotWidget.plotItem.getAxis('left').setPen(self.pen) self.plotWidget.plotItem.getAxis('bottom').setPen(self.pen) self.plotWidget.setLabel('left', 'Amp', 'arb') self.plotWidget.setLabel('bottom', 'Freq', 'MHz') self.plt = self.plotWidget def ButtonUpdate_start_clicked(self): self.ScanThread = QThread(self) self.scandata = [float(self.StartSpinBox.text()),float(self.StopSpinBox.text()),float(self.StepSpinBox.text())] self.freq_device = str(self.comboSerialBox.currentText()) self.detector_device = str(self.comboSerialBox_2.currentText()) self.ScanRunner = ScanRunner(self.scandata,self.freq_device,self.detector_device) self.ScanRunner.devicestatus.connect(self.control_label.setText) self.ScanRunner.moveToThread(self.ScanThread) self.ScanThread.started.connect(self.ScanRunner.run) self.ScanRunner.freqIncreased.connect(self.label_Freq.setText) self.ScanRunner.stepIncreased.connect(self.label_Step.setText) self.ScanRunner.devicestatus.connect(self.control_label_2.setText) self.ScanRunner.plotUpdate.connect(self.update_plot) self.ScanThread.finished.connect(self.ScanThread.quit) self.ScanThread.start() def ButtonUpdate_stop_clicked(self): self.ScanRunner.stop() def update_plot(self,data): self.plt.plot(data[0],data[1],clear=True,pen={'color':'k','width':2})
class NativePocketSphinx(QObject): recognizedToFile = QtCore.pyqtSignal(str) def __init__(self): super().__init__() logger.info("Initializing PocketSphinx backend") self.ps = PocketSphinx() logger.debug("Initializing PocketSphinx decoder") self.ps.initializeDecoder() logger.debug("Initializing audio recording") self.ps.initializeAudio() self.contWorker = ContinuousAsrWorker() self.workerThread = QThread(self) self.workerThread.started.connect(self.contWorker.start) self.workerThread.finished.connect(self.contWorker.deleteLater) self.contWorker.moveToThread(self.workerThread) def recognizeFromMicrophone(self, sinkFileName): self.ps.recognizeFromMicrophone(sinkFileName) self.recognizedToFile.emit(sinkFileName) # This returns an iterator for n file names following some naming schema def fileNamesRange(self, n, stem="hyp"): for i in range(1, n+1): yield stem + str(i) #TODO: Write a solution for continuous recognition # Location of written files (and naming schema) should be governed by # a configuration manager # This will probably need a separate thread (QThread to be used) # FIXME: We should probably shut down the main PS instance to avoid conflicts # between it and the worker def startContinuousRecognition(self): logger.info("Starting continuous speech recognition") self.workerThread.start() def stopContinuousRecognition(self): logger.info("Stopping continuous speech recognition") self.workerThread.quit() @classmethod def supported(): return True
def pointWorkerFinish(self, success, inputs): #Create a new thread to call the point index service if success: thread = QThread(self) worker = self.worker = NavigationWorker(inputs, self.navMutex) worker.moveToThread(thread) #Connect the slots thread.started.connect(worker.run) #Print the periodic status messages to the text browser worker.status.connect(self.dlg.setTextBrowser) worker.error.connect(self.workerError) #When done, call the pointWOrkerFinish to go to the next task worker.finished.connect(self.navWorkerFinish) #Clean up the thread worker.finished.connect(worker.deleteLater) thread.finished.connect(thread.deleteLater) worker.finished.connect(thread.quit) thread.start()
def runner(): app = QApplication(sys.argv) window = QMainWindow() ui = Ui_MainWindow() ui.setupUi(window) cam = CameraDevice() thread = QThread() cam.moveToThread(thread) thread.start() thread.setPriority(QThread.LowestPriority) atexit.register(cam.capture.release) cam.video_signal.connect(ui.set_image) ui.preview_on.clicked.connect(cam.preview_on) ui.preview_on.clicked.connect(cam.startVideo) ui.preview_off.clicked.connect(cam.preview_off) # ui.preview_off.clicked.disconnect(cam.startVideo) window.show() sys.exit(app.exec_())
class SlicerEngineManager(object): ''' SlicerEngineManager is class designed for managing slicers engine and prepare parameters ''' def __init__(self, controller): self.controller = controller self.slice_thread = None self.slice_engine = Slic3rEngineRunner(self.controller) def slice(self): self.slice_thread = QThread() #TODO:Make it universal(for other slice engines) self.slice_engine = Slic3rEngineRunner(self.controller) self.slice_engine.moveToThread(self.slice_thread) self.slice_thread.started.connect(self.slice_engine.slice) self.slice_engine.finished.connect(self.thread_ended) self.slice_engine.filament_info.connect(self.controller.set_print_info_text) self.slice_engine.step_increased.connect(self.controller.set_progress_bar) self.slice_engine.send_message.connect(self.controller.slicing_message) self.slice_engine.send_gcodedata.connect(self.controller.set_gcode_instance) self.slice_thread.start() def cancel(self): logging.debug("Thread canceling") if self.slice_engine and self.slice_thread: self.slice_engine.is_running = False self.slice_engine.kill() self.slice_thread.quit() self.slice_thread.wait() self.controller.status = 'canceled' self.controller.set_generate_button() self.controller.set_progress_bar(0.0) def thread_ended(self): self.slice_thread.quit() #TODO: add function to read gcode self.controller.scene_was_sliced() def get_version(self): return self.slice_engine.get_version()
class StopperWidget(QWidget): def __init__(self): super(QWidget, self).__init__() self.stopper = Stopper() self.timer_thread = QThread() print "w.timer_thread: %r" % self.timer_thread self.timer_thread.start() self.stopper.moveToThread(self.timer_thread) self.stopper.start_timer.emit() self.button = QPushButton(self) self.button.setText("Stop timer") self.button.clicked.connect(self.stopper.stop_timer) def __del__(self): print QThread.currentThread() print self.thread() print self.timer_thread self.stopper.stop_timer.emit()
def start_worker(worker, iface, message, with_progress=True): # configure the QgsMessageBar message_bar = iface.messageBar().createMessage(message) progress_bar = QProgressBar() progress_bar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) if not with_progress: progress_bar.setMinimum(0) progress_bar.setMaximum(0) cancel_button = QPushButton() cancel_button.setText('Cancel') cancel_button.clicked.connect(worker.kill) message_bar.layout().addWidget(progress_bar) message_bar.layout().addWidget(cancel_button) iface.messageBar().pushWidget(message_bar, iface.messageBar().INFO) # start the worker in a new thread # let Qt take ownership of the QThread thread = QThread(iface.mainWindow()) worker.moveToThread(thread) worker.set_message.connect(lambda message: set_worker_message( message, message_bar_item)) worker.toggle_show_progress.connect(lambda show: toggle_worker_progress( show, progress_bar)) worker.toggle_show_cancel.connect(lambda show: toggle_worker_cancel( show, cancel_button)) worker.finished.connect(lambda result: worker_finished( result, thread, worker, iface, message_bar)) worker.error.connect(lambda e, exception_str: worker_error( e, exception_str, iface)) worker.progress.connect(progress_bar.setValue) thread.started.connect(worker.run) thread.start() return thread, message_bar
def handleMouseDown(self, point, button): #QMessageBox.information( self.iface.mainWindow(), "Info", "X,Y = %s,%s"%( str(point.x()), str(point.y()) ) ) self.dlg.clearTextBrowser() self.dlg.setTextBrowser( str(point.x())+' , '+str(point.y()) ) #Create a new thread to call the point index service thread = QThread(self) worker = self.worker = PointWorker(point) worker.moveToThread(thread) #Connect the slots thread.started.connect(worker.run) #Print the periodic status messages to the text browser worker.status.connect(self.dlg.setTextBrowser) worker.error.connect(self.workerError) #When done, call the pointWOrkerFinish to go to the next task worker.finished.connect(self.pointWorkerFinish) #Clean up the thread worker.finished.connect(worker.deleteLater) thread.finished.connect(thread.deleteLater) worker.finished.connect(thread.quit) thread.start()
def worker_start(self, layer, field_name, iterations): """Start a worker instance on a background thread.""" worker = CartogramWorker(layer, field_name, iterations) message_bar = self.iface.messageBar().createMessage('') label = QLabel('Creating cartogram...') label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) progress_bar = QProgressBar() progress_bar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) progress_bar.setMaximum(iterations * layer.featureCount()) cancel_button = QPushButton() cancel_button.setText(self.tr('Cancel')) cancel_button.clicked.connect(worker.kill) message_bar.layout().addWidget(label) message_bar.layout().addWidget(progress_bar) message_bar.layout().addWidget(cancel_button) self.iface.messageBar().pushWidget(message_bar, self.iface.messageBar().INFO) self.message_bar = message_bar # start the worker in a new thread thread = QThread() worker.moveToThread(thread) # connect some odds and ends worker.finished.connect(self.worker_finished) worker.error.connect(self.worker_error) worker.progress.connect(progress_bar.setValue) thread.started.connect(worker.run) thread.start() self.thread = thread self.worker = worker
class QHamegLCR(QObject, HamegLCR): """ Extension of the HamegLCR class. It enables a number of nonblocking functionalities by employing the Qt signal-slot mechanism. Note that even though the object is self moved in its separate thread, direct calling of it methods does not execute in this thread but in the thread from which the method was called. """ values_signal = pyqtSignal(float, float) def __init__(self, *args, **kwargs): self.__lock = threading.Lock() QObject.__init__(self) HamegLCR.__init__(self, *args, **kwargs) self._thread = QThread() self._thread.start() self.moveToThread(self._thread) @pyqtSlot() def nonblockingGetValues(self): self.triggerAndWait() x1 = self.getMainValue() x2 = self.getSecondaryValue() self.values_signal.emit(x1, x2) def read(self): self.__lock.acquire() val = HamegLCR.read(self) self.__lock.release() return val def write(self, string): self.__lock.acquire() HamegLCR.write(self, string) self.__lock.release()
def worker_start(self, dir, url, id): """Start a worker instance on a background thread.""" worker = NaturalEarthRasterWorker(dir, url, id) message_bar = self.iface.messageBar().createMessage('') label = QLabel('Downloading theme {}...'.format(id)) label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) progress_bar = QProgressBar() progress_bar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) progress_bar.setMaximum(100) cancel_button = QPushButton() cancel_button.setText(self.tr('Cancel')) cancel_button.clicked.connect(worker.kill) message_bar.layout().addWidget(label) message_bar.layout().addWidget(progress_bar) message_bar.layout().addWidget(cancel_button) self.iface.messageBar().pushWidget(message_bar, self.iface.messageBar().INFO) self.message_bar = message_bar # start the worker in a new thread thread = QThread() worker.moveToThread(thread) # connect some odds and ends worker.finished.connect(self.worker_finished) worker.error.connect(self.worker_error) worker.progress.connect(progress_bar.setValue) thread.started.connect(worker.run) thread.start() self.thread = thread self.worker = worker
class AddonManagerDialog(QDialog): def __init__(self, parent=None, **kwargs): super(AddonManagerDialog, self).__init__(parent, **kwargs) self.setLayout(QVBoxLayout()) self.addonwidget = AddonManagerWidget() self.addonwidget.layout().setContentsMargins(0, 0, 0, 0) self.layout().addWidget(self.addonwidget) buttons = QDialogButtonBox(orientation=Qt.Horizontal, standardButtons=QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttons.accepted.connect(self.__accepted) buttons.rejected.connect(self.reject) self.layout().addWidget(buttons) self.__progress = QProgressDialog( self, Qt.Sheet, minimum=0, maximum=0, labelText=self.tr("Retrieving package list"), sizeGripEnabled=False, windowTitle="Progress") self.__progress.canceled.connect(self.reject) # The installer thread self.__thread = None # The installer object self.__installer = None @Slot(list) def setItems(self, items): self.addonwidget.setItems(items) def progressDialog(self): return self.__progress def done(self, retcode): super(AddonManagerDialog, self).done(retcode) if self.__thread is not None: self.__thread.quit() self.__thread.wait(1000) def closeEvent(self, event): super(AddonManagerDialog, self).closeEvent(event) if self.__thread is not None: self.__thread.quit() self.__thread.wait(1000) def __accepted(self): steps = self.addonwidget.itemState() if steps: # Move all uninstall steps to the front steps = sorted(steps, key=lambda step: 0 if step[0] == Uninstall else 1) self.__installer = Installer(steps=steps) self.__thread = QThread(self) self.__thread.start() self.__installer.moveToThread(self.__thread) self.__installer.finished.connect(self.__on_installer_finished) self.__installer.error.connect(self.__on_installer_error) self.__installer.installStatusChanged.connect( self.__progress.setLabelText) self.__progress.show() self.__progress.setLabelText("Installing") self.__installer.start() else: self.accept() def __on_installer_error(self, command, pkg, retcode, output): message_error( "An error occurred while running a subprocess", title="Error", informative_text="{} exited with non zero status.".format(command), details="".join(output), parent=self) self.reject() def __on_installer_finished(self): message_information( "Please restart the application for changes to take effect.", parent=self) self.accept()
class XOrbRecordBox(XComboBox): __designer_group__ = 'ProjexUI - ORB' """ Defines a combo box that contains records from the ORB system. """ loadRequested = Signal(object) loadingStarted = Signal() loadingFinished = Signal() currentRecordChanged = Signal(object) currentRecordEdited = Signal(object) initialized = Signal() def __init__(self, parent=None): # needs to be defined before the base class is initialized or the # event filter won't work self._treePopupWidget = None super(XOrbRecordBox, self).__init__(parent) # define custom properties self._currentRecord = None # only used while loading self._changedRecord = -1 self._tableTypeName = '' self._tableLookupIndex = '' self._baseHints = ('', '') self._tableType = None self._order = None self._query = None self._iconMapper = None self._labelMapper = str self._required = True self._loaded = False self._showTreePopup = False self._autoInitialize = False self._threadEnabled = True self._specifiedColumns = None self._specifiedColumnsOnly = False # create threading options self._worker = XOrbLookupWorker() self._workerThread = QThread() self._worker.moveToThread(self._workerThread) self._worker.setBatched(False) self._workerThread.start() # create connections self.loadRequested.connect(self._worker.loadRecords) self.lineEdit().textEntered.connect(self.assignCurrentRecord) self.lineEdit().editingFinished.connect(self.emitCurrentRecordEdited) self.lineEdit().returnPressed.connect(self.emitCurrentRecordEdited) self._worker.loadingStarted.connect(self.markLoadingStarted) self._worker.loadingFinished.connect(self.markLoadingFinished) self._worker.loadedRecords.connect(self.addRecordsFromThread) self.currentIndexChanged.connect(self.emitCurrentRecordChanged) QApplication.instance().aboutToQuit.connect(self.__cleanupWorker) def __del__(self): self.__cleanupWorker() def __cleanupWorker(self): if not self._workerThread: return thread = self._workerThread worker = self._worker self._workerThread = None self._worker = None worker.deleteLater() thread.finished.connect(thread.deleteLater) thread.quit() thread.wait() def addRecord(self, record): """ Adds the given record to the system. :param record | <str> """ label_mapper = self.labelMapper() icon_mapper = self.iconMapper() self.addItem(label_mapper(record)) self.setItemData(self.count() - 1, wrapVariant(record), Qt.UserRole) # load icon if icon_mapper: self.setItemIcon(self.count() - 1, icon_mapper(record)) if self.showTreePopup(): XOrbRecordItem(self.treePopupWidget(), record) def addRecords(self, records): """ Adds the given record to the system. :param records | [<orb.Table>, ..] """ label_mapper = self.labelMapper() icon_mapper = self.iconMapper() # create the items to display tree = None if self.showTreePopup(): tree = self.treePopupWidget() tree.blockSignals(True) tree.setUpdatesEnabled(False) # add the items to the list start = self.count() self.addItems(map(label_mapper, records)) # update the item information for i, record in enumerate(records): index = start + i self.setItemData(index, wrapVariant(record), Qt.UserRole) if icon_mapper: self.setItemIcon(index, icon_mapper(record)) if tree: XOrbRecordItem(tree, record) if tree: tree.blockSignals(False) tree.setUpdatesEnabled(True) def addRecordsFromThread(self, records): """ Adds the given record to the system. :param records | [<orb.Table>, ..] """ label_mapper = self.labelMapper() icon_mapper = self.iconMapper() tree = None if self.showTreePopup(): tree = self.treePopupWidget() # add the items to the list start = self.count() # update the item information blocked = self.signalsBlocked() self.blockSignals(True) for i, record in enumerate(records): index = start + i self.addItem(label_mapper(record)) self.setItemData(index, wrapVariant(record), Qt.UserRole) if icon_mapper: self.setItemIcon(index, icon_mapper(record)) if record == self._currentRecord: self.setCurrentIndex(self.count() - 1) if tree: XOrbRecordItem(tree, record) self.blockSignals(blocked) def acceptRecord(self, item): """ Closes the tree popup and sets the current record. :param record | <orb.Table> """ record = item.record() self.treePopupWidget().close() self.setCurrentRecord(record) def assignCurrentRecord(self, text): """ Assigns the current record from the inputed text. :param text | <str> """ if self.showTreePopup(): item = self._treePopupWidget.currentItem() if item: self._currentRecord = item.record() else: self._currentRecord = None return # look up the record for the given text if text: index = self.findText(text) elif self.isRequired(): index = 0 else: index = -1 # determine new record to look for record = self.recordAt(index) if record == self._currentRecord: return # set the current index and record for any changes self._currentRecord = record self.setCurrentIndex(index) def autoInitialize(self): """ Returns whether or not this record box should auto-initialize its records. :return <bool> """ return self._autoInitialize def batchSize(self): """ Returns the batch size to use when processing this record box's list of entries. :return <int> """ return self._worker.batchSize() def checkedRecords(self): """ Returns a list of the checked records from this combo box. :return [<orb.Table>, ..] """ indexes = self.checkedIndexes() return map(self.recordAt, indexes) def currentRecord(self): """ Returns the record found at the current index for this combo box. :rerturn <orb.Table> || None """ if self._currentRecord is None and self.isRequired(): self._currentRecord = self.recordAt(self.currentIndex()) return self._currentRecord def dragEnterEvent(self, event): """ Listens for query's being dragged and dropped onto this tree. :param event | <QDragEnterEvent> """ data = event.mimeData() if data.hasFormat('application/x-orb-table') and \ data.hasFormat('application/x-orb-query'): tableName = self.tableTypeName() if str(data.data('application/x-orb-table')) == tableName: event.acceptProposedAction() return elif data.hasFormat('application/x-orb-records'): event.acceptProposedAction() return super(XOrbRecordBox, self).dragEnterEvent(event) def dragMoveEvent(self, event): """ Listens for query's being dragged and dropped onto this tree. :param event | <QDragEnterEvent> """ data = event.mimeData() if data.hasFormat('application/x-orb-table') and \ data.hasFormat('application/x-orb-query'): tableName = self.tableTypeName() if str(data.data('application/x-orb-table')) == tableName: event.acceptProposedAction() return elif data.hasFormat('application/x-orb-records'): event.acceptProposedAction() return super(XOrbRecordBox, self).dragMoveEvent(event) def dropEvent(self, event): """ Listens for query's being dragged and dropped onto this tree. :param event | <QDropEvent> """ # overload the current filtering options data = event.mimeData() if data.hasFormat('application/x-orb-table') and \ data.hasFormat('application/x-orb-query'): tableName = self.tableTypeName() if str(data.data('application/x-orb-table')) == tableName: data = str(data.data('application/x-orb-query')) query = Q.fromXmlString(data) self.setQuery(query) return elif self.tableType() and data.hasFormat('application/x-orb-records'): from projexui.widgets.xorbtreewidget import XOrbTreeWidget records = XOrbTreeWidget.dataRestoreRecords(data) for record in records: if isinstance(record, self.tableType()): self.setCurrentRecord(record) return super(XOrbRecordBox, self).dropEvent(event) def emitCurrentRecordChanged(self): """ Emits the current record changed signal for this combobox, provided \ the signals aren't blocked. """ record = unwrapVariant(self.itemData(self.currentIndex(), Qt.UserRole)) if not Table.recordcheck(record): record = None self._currentRecord = record if not self.signalsBlocked(): self._changedRecord = record self.currentRecordChanged.emit(record) def emitCurrentRecordEdited(self): """ Emits the current record edited signal for this combobox, provided the signals aren't blocked and the record has changed since the last time. """ if self._changedRecord == -1: return if self.signalsBlocked(): return record = self._changedRecord self._changedRecord = -1 self.currentRecordEdited.emit(record) def eventFilter(self, object, event): """ Filters events for the popup tree widget. :param object | <QObject> event | <QEvent> :retuen <bool> | consumed """ if not (object and object == self._treePopupWidget): return super(XOrbRecordBox, self).eventFilter(object, event) elif event.type() == event.KeyPress: # accept lookup if event.key() in (Qt.Key_Enter, Qt.Key_Return, Qt.Key_Tab, Qt.Key_Backtab): item = object.currentItem() text = self.lineEdit().text() if not text: record = None item = None elif isinstance(item, XOrbRecordItem): record = item.record() if record and item.isSelected() and not item.isHidden(): self.hidePopup() self.setCurrentRecord(record) event.accept() return True else: self.setCurrentRecord(None) self.hidePopup() self.lineEdit().setText(text) self.lineEdit().keyPressEvent(event) event.accept() return True # cancel lookup elif event.key() == Qt.Key_Escape: text = self.lineEdit().text() self.setCurrentRecord(None) self.lineEdit().setText(text) self.hidePopup() event.accept() return True # update the search info else: self.lineEdit().keyPressEvent(event) elif event.type() == event.Show: object.resizeToContents() object.horizontalScrollBar().setValue(0) elif event.type() == event.KeyRelease: self.lineEdit().keyReleaseEvent(event) elif event.type() == event.MouseButtonPress: local_pos = object.mapFromGlobal(event.globalPos()) in_widget = object.rect().contains(local_pos) if not in_widget: text = self.lineEdit().text() self.setCurrentRecord(None) self.lineEdit().setText(text) self.hidePopup() event.accept() return True return super(XOrbRecordBox, self).eventFilter(object, event) def focusNextChild(self, event): if not self.isLoading(): self.assignCurrentRecord(self.lineEdit().text()) return super(XOrbRecordBox, self).focusNextChild(event) def focusNextPrevChild(self, event): if not self.isLoading(): self.assignCurrentRecord(self.lineEdit().text()) return super(XOrbRecordBox, self).focusNextPrevChild(event) def focusInEvent(self, event): """ When this widget loses focus, try to emit the record changed event signal. """ self._changedRecord = -1 super(XOrbRecordBox, self).focusInEvent(event) def focusOutEvent(self, event): """ When this widget loses focus, try to emit the record changed event signal. """ if not self.isLoading(): self.assignCurrentRecord(self.lineEdit().text()) super(XOrbRecordBox, self).focusOutEvent(event) def hidePopup(self): """ Overloads the hide popup method to handle when the user hides the popup widget. """ if self._treePopupWidget and self.showTreePopup(): self._treePopupWidget.close() super(XOrbRecordBox, self).hidePopup() def iconMapper(self): """ Returns the icon mapping method to be used for this combobox. :return <method> || None """ return self._iconMapper def isLoading(self): """ Returns whether or not this combobox is loading records. :return <bool> """ return self._worker.isRunning() def isRequired(self): """ Returns whether or not this combo box requires the user to pick a selection. :return <bool> """ return self._required def isThreadEnabled(self): """ Returns whether or not threading is enabled for this combo box. :return <bool> """ return self._threadEnabled def labelMapper(self): """ Returns the label mapping method to be used for this combobox. :return <method> || None """ return self._labelMapper @Slot(object) def lookupRecords(self, record): """ Lookups records based on the inputed record. This will use the tableLookupIndex property to determine the Orb Index method to use to look up records. That index method should take the inputed record as an argument, and return a list of records. :param record | <orb.Table> """ table_type = self.tableType() if not table_type: return index = getattr(table_type, self.tableLookupIndex(), None) if not index: return self.setRecords(index(record)) def markLoadingStarted(self): """ Marks this widget as loading records. """ if self.isThreadEnabled(): XLoaderWidget.start(self) if self.showTreePopup(): tree = self.treePopupWidget() tree.setCursor(Qt.WaitCursor) tree.clear() tree.setUpdatesEnabled(False) tree.blockSignals(True) self._baseHints = (self.hint(), tree.hint()) tree.setHint('Loading records...') self.setHint('Loading records...') else: self._baseHints = (self.hint(), '') self.setHint('Loading records...') self.setCursor(Qt.WaitCursor) self.blockSignals(True) self.setUpdatesEnabled(False) # prepare to load self.clear() use_dummy = not self.isRequired() or self.isCheckable() if use_dummy: self.addItem('') self.loadingStarted.emit() def markLoadingFinished(self): """ Marks this widget as finished loading records. """ XLoaderWidget.stop(self, force=True) hint, tree_hint = self._baseHints self.setHint(hint) # set the tree widget if self.showTreePopup(): tree = self.treePopupWidget() tree.setHint(tree_hint) tree.unsetCursor() tree.setUpdatesEnabled(True) tree.blockSignals(False) self.unsetCursor() self.blockSignals(False) self.setUpdatesEnabled(True) self.loadingFinished.emit() def order(self): """ Returns the ordering for this widget. :return [(<str> column, <str> asc|desc, ..] || None """ return self._order def query(self): """ Returns the query used when querying the database for the records. :return <Query> || None """ return self._query def records(self): """ Returns the record list that ist linked with this combo box. :return [<orb.Table>, ..] """ records = [] for i in range(self.count()): record = self.recordAt(i) if record: records.append(record) return records def recordAt(self, index): """ Returns the record at the inputed index. :return <orb.Table> || None """ return unwrapVariant(self.itemData(index, Qt.UserRole)) def refresh(self, records): """ Refreshs the current user interface to match the latest settings. """ self._loaded = True if self.isLoading(): return # load the information if RecordSet.typecheck(records): table = records.table() self.setTableType(table) if self.order(): records.setOrder(self.order()) # load specific data for this record box if self.specifiedColumnsOnly(): records.setColumns( map(lambda x: x.name(), self.specifiedColumns())) # load the records asynchronously if self.isThreadEnabled() and \ table and \ table.getDatabase().isThreadEnabled(): # assign ordering based on tree table if self.showTreePopup(): tree = self.treePopupWidget() if tree.isSortingEnabled(): col = tree.sortColumn() colname = tree.headerItem().text(col) column = table.schema().column(colname) if column: if tree.sortOrder() == Qt.AscendingOrder: sort_order = 'asc' else: sort_order = 'desc' records.setOrder([(column.name(), sort_order)]) self.loadRequested.emit(records) return # load the records synchronously self.loadingStarted.emit() curr_record = self.currentRecord() self.blockSignals(True) self.setUpdatesEnabled(False) self.clear() use_dummy = not self.isRequired() or self.isCheckable() if use_dummy: self.addItem('') self.addRecords(records) self.setUpdatesEnabled(True) self.blockSignals(False) self.setCurrentRecord(curr_record) self.loadingFinished.emit() def setAutoInitialize(self, state): """ Sets whether or not this combo box should auto initialize itself when it is shown. :param state | <bool> """ self._autoInitialize = state def setBatchSize(self, size): """ Sets the batch size of records to look up for this record box. :param size | <int> """ self._worker.setBatchSize(size) def setCheckedRecords(self, records): """ Sets the checked off records to the list of inputed records. :param records | [<orb.Table>, ..] """ QApplication.sendPostedEvents(self, -1) indexes = [] for i in range(self.count()): record = self.recordAt(i) if record is not None and record in records: indexes.append(i) self.setCheckedIndexes(indexes) def setCurrentRecord(self, record, autoAdd=False): """ Sets the index for this combobox to the inputed record instance. :param record <orb.Table> :return <bool> success """ if record is not None and not Table.recordcheck(record): return False # don't reassign the current record # clear the record if record is None: self._currentRecord = None blocked = self.signalsBlocked() self.blockSignals(True) self.setCurrentIndex(-1) self.blockSignals(blocked) if not blocked: self.currentRecordChanged.emit(None) return True elif record == self.currentRecord(): return False self._currentRecord = record found = False blocked = self.signalsBlocked() self.blockSignals(True) for i in range(self.count()): stored = unwrapVariant(self.itemData(i, Qt.UserRole)) if stored == record: self.setCurrentIndex(i) found = True break if not found and autoAdd: self.addRecord(record) self.setCurrentIndex(self.count() - 1) self.blockSignals(blocked) if not blocked: self.currentRecordChanged.emit(record) return False def setIconMapper(self, mapper): """ Sets the icon mapping method for this combobox to the inputed mapper. \ The inputed mapper method should take a orb.Table instance as input \ and return a QIcon as output. :param mapper | <method> || None """ self._iconMapper = mapper def setLabelMapper(self, mapper): """ Sets the label mapping method for this combobox to the inputed mapper.\ The inputed mapper method should take a orb.Table instance as input \ and return a string as output. :param mapper | <method> """ self._labelMapper = mapper def setOrder(self, order): """ Sets the order for this combo box to the inputed order. This will be used in conjunction with the query when loading records to the combobox. :param order | [(<str> column, <str> asc|desc), ..] || None """ self._order = order def setQuery(self, query, autoRefresh=True): """ Sets the query for this record box for generating records. :param query | <Query> || None """ self._query = query tableType = self.tableType() if not tableType: return False if autoRefresh: self.refresh(tableType.select(where=query)) return True def setRecords(self, records): """ Sets the records on this combobox to the inputed record list. :param records | [<orb.Table>, ..] """ self.refresh(records) def setRequired(self, state): """ Sets the required state for this combo box. If the column is not required, a blank record will be included with the choices. :param state | <bool> """ self._required = state def setShowTreePopup(self, state): """ Sets whether or not to use an ORB tree widget in the popup for this record box. :param state | <bool> """ self._showTreePopup = state def setSpecifiedColumns(self, columns): """ Sets the specified columns for this combobox widget. :param columns | [<orb.Column>, ..] || [<str>, ..] || None """ self._specifiedColumns = columns self._specifiedColumnsOnly = columns is not None def setSpecifiedColumnsOnly(self, state): """ Sets whether or not only specified columns should be loaded for this record box. :param state | <bool> """ self._specifiedColumnsOnly = state def setTableLookupIndex(self, index): """ Sets the name of the index method that will be used to lookup records for this combo box. :param index | <str> """ self._tableLookupIndex = str(index) def setTableType(self, tableType): """ Sets the table type for this record box to the inputed table type. :param tableType | <orb.Table> """ self._tableType = tableType if tableType: self._tableTypeName = tableType.schema().name() else: self._tableTypeName = '' def setTableTypeName(self, name): """ Sets the table type name for this record box to the inputed name. :param name | <str> """ self._tableTypeName = str(name) self._tableType = None def setThreadEnabled(self, state): """ Sets whether or not threading should be enabled for this widget. Actual threading will be determined by both this property, and whether or not the active ORB backend supports threading. :param state | <bool> """ self._threadEnabled = state def setVisible(self, state): """ Sets the visibility for this record box. :param state | <bool> """ super(XOrbRecordBox, self).setVisible(state) if state and not self._loaded: if self.autoInitialize(): table = self.tableType() if not table: return self.setRecords(table.select(where=self.query())) else: self.initialized.emit() def showPopup(self): """ Overloads the popup method from QComboBox to display an ORB tree widget when necessary. :sa setShowTreePopup """ if not self.showTreePopup(): return super(XOrbRecordBox, self).showPopup() tree = self.treePopupWidget() if tree and not tree.isVisible(): tree.move(self.mapToGlobal(QPoint(0, self.height()))) tree.resize(self.width(), 250) tree.resizeToContents() tree.filterItems('') tree.setFilteredColumns(range(tree.columnCount())) tree.show() def showTreePopup(self): """ Sets whether or not to use an ORB tree widget in the popup for this record box. :return <bool> """ return self._showTreePopup def specifiedColumns(self): """ Returns the list of columns that are specified based on the column view for this widget. :return [<orb.Column>, ..] """ columns = [] table = self.tableType() tree = self.treePopupWidget() schema = table.schema() if self._specifiedColumns is not None: colnames = self._specifiedColumns else: colnames = tree.columns() for colname in colnames: if isinstance(colname, Column): columns.append(colname) else: col = schema.column(colname) if col and not col.isProxy(): columns.append(col) return columns def specifiedColumnsOnly(self): """ Returns whether or not only specified columns should be loaded for this record box. :return <int> """ return self._specifiedColumnsOnly def tableLookupIndex(self): """ Returns the name of the index method that will be used to lookup records for this combo box. :return <str> """ return self._tableLookupIndex def tableType(self): """ Returns the table type for this instance. :return <subclass of orb.Table> || None """ if not self._tableType: if self._tableTypeName: self._tableType = Orb.instance().model(str( self._tableTypeName)) return self._tableType def tableTypeName(self): """ Returns the table type name that is set for this combo box. :return <str> """ return self._tableTypeName def treePopupWidget(self): """ Returns the popup widget for this record box when it is supposed to be an ORB tree widget. :return <XTreeWidget> """ if not self._treePopupWidget: # create the treewidget tree = XTreeWidget(self) tree.setWindowFlags(Qt.Popup) tree.setFocusPolicy(Qt.StrongFocus) tree.installEventFilter(self) tree.setAlternatingRowColors(True) tree.setShowGridColumns(False) tree.setRootIsDecorated(False) tree.setVerticalScrollMode(tree.ScrollPerPixel) # create connections tree.itemClicked.connect(self.acceptRecord) self.lineEdit().textEdited.connect(tree.filterItems) self.lineEdit().textEdited.connect(self.showPopup) self._treePopupWidget = tree return self._treePopupWidget def worker(self): """ Returns the worker object for loading records for this record box. :return <XOrbLookupWorker> """ return self._worker x_batchSize = Property(int, batchSize, setBatchSize) x_required = Property(bool, isRequired, setRequired) x_tableTypeName = Property(str, tableTypeName, setTableTypeName) x_tableLookupIndex = Property(str, tableLookupIndex, setTableLookupIndex) x_showTreePopup = Property(bool, showTreePopup, setShowTreePopup) x_threadEnabled = Property(bool, isThreadEnabled, setThreadEnabled)
class Measurement(QObject): throwMessage = pyqtSignal(str, int, name='throwMessage') sigSaveMeasurement = pyqtSignal(bool, name='save') updateWindows = pyqtSignal(str, name="updateView") sigUpdateCountrate = pyqtSignal(int, str) sigTest = pyqtSignal() def __init__(self, openFile=None, themisApp=None): QObject.__init__(self) self.themisApp = themisApp self.configWidget = configurationWidget.configurationWidget(self) self.config = self.configWidget.p self.DataThread = QThread(self) self.dataHandler = DataHandler(self.configWidget.toDict()) self.dataHandler.moveToThread(self.DataThread) self.dataHandler.updateTimer.timeout.connect( self.dataHandler.updateGui) self.dataHandler.sigUpdateGui.connect(self.updateView, QtCore.Qt.QueuedConnection) self.configWidget.sigConfigChanged.connect( self.dataHandler.configChanged, QtCore.Qt.QueuedConnection) self.DataThread.start() self.viewer = DataViewer(name="Measurement") self.timeFilter = [None, None] self.acquisitionTime = -1 #SetWindows self.viewer.addWidget("Control", self.configWidget, position="left", Size=(2, 2), minSize=(250, 250), sizePolicy=(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)) self.timeImage = self.viewer.addWidget("Time Image", PlotBase.TimeImageWidget(self), position="right", relativeTo="Control", Size=(10, 10), minSize=(300, 250)) self.energyImage = self.viewer.addWidget( "Energy Image", PlotBase.EnergyImageWidget(self), position="below", relativeTo="Time Image", Size=(10, 10), minSize=(300, 250)) self.countrate = self.viewer.addWidget("Countrate", PlotBase.CountrateWidget(), position="bottom", relativeTo="Time Image", Size=(10, 10), minSize=(300, 250)) self.keithley = self.viewer.addWidget("Keithley", PlotBase.KeithleyWidget(), position="below", relativeTo="Countrate", Size=(10, 10), minSize=(300, 250)) self.timeHistogram = self.viewer.addWidget( "Time Histogram", PlotBase.TimeHistogramWidget(), position="right", relativeTo="Time Image", Size=(10, 10), minSize=(300, 250)) self.energyHistogram = self.viewer.addWidget( "Energy Histogram", PlotBase.EnergyHistogramWidget(self), position="below", relativeTo="Time Histogram", Size=(10, 10), minSize=(300, 250)) if themisApp != None: self.liveView = self.viewer.addWidget("MCP", PlotBase.LiveView(self), position="above", relativeTo="Time Image", Size=(10, 10), minSize=(400, 250)) self.viewer.addWidget("Themis", self.themisApp, position="above", relativeTo="Control", Size=(10, 10), minSize=(250, 250)) # Buttons=QtGui.QHBoxLayout() # self.start=QtGui.QPushButton("Start") # self.start.setIcon(QtGui.QIcon('Bilder/accept.png')) # #self.start.clicked.connect(self.startMeasurement) # Buttons.addWidget(self.start) # self.pause=QtGui.QPushButton("Pause") # self.pause.setIcon(QtGui.QIcon('Bilder/pause.png')) # #self.pause.clicked.connect(self.pauseMeasurement) # Buttons.addWidget(self.pause) # self.stop=QtGui.QPushButton("Stop") # self.stop.setIcon(QtGui.QIcon('Bilder/stop.png')) # #self.stop.clicked.connect(self.on_stop_clicked) # Buttons.addWidget(self.stop) # #self.configWidget.layout().insertWidget(0,Buttons) else: self.liveView = None #None#self.viewer.addWidget("MCP",PlotBase.LiveView(),position="above",relativeTo="Time Image",Size=(10,10),minSize=(400,250)) if not openFile == None: self.loadMeasurement(openFile) self.initializeSignals() def finalize(self): self.dataHandler.finalize() self.dataHandler.deleteLater() del self.dataHandler self.DataThread.quit() self.DataThread.wait() #self.dataHandler.updateTimer.timeout.disconnect(self.dataHandler.updateGui) #self.dataHandler.sigUpdateGui.disconnect(self.updateView) #self.configWidget.sigConfigChanged.disconnect(self.dataHandler.configChanged,) #self.dataHandler.deleteLater() def initializeSignals(self): self.configWidget.connectSignal( self.config.param("Spectra", "Time Histogram", "Time min").sigValueChanged, self.timeConfigChanged) self.configWidget.connectSignal( self.config.param("Spectra", "Time Histogram", "Time max").sigValueChanged, self.timeConfigChanged) self.configWidget.connectSignal( self.config.param("Spectra", "Time Histogram", "Time resolution").sigValueChanged, self.timeConfigChanged) self.configWidget.connectSignal( self.config.param("Spectra", "Time Image", "Xbins").sigValueChanged, self.timeConfigChanged) self.configWidget.connectSignal( self.config.param("Spectra", "Time Image", "Ybins").sigValueChanged, self.timeConfigChanged) self.configWidget.connectSignal( self.config.param("Spectra", "Rotate MCP").sigValueChanged, self.dataHandler.rotationChanged) self.configWidget.connectSignal( self.config.param("DLLParams", "X Min").sigValueChanged, self.timeConfigChanged) self.configWidget.connectSignal( self.config.param("DLLParams", "X Max").sigValueChanged, self.timeConfigChanged) self.configWidget.connectSignal( self.config.param("DLLParams", "Y Min").sigValueChanged, self.timeConfigChanged) self.configWidget.connectSignal( self.config.param("DLLParams", "Y Max").sigValueChanged, self.timeConfigChanged) self.configWidget.connectSignal( self.config.param('File Configuration', 'Save Measurement').sigActivated, self.saveMeasurement) self.configWidget.connectSignal( self.config.param('Spectra', 'Create new Conversion').sigActivated, self.createStandardConversion) self.sigSaveMeasurement.connect(self.saveMeasurement) self.timeHistogram.selectionChanged.connect(self.selectionChanged) def selectionChanged(self, min, max): with self.dataHandler: self.dataHandler.dataSet.timeImage.fill(0) electrons = self.dataHandler.dataSet.getElectronsFromRawData() if min == -999999999: timeFilter = (None, None) else: timeFilter = (self.dataHandler.dataSet.unitsToRawData( min, "T"), self.dataHandler.dataSet.unitsToRawData(max, "T")) if not timeFilter[0] == None and not timeFilter[1] == None: electrons = electrons[np.logical_and( electrons["time"] > timeFilter[0], electrons["time"] < timeFilter[1])] elif not timeFilter[0] == None: electrons = electrons[electrons["time"] > timeFilter[0]] elif not timeFilter[1] == None: electrons = electrons[electrons["time"] < timeFilter[1]] self.dataHandler.dataSet.setElectrons(electrons=electrons, spectrum="timeImage") self.updateView( {"timeImage": np.copy(self.dataHandler.dataSet.timeImage)}) def timeConfigChanged(self): with self.dataHandler: if not self.dataHandler.dataSet.rawData == None: self.dataHandler.dataSet.setTimeRange() self.dataHandler.dataSet.setTimeImageRange() electrons = self.dataHandler.dataSet.getElectronsFromRawData() self.dataHandler.dataSet.setElectrons( electrons=electrons, spectrum=["timeHistogram", "timeImage"]) self.dataHandler.dataTransfer(["timeHistogram", "timeImage"]) else: self.dataHandler.dataSet.initialize() self.dataHandler.sigUpdateRequest.emit(["all"]) def updateView(self, data=None): if data == None: self.dataHandler.sigUpdateRequest.emit("all") return if "timeHistogram" in data: self.timeHistogram.updateData(data["timeHistogram"][0], data["timeHistogram"][1]) if "energyHistogram" in data: self.energyHistogram.updateData(data["energyHistogram"][0], data["energyHistogram"][1]) if "timeImage" in data: self.timeImage.updateData(data["timeImage"]) if "energyImage" in data: self.energyImage.updateData(data["energyImage"]) if "countrate" in data: self.countrate.updateData(data["countrate"][0], data["countrate"][1]) if "keithley" in data: self.keithley.updateData(data["keithley"][0], data["keithley"][1]) if "liveView" in data: if self.liveView != None: self.liveView.updateData(data["liveView"][0]) self.liveView.countrate.setValue(data["liveView"][1]) self.liveView.selection.setValue(data["liveView"][2]) def setDLDParameter(self, factors, offsets, pxLimits): #set from dld self.configWidget.setDLLParams(factors=factors, offsets=offsets, pxLimits=pxLimits) def createConversion(self, dialog=False, recommended=False): conversion = EnergyConversion.ConversionItem(self.dataHandler.dataSet, self.configWidget) with self.dataHandler: if not self.dataHandler.dataSet.rawData == None: conversion.control.state.getEventData( )._data = self.dataHandler.dataSet.getElectronsFromRawData() if dialog: newconversion = conversion.startConversionDialog() else: self.throwMessage.emit("Create ConversionSplines", 2) newconversion = conversion.createStandardConversion( recommended=recommended) self.throwMessage.emit("--> Finished", 4) if newconversion == None: return self.configWidget.setEnergyParams(newconversion.factors, newconversion.offsets) with self.dataHandler: self.dataHandler.dataSet.conversion = conversion self.dataHandler.dataSet.energyHistogram = np.sum( np.sum(newconversion.getDataContent(), 0), 0) self.dataHandler.dataSet.energyImage = np.sum( newconversion.getDataContent(), 2) self.dataHandler.dataSet.setEnergyRange() self.energyImage.setExtent() self.dataHandler.sigUpdateRequest.emit( ["energyHistogram", "energyImage"]) def startConversionDialog(self): self.createConversion(dialog=True) def createStandardConversion(self): self.createConversion() def clearSpectra(self, rawData=True): QtCore.QMetaObject.invokeMethod(self.dataHandler, "clearSpectra", QtCore.Qt.QueuedConnection, QtCore.Q_ARG(bool, True)) # with self.dataHandler: # # self.dataHandler.dataSet.clearSpectra() # self.dataHandler.electrons=np.empty(0, [("x", 'i2'), ("y", 'i2'), ("time", 'i4')]) # if rawData: # self.dataHandler.closeRawData() # self.dataHandler.sigUpdateRequest.emit(["all"]) def saveMeasurement(self, confirm=True, autoOverride=False): path = self.config["File Configuration", "Folder"] if path == "Default": path = fileaccess.createDefaultPath( self.config["File Configuration", "Folder", "Beamtime"], self.config["File Configuration", "Folder", "Groupname"]) else: path = self.config["File Configuration", "Folder", "Custom Folder"] if not fileaccess.checkIfPathExist(path): self.throwMessage.emit("--> Could not create the desired Path", 0) self.throwMessage.emit("--> Measurement was not Saved", 2) return False filename = self.config["File Configuration", "Filename"] if self.themisApp != None: filename = filename.replace( "%n", ("%03d" % self.themisApp.parent.joblist.measurementNumber.value())) self.themisApp.parent.joblist.measurementNumber.setValue( self.themisApp.parent.joblist.measurementNumber.value() + 1) file = path + "/" + filename + ".lv" if not autoOverride: if fileaccess.checkIfFileExists(file): file = fileaccess.fileOverwriteDialog(file, path) if file == None: self.throwMessage.emit("--> Measurement was not Saved", 2) return False self.throwMessage.emit("Save Measurement as: " + file, 2) file = file.replace("/", "\\") with self.dataHandler: if not self.dataHandler.dataSet.rawData == None and self.dataHandler.dataSet.rawData.filename.replace( "/", "\\") == file.replace("/", "\\"): file = file + "_temporay_while_edited" h5file = fileaccess.openFile(file, mode="w", title="Measurement") ##! Adjust and Save Config config = self.config.saveState() config["children"]["File Configuration"]["children"]["Folder"][ "children"]["Custom Folder"]["value"] = path config["children"]["File Configuration"]["children"]["Folder"][ "value"] = "Custom" config["children"]["File Configuration"]["children"]["Filename"][ "value"] = filename if config["children"]["Measurement Info"]["children"][ "End of Acquisition"]["value"] == "not yet finished": config["children"]["Measurement Info"]["children"][ "End of Acquisition"]["value"] = datetime.datetime.now( ).strftime("%Y-%m-%d, %H:%M:%S") h5file.root._v_attrs.config = str(config) self.dataHandler.dataSet.saveDataSet(h5file) h5file.flush() if "_temporay_while_edited" in file: self.dataHandler.dataSet.rawData.close() _cwd = os.getcwd() os.chdir(path) name = file.split("\\")[-1] os.remove(file[0:-22]) h5file.close() os.rename(name, name[0:-22]) self.dataHandler.dataSet.rawData = fileaccess.openFile( file[0:-22], mode="a") self.dataHandler.dataSet.events = self.dataHandler.dataSet.rawData.getNode( "/rawData/", "events") else: h5file.close() if self.config["File Configuration", "Copy to Grouphome"]: grouphome = path.replace("C:/", "Z:/") if grouphome == path: return try: fileaccess.checkIfPathExist(grouphome, create=True) shutil.copy(file, grouphome) except (WindowsError): self.throwMessage.emit( "Error: Could not connect to Grouphome", 2) if confirm: msgBox = QtGui.QMessageBox(self.viewer) msgBox.setWindowTitle("Measurement Saved") msgBox.setText("Measurement has been saved to:") msgBox.setInformativeText(file) msgBox.exec_() @staticmethod def loadMeasurement(file, mode="r"): measurement = Measurement() measurement.load(file, mode=mode) return (measurement) def load(self, file, mode="r"): self.viewer.setWindowTitle("Data Viewer - " + file) if not fileaccess.checkIfFileExists(file): self.throwMessage.emit("Load Error: File " + file + " not found", 0) return h5file = fileaccess.openFile(file, mode=mode) h5file = self.configWidget.restoreState(h5file, file=file) with self.dataHandler: self.dataHandler.dataSet.config = self.configWidget.toDict() self.dataHandler.dataSet.loadDataSet(h5file) self.timeImage.setExtent() self.energyImage.setExtent() self.dataHandler.sigUpdateRequest.emit(["all"]) def show(self): self.viewer.show()
class SearchPlugin(widget, base, Page): title = "Search" icon = resolve("search.svg") def __init__(self, api, parent=None): super(SearchPlugin, self).__init__(parent) self.setupUi(self) self.api = api self.project = None self.dbpath = None self.flickcharm = FlickCharm() self.flickcharm.activateOn(self.resultsView) self.searchbox.textChanged.connect(self.search) self.searchbox.installEventFilter(self) self.resultsView.itemClicked.connect(self.jump_to) self.rebuildLabel.linkActivated.connect(self.rebuild_index) self.fuzzyCheck.stateChanged.connect(self.fuzzy_changed) self.indexbuilder = None self.indexthread = None def fuzzy_changed(self, state): self.search(self.searchbox.text()) def index_built(self, dbpath, timing): self.dbpath = dbpath self.resultsView.clear() self.searchbox.setEnabled(True) print "Index built in: {} seconds".format(timing) def eventFilter(self, object, event): if event.type() == QEvent.FocusIn: RoamEvents.openkeyboard.emit() return False @property def db(self): db = sqlite3.connect(self.dbpath) db.create_function("rank", 1, make_rank_func((1., .1, 0, 0))) return db def project_loaded(self, project): self.project = project self.build_index(project) def rebuild_index(self): self.build_index(self.project) def build_index(self, project): self.searchbox.setEnabled(False) self.resultsView.setEnabled(False) self.resultsView.addItem("Just let me build the search index first....") validformat, settings = valid_search_settings(project.settings) if not validformat: RoamEvents.raisemessage("Searching", "Invalid search config.", level=1) self.searchbox.hide() self.resultsView.clear() self.resultsView.addItem("Invalid search config found") return self.indexthread = QThread() self.indexbuilder = IndexBuilder(project.folder, settings) self.indexbuilder.moveToThread(self.indexthread) self.indexbuilder.indexBuilt.connect(self.index_built) self.indexbuilder.finished.connect(self.indexthread.quit) self.indexthread.started.connect(self.indexbuilder.build_index) self.indexthread.finished.connect(self.indexbuilder.quit) self.indexthread.start() def search(self, text): db = self.db c = db.cursor() self.resultsView.clear() self.resultsView.setEnabled(False) if not text: return if self.fuzzyCheck.isChecked(): search = "* ".join(text.split()) + "*" else: search = text query = c.execute("""SELECT layer, featureid, snippet(search, '[',']') as snippet FROM search JOIN featureinfo on search.docid = featureinfo.id WHERE search match '{}' LIMIT 100""".format(search)).fetchall() for layer, featureid, snippet in query: item = QListWidgetItem() text = "{}\n {}".format(layer, snippet.replace('\n', ' ')) item.setText(text) item.setData(Qt.UserRole, (layer, featureid, snippet)) self.resultsView.addItem(item) self.resultsView.setEnabled(True) if self.resultsView.count() == 0: self.resultsView.addItem("No Results") db.close() def jump_to(self, item): data = item.data(32) layername, fid = data[0], data[1] layer = roam.api.utils.layer_by_name(layername) layer.select(fid) feature = layer.selectedFeatures()[0] self.api.mainwindow.canvas.zoomToSelected(layer) layer.removeSelection() self.api.mainwindow.showmap() RoamEvents.selectionchanged.emit({layer: [feature]})
transmitter = Transmitter() debug_object(transmitter) transmitter.moveToThread(tx_thread) debug_object(transmitter) rx_thread = QThread() debug_thread("rx_thread", rx_thread) if USE_DERIVED: receiver = Derived() else: receiver = Base() debug_object(receiver) receiver.moveToThread(rx_thread) debug_object(receiver) # Signals: startup tx_thread.started.connect(transmitter.start) rx_thread.started.connect(receiver.start) # ... shutdown transmitter.finished.connect(tx_thread.quit) tx_thread.finished.connect(rx_thread.quit) rx_thread.finished.connect(app.quit) # ... action transmitter.transmit.connect(receiver.receive) # Go rx_thread.start() tx_thread.start() report("Starting app") app.exec_()
class Runner: """A wrapper around thread and worker objects Correctly starts a new thread and moves worker to it. """ def __init__(self, cls): """Store worker class in instance""" self.cls = cls self.running = False def start(self, *args, **kwargs): """Starts worker in separate thread""" # stop any previously running workers self.stop() self.result = None self.error = None self.running = True self.thread = QThread() self.worker = self.cls(*args, **kwargs) self.worker.moveToThread(self.thread) self.thread.started.connect(self.worker.start) # graceful stop does not work without direct connection (?) self.worker.finished.connect(self.thread.quit, Qt.DirectConnection) self.worker.finished.connect(self.worker.deleteLater) self.thread.finished.connect(self.thread.deleteLater) # also save result/error on finish (useful for testing) self.worker.finished.connect(self.handlefinished, Qt.DirectConnection) self.worker.raised.connect(self.handleraised, Qt.DirectConnection) self.worker.completed.connect(self.handlecompleted, Qt.DirectConnection) self.worker.killed.connect(self.handlekilled, Qt.DirectConnection) self.thread.start() # returns worker so caller can connect signals, etc. return self.worker def stop(self): """Attempts to gracefully stop worker Forcefully terminates otherwise.""" logging.debug('Worker: trying to stop gracefully') if not self.running: logging.debug('Worker was not running') return try: self.worker.kill() if not self.thread.wait(settings.KILL_TIMEOUT): raise KillTimeoutError() except KillTimeoutError: logging.warn('Worker: kill timed out, force terminating...') self.thread.terminate() self.thread.wait() except Exception as e: logging.warn('Worker: terminate failed!?') logging.warn(e) # The following methods are for testing @pyqtSlot(str) def handleraised(self, error): """Slot that saves error, useful for testing""" logging.warn('[WORKER] raised: {}'.format(error)) self.error = error @pyqtSlot(object) def handlefinished(self): logging.debug('[WORKER] finished') self.running = False @pyqtSlot(object) def handlecompleted(self, result): """Slot that saves result, useful for testing""" logging.debug('[WORKER] completed') self.result = result @pyqtSlot(object) def handlekilled(self): logging.debug('[WORKER] killed') def wait(self, timeout): """Wait for thread to end, and return result Useful for testing. """ try: self.thread.wait(timeout) return self.result except: return None
class v_calculator_main(QObject): """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: QgisInterface """ super(v_calculator_main, self).__init__() self.calculation_thread = QThread(self) self.calculation_worker = None # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join( self.plugin_dir, 'i18n', 'vegetation_calculator_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) # Declare instance attributes self.actions = [] self.menu = self.getTranslation(u'&Vegetation calculator') # We are going to let the user set this up in a future iteration self.toolbar = self.iface.addToolBar(u'vegetation_calculator') self.toolbar.setObjectName(u'vegetation_calculator') self.LOGGER = logging.getLogger("calculator_logger") if len(self.LOGGER.handlers) == 0: format_log = \ "%(asctime)s - [%(levelname)s] - %(name)s - (%(filename)s).%(funcName)s(%(lineno)d) - %(message)s" fh = RotatingFileHandler(filename=os.path.join(self.plugin_dir, "calculator.log"), maxBytes=5 * 1024 * 1024, backupCount=5) fh.setFormatter(logging.Formatter(format_log)) self.LOGGER.addHandler(fh) self.LOGGER.setLevel(logging.DEBUG) # noinspection PyMethodMayBeStatic def getTranslation(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('v_calculator_main', 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 = vegetation_calculatorDialog() self.initHandlers() 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.addPluginToRasterMenu( self.menu, action) self.actions.append(action) return action def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = os.path.join(self.plugin_dir, 'icon.png') self.add_action( icon_path, text=self.getTranslation(u'Open calculator'), 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.removePluginRasterMenu( self.getTranslation(u'&Vegetation calculator'), action) self.iface.removeToolBarIcon(action) # remove the toolbar del self.toolbar def initHandlers(self): """ Initializes handlers for UI element events. """ self.LOGGER.debug("init handlers") self.dlg.accepted.connect(self.startCalculation) self.dlg.cbx_ndvi_redLayer.currentIndexChanged.connect(self.showLayerBandsForNdviRed) self.dlg.cbx_ndvi_infraredLayer.currentIndexChanged.connect(self.showLayerBandsForNdviInfrared) self.dlg.cbx_agr_swirLayer.currentIndexChanged.connect(self.showLayerBandsForAgroSwir) self.dlg.cbx_agr_nnirLayer.currentIndexChanged.connect(self.showLayerBandsForAgroIr) self.dlg.cbx_agr_blueLayer.currentIndexChanged.connect(self.showLayerBandsForAgroBlue) def run(self): """Run method that performs all the real work""" logging.info("start") layers = QgsMapLayerRegistry.instance().mapLayers() self.showLayersLists(layers) self.showColorSchemes() # show the dialog self.LOGGER.debug("show the dialog") self.dlg.show() self.LOGGER.debug("run the dialog event loop") # Run the dialog event loop result = self.dlg.exec_() self.LOGGER.info("end") def showLayersLists(self, layers): """ Display a list of raster layers on the UI. :param layers: layers to displaying. :type: [QgsMapLayer, ...] """ self.LOGGER.debug("showing layers lists") self.dlg.cbx_ndvi_redLayer.clear() self.dlg.cbx_ndvi_infraredLayer.clear() self.dlg.cbx_agr_swirLayer.clear() self.dlg.cbx_agr_nnirLayer.clear() self.dlg.cbx_agr_blueLayer.clear() layer_names = [] for name, layer in layers.iteritems(): if layer.type() == 1: # 1 = raster layer layer_names.append(layer.name()) layer_names.sort(cmp=locale.strcoll) self.dlg.cbx_ndvi_redLayer.addItems(layer_names) self.dlg.cbx_ndvi_infraredLayer.addItems(layer_names) self.dlg.cbx_agr_swirLayer.addItems(layer_names) self.dlg.cbx_agr_nnirLayer.addItems(layer_names) self.dlg.cbx_agr_blueLayer.addItems(layer_names) def showLayerBandsForNdviRed(self, index): """ Display bands of selected layer with red band for NDVI. :param index: index of an item in the QCombobox. :type: int """ self.LOGGER.debug("showing bands of the red layer (NDVI)") layer_name = self.dlg.cbx_ndvi_redLayer.itemText(index) self.showLayerBands(self.dlg.lstw_ndvi_redBands, layer_name, 3) # 3 = red def showLayerBandsForNdviInfrared(self, index): """ Display bands of selected layer with infrared band for NDVI. :param index: index of an item in the QCombobox. :type: int """ self.LOGGER.debug("showing bands of the infrared layer (NDVI)") layer_name = self.dlg.cbx_ndvi_infraredLayer.itemText(index) self.showLayerBands(self.dlg.lstw_ndvi_infraredBands, layer_name, 0) # 0 = undefined color def showLayerBandsForAgroSwir(self, index): """ Display bands of selected layer with SWIR (short wave infrared) band for agriculture or healthy vegetation. :param index: index of an item in the QCombobox. :type: int """ self.LOGGER.debug("showing bands of the SWIR layer (agriculture and HV)") layer_name = self.dlg.cbx_agr_swirLayer.itemText(index) self.showLayerBands(self.dlg.lstw_agr_swirBands, layer_name, 0) # 0 = undefined color def showLayerBandsForAgroIr(self, index): """ Display bands of selected layer with infrared band for agriculture or healthy vegetation. :param index: index of an item in the QCombobox. :type: int """ self.LOGGER.debug("showing bands of the IR layer (agriculture and HV)") layer_name = self.dlg.cbx_agr_nnirLayer.itemText(index) self.showLayerBands(self.dlg.lstw_agr_nnirBands, layer_name, 0) # 0 = undefined color def showLayerBandsForAgroBlue(self, index): """ Display bands of selected layer with blue band for agriculture or healthy vegetation. :param index: index of an item in the QCombobox. :type: int """ self.LOGGER.debug("showing bands of the blue layer (agriculture and HV)") layer_name = self.dlg.cbx_agr_blueLayer.itemText(index) self.showLayerBands(self.dlg.lstw_agr_blueBands, layer_name, 5) # 5 = blue def showLayerBands(self, qListWidget, layer_name, color_interpretation=None): """ Display bands of the layer into the QListWidget. :param qListWidget: A QListWidget on the UI. :type QListWidget :param layer_name: A name of layer with bands to display :type: unicode :param color_interpretation: Color interpretation for automatic band selection. :type: int """ self.LOGGER.debug("showing bands of %s", layer_name) try: raster_layer = QgsMapLayerRegistry.instance().mapLayersByName(layer_name)[0] except IndexError: return bands_dictionary = self.getBandsFromLayer(raster_layer) sorted(bands_dictionary) qListWidget.clear() index = 0 band_number = None for band_information in bands_dictionary.values(): qListWidget.addItem(band_information.full_name) if band_number is None and band_information.color_interpretation == color_interpretation: band_number = index index += 1 if band_number is not None: qListWidget.setCurrentRow(band_number) else: qListWidget.setCurrentRow(0) def getBandsFromLayer(self, raster_layer): """ Get bands of the raster layer. :param raster_layer: A layer to get channels from. :type: QgsRasterLayer :return: dictionary of BandInformation. :type: {unicode: BandInformation} """ self.LOGGER.debug("getting bands of %s", raster_layer.name()) layer_data_provider = raster_layer.dataProvider() bands = {} for band_number in range(1, raster_layer.bandCount() + 1): band = BandInformation(layer_data_provider.colorInterpretationName(band_number), band_number, layer_data_provider.colorInterpretation(band_number)) bands[band.full_name] = band return OrderedDict(sorted(bands.items())) def showColorSchemes(self): """ Display color schemes. """ self.LOGGER.debug("showing color schemes") self.dlg.cbx_color_schemes.clear() color_schemes = OrderedDict(sorted(ColorsForNdviMap().colorSchemes.items())) for color_scheme_name in color_schemes: color_scheme = color_schemes[color_scheme_name] icon_pixmap = QPixmap(50, 20) painter = QPainter(icon_pixmap) painter.fillRect(0, 0, 10, 20, color_scheme["ndvi_0"]) painter.fillRect(10, 0, 20, 20, color_scheme["ndvi_0.25"]) painter.fillRect(20, 0, 30, 20, color_scheme["ndvi_0.5"]) painter.fillRect(30, 0, 40, 20, color_scheme["ndvi_0.75"]) painter.fillRect(40, 0, 50, 20, color_scheme["ndvi_1"]) painter.end() icon = QIcon(icon_pixmap) self.dlg.cbx_color_schemes.addItem(icon, color_scheme_name) def startCalculation(self): """ Start calculating NDVI, agriculture or healthy vegetation """ self.LOGGER.debug("start calculation") if self.calculation_thread.isRunning() is True: return if self.dlg.tabw_content.currentIndex() == 0: # NDVI if self.dlg.rbtn_calculateNdvi.isChecked(): self.calculateNdvi() elif self.dlg.rbtn_openNdviFile.isChecked(): try: input_file_name = self.dlg.led_ndvi_inputFile.text() self.validateInputFilePath(input_file_name) except CalculatorException as exp: self.LOGGER.info(exp.message) self.dlg.show_error_message(self.getTranslation(exp.title), self.getTranslation(exp.message)) return self.openNdviFile(input_file_name) elif self.dlg.tabw_content.currentIndex() == 1: # Agriculture and HV self.calculateAgricultureOrHv() def calculateAgricultureOrHv(self): """ Start calculating agriculture or healthy vegetation """ self.LOGGER.info("start agriculture or hv calculation") self.LOGGER.info("Agriculture: %s", self.dlg.rbtn_agr_agriculture.isChecked()) self.LOGGER.info("HV: %s", self.dlg.rbtn_agr_hv.isChecked()) if self.dlg.cbx_agr_swirLayer.count() == 0 or \ self.dlg.cbx_agr_nnirLayer.count() == 0 or \ self.dlg.cbx_agr_blueLayer.count() == 0: self.LOGGER.info("Layers not found") self.dlg.show_error_message(self.getTranslation("Error"), self.getTranslation("Layers not found")) return self.LOGGER.info("SWIR: %s", self.dlg.cbx_agr_swirLayer.currentText()) self.LOGGER.info("NNIR: %s", self.dlg.cbx_agr_nnirLayer.currentText()) self.LOGGER.info("blue: %s", self.dlg.cbx_agr_blueLayer.currentText()) output_file_name = self.dlg.led_agr_outputFile.text() try: self.validateOutputFilePath(output_file_name) swir_layer = self.getLayerByName(self.dlg.cbx_agr_swirLayer.currentText()) nnir_layer = self.getLayerByName(self.dlg.cbx_agr_nnirLayer.currentText()) blue_layer = self.getLayerByName(self.dlg.cbx_agr_blueLayer.currentText()) swir_band = self.getBandsFromLayer(swir_layer)[self.getCurrentBandName(self.dlg.lstw_agr_swirBands)] nnir_band = self.getBandsFromLayer(nnir_layer)[self.getCurrentBandName(self.dlg.lstw_agr_nnirBands)] blue_band = self.getBandsFromLayer(blue_layer)[self.getCurrentBandName(self.dlg.lstw_agr_blueBands)] except CalculatorException as exp: self.LOGGER.info(exp.message) self.dlg.show_error_message(self.getTranslation(exp.title), self.getTranslation(exp.message)) return if self.dlg.rbtn_agr_agriculture.isChecked(): layer_list = [(swir_layer, swir_band.serial_number), (nnir_layer, nnir_band.serial_number), (blue_layer, blue_band.serial_number)] elif self.dlg.rbtn_agr_hv.isChecked(): layer_list = [(nnir_layer, nnir_band.serial_number), (swir_layer, swir_band.serial_number), (blue_layer, blue_band.serial_number)] else: return self.dlg.enable_load_mode() self.calculation_worker = RasterLayerHandler(output_file_name, layer_list) self.calculation_worker.moveToThread(self.calculation_thread) self.calculation_thread.started.connect(self.calculation_worker.combine_bands) self.calculation_worker.warning.connect(self.showWarning) self.calculation_worker.finished.connect(self.finishAgricultureOrHvCalculation) self.calculation_thread.start() def calculateNdvi(self): """ Start calculating NDVI """ self.LOGGER.info("start NDVI calculation") if self.dlg.cbx_ndvi_redLayer.count() == 0 or \ self.dlg.cbx_ndvi_infraredLayer.count() == 0: self.LOGGER.info("Layers not found") self.dlg.show_error_message(self.getTranslation("Error"), self.getTranslation("Layers not found")) return self.LOGGER.info("red: %s", self.dlg.cbx_ndvi_redLayer.currentText()) self.LOGGER.info("red band number: %s", self.dlg.lstw_ndvi_redBands.currentItem().text()) self.LOGGER.info("IR: %s", self.dlg.cbx_ndvi_infraredLayer.currentText()) self.LOGGER.info("IR band number: %s", self.dlg.lstw_ndvi_infraredBands.currentItem().text) output_file_name = self.dlg.led_ndvi_outputFile.text() try: self.validateOutputFilePath(output_file_name) red_layer_for_calculation = self.getCurrentLayerWithRedBand() infrared_layer_for_calculation = self.getCurrentLayerWithInfraredBand() bands = self.getBandsFromLayer(red_layer_for_calculation) red_band = bands[self.getCurrentBandName(self.dlg.lstw_ndvi_redBands)] bands = self.getBandsFromLayer(infrared_layer_for_calculation) infrared_band = bands[self.getCurrentBandName(self.dlg.lstw_ndvi_infraredBands)] except CalculatorException as exp: self.LOGGER.info(exp.message) self.dlg.show_error_message(self.getTranslation(exp.title), self.getTranslation(exp.message)) return self.dlg.enable_load_mode() self.calculation_worker = NdviCalculator(red_layer_for_calculation, infrared_layer_for_calculation, red_band.serial_number, infrared_band.serial_number, output_file_name) self.calculation_worker.moveToThread(self.calculation_thread) self.calculation_thread.started.connect(self.calculation_worker.run) self.calculation_worker.finished.connect(self.finishCalculationNdvi) self.calculation_thread.start() def getCurrentLayerWithRedBand(self): """ Get user selected layer with red band. :raise CalculatorException: layer with this name not found """ self.LOGGER.debug("getting current a layer with red band") layer_name = self.dlg.cbx_ndvi_redLayer.currentText() return self.getLayerByName(layer_name) def getCurrentLayerWithInfraredBand(self): """ Get user selected layer with infrared band. :raise CalculatorException: layer with this name not found """ self.LOGGER.debug("getting current a layer with IR band") layer_name = self.dlg.cbx_ndvi_infraredLayer.currentText() return self.getLayerByName(layer_name) def getLayerByName(self, layer_name): """ Get layer by name. :param layer_name: layer name :type: unicode :return: QGis layer :type: QgsMapLayer :raise CalculatorException: layer with this name not found """ self.LOGGER.debug("getting a layer by name: %s", layer_name) try: return QgsMapLayerRegistry.instance().mapLayersByName(layer_name)[0] except IndexError: raise CalculatorException("Layer not found", "One of the specified layers was not found") def getCurrentBandName(self, lstw_ndv): """ Get text from QListView with band name :param lstw_ndv: QListView with band name :type: QListView :return: band name :type: unicode """ self.LOGGER.debug("getting current band name from the UI. Band name: %s", lstw_ndv.currentItem().text()) return lstw_ndv.currentItem().text() def validateInputFilePath(self, file_path): """ Checking for the correctness of the path to the file to be opened. :param file_path: file path :type: unicode :raise CalculatorException: the file is not exist """ self.LOGGER.debug("validating input file path: %s", file_path) if not os.path.exists(file_path): self.LOGGER.info("input file - file do not exist") raise CalculatorException("File error", "File does not exist") def validateOutputFilePath(self, file_path): """ Checking for the correctness of the path to the file to be created. :param file_path: file path :type: unicode :raise CalculatorException: the path is not correct """ self.LOGGER.debug("validating output file path: %s", file_path) if not file_path: self.LOGGER.info("output file - file path is None") raise CalculatorException("File error", "File path is empty") file_path_copy = copy.copy(file_path) pattern = re.compile(ur"/(?u)\w+.tif$") try: file_name = pattern.search(file_path_copy).group(0) except AttributeError: self.LOGGER.info("output file - incorrect file name") raise CalculatorException("File error", "Incorrect file name") directory_name = pattern.sub(u"", file_path_copy) if not os.path.isdir(directory_name): self.LOGGER.info("output file - incorrect directory name") raise CalculatorException("File error", "Incorrect directory name") def finishCalculationNdvi(self, output_file_name): """ Completion of NDVI calculation. Unlocking UI and opening file with NDVI. Callback for calculation thread. :param output_file_name: path to file with NDVI :type: unicode """ self.LOGGER.debug("end of NDVI calculation") self.calculation_thread.quit() self.dlg.disable_load_mode() self.openNdviFile(output_file_name) def openNdviFile(self, file_name): """ Open file with NDVI :param file_name: path to file with NDVI :type: unicode """ self.LOGGER.info("opening NDVI file: %s", file_name) try: self.validateInputFilePath(file_name) except CalculatorException as e: self.LOGGER.info(e.message) self.dlg.show_error_message(self.getTranslation(e.title), self.getTranslation(e.message)) return ndvi0_raster_layer = QgsRasterLayer(file_name, "NDVI - <0") layer_data_type = ndvi0_raster_layer.dataProvider().dataType(1) ndvi_thresholds = NdviThreshold().dataTypes.get(layer_data_type) if ndvi_thresholds is None: self.LOGGER.info("NDVI file - unknown data type") self.dlg.show_error_message(self.getTranslation("NDVI file open error"), self.getTranslation("Unknown data type")) ndvi_raster_layer = QgsRasterLayer(file_name, "NDVI") map_layer_registry = QgsMapLayerRegistry.instance() map_layer_registry.addMapLayer(ndvi_raster_layer) return ndvi025_raster_layer = QgsRasterLayer(file_name, "NDVI - 0-0.25") ndvi05_raster_layer = QgsRasterLayer(file_name, "NDVI - 0.25-0.5") ndvi075_raster_layer = QgsRasterLayer(file_name, "NDVI - 0.5-0.75") ndvi1_raster_layer = QgsRasterLayer(file_name, "NDVI - 0.75-1") algorithm = QgsContrastEnhancement.StretchToMinimumMaximum limits = QgsRaster.ContrastEnhancementMinMax ndvi0_raster_layer.setContrastEnhancement(algorithm, limits) ndvi025_raster_layer.setContrastEnhancement(algorithm, limits) ndvi05_raster_layer.setContrastEnhancement(algorithm, limits) ndvi075_raster_layer.setContrastEnhancement(algorithm, limits) ndvi1_raster_layer.setContrastEnhancement(algorithm, limits) colors_scheme = ColorsForNdviMap().getColorScheme(self.dlg.cbx_color_schemes.currentText()) ndvi0_raster_layer.setRenderer( self.getRenderer(ndvi0_raster_layer.dataProvider(), self.getColorMapForNdvi0(colors_scheme, ndvi_thresholds))) ndvi025_raster_layer.setRenderer( self.getRenderer(ndvi025_raster_layer.dataProvider(), self.getColorMapForNdvi025(colors_scheme, ndvi_thresholds))) ndvi05_raster_layer.setRenderer( self.getRenderer(ndvi05_raster_layer.dataProvider(), self.getColorMapForNdvi05(colors_scheme, ndvi_thresholds))) ndvi075_raster_layer.setRenderer( self.getRenderer(ndvi075_raster_layer.dataProvider(), self.getColorMapForNdvi075(colors_scheme, ndvi_thresholds))) ndvi1_raster_layer.setRenderer( self.getRenderer(ndvi1_raster_layer.dataProvider(), self.getColorMapForNdvi1(colors_scheme, ndvi_thresholds))) map_layer_registry = QgsMapLayerRegistry.instance() map_layer_registry.addMapLayer(ndvi0_raster_layer) map_layer_registry.addMapLayer(ndvi025_raster_layer) map_layer_registry.addMapLayer(ndvi05_raster_layer) map_layer_registry.addMapLayer(ndvi075_raster_layer) map_layer_registry.addMapLayer(ndvi1_raster_layer) def getRenderer(self, layer_data_provider, color_map): """ Get QgsSingleBandPseudoColorRenderer for NDVI display. :param layer_data_provider: layer data provider :type: QgsDataProvider :param color_map: color list :type: [ColorRampItem...] :return: QgsSingleBandPseudoColorRenderer """ self.LOGGER.debug("getting renderer") raster_shader = QgsRasterShader() color_ramp_shader = QgsColorRampShader() color_ramp_shader.setColorRampType(QgsColorRampShader.DISCRETE) color_ramp_shader.setColorRampItemList(color_map) raster_shader.setRasterShaderFunction(color_ramp_shader) return QgsSingleBandPseudoColorRenderer(layer_data_provider, 1, raster_shader) def getColorMapForNdvi0(self, color_scheme, ndvi_thresholds): """ Get list of colors for layer with NDVI less than 0 :param color_scheme: color scheme (described in colors_for_ndvi_map.py) :type: {str: QColor} :param ndvi_thresholds: NDVI thresholds for the current data type :type: NdviThreshold.DataType :return: color list :type: [ColorRampItem...] """ self.LOGGER.debug("getting color map for NDVI 0") color_list = [] qri = QgsColorRampShader.ColorRampItem color_list.append(qri(ndvi_thresholds.ndvi0, color_scheme["ndvi_0"], "<0")) color_list.append(qri(ndvi_thresholds.ndvi1, QColor(0, 0, 0, 0), ">0")) return color_list def getColorMapForNdvi025(self, color_scheme, ndvi_thresholds): """ Get a list of colors for a layer with NDVI from 0 to 0.25. :param color_scheme: color scheme (described in colors_for_ndvi_map.py) :type: {str: QColor} :param ndvi_thresholds: NDVI thresholds for the current data type :type: NdviThreshold.DataType :return: color list :type: [ColorRampItem...] """ self.LOGGER.debug("getting color map for NDVI 0.25") color_list = [] qri = QgsColorRampShader.ColorRampItem color_list.append(qri(ndvi_thresholds.ndvi0, QColor(0, 0, 0, 0), "<0")) color_list.append(qri(ndvi_thresholds.ndvi025, color_scheme["ndvi_0.25"], "0-0.25")) color_list.append(qri(ndvi_thresholds.ndvi1, QColor(0, 0, 0, 0), ">0.25")) return color_list def getColorMapForNdvi05(self, color_scheme, ndvi_thresholds): """ Get a list of colors for a layer with NDVI from 0.25 to 0.5. :param color_scheme: color scheme (described in colors_for_ndvi_map.py) :type: {str: QColor} :param ndvi_thresholds: NDVI thresholds for the current data type :type: NdviThreshold.DataType :return: color list :type: [ColorRampItem...] """ self.LOGGER.debug("getting color map for NDVI 0.5") color_list = [] qri = QgsColorRampShader.ColorRampItem color_list.append(qri(ndvi_thresholds.ndvi025, QColor(0, 0, 0, 0), "<0.25")) color_list.append(qri(ndvi_thresholds.ndvi05, color_scheme["ndvi_0.5"], "0.25-0.5")) color_list.append(qri(ndvi_thresholds.ndvi1, QColor(0, 0, 0, 0), ">0.5")) return color_list def getColorMapForNdvi075(self, color_scheme, ndvi_thresholds): """ Get a list of colors for a layer with NDVI from 0.5 to 0.75. :param color_scheme: color scheme (described in colors_for_ndvi_map.py) :type: {str: QColor} :param ndvi_thresholds: NDVI thresholds for the current data type :type: NdviThreshold.DataType :return: color list :type: [ColorRampItem...] """ self.LOGGER.debug("getting color map for NDVI 0.75") color_list = [] qri = QgsColorRampShader.ColorRampItem color_list.append(qri(ndvi_thresholds.ndvi05, QColor(0, 0, 0, 0), "<0.5")) color_list.append(qri(ndvi_thresholds.ndvi075, color_scheme["ndvi_0.75"], "0.5-0.75")) color_list.append(qri(ndvi_thresholds.ndvi1, QColor(0, 0, 0, 0), ">0.75")) return color_list def getColorMapForNdvi1(self, color_scheme, ndvi_thresholds): """ Get a list of colors for a layer with NDVI from 0.75 to 1. :param color_scheme: color scheme (described in colors_for_ndvi_map.py) :type: {str: QColor} :param ndvi_thresholds: NDVI thresholds for the current data type :type: NdviThreshold.DataType :return: color list :type: [ColorRampItem...] """ self.LOGGER.debug("getting color map for NDVI 1") color_list = [] qri = QgsColorRampShader.ColorRampItem color_list.append(qri(ndvi_thresholds.ndvi075, QColor(0, 0, 0, 0), "<0.75")) color_list.append(qri(ndvi_thresholds.ndvi1, color_scheme["ndvi_1"], ">0.75")) return color_list def showWarning(self, message): """ Display warning window. :param message: warning text :type: unicode """ self.LOGGER.debug("showing warning dialog. message: %s", message) self.dlg.show_error_message(self.getTranslation("Warning"), self.getTranslation(message)) def finishAgricultureOrHvCalculation(self, status, message, output_file_name): """ Completion of agriculture or healthy vegetation calculation. Unlocking UI and opening file with result. Callback for calculation thread. :param output_file_name: path to file with agriculture or HV :type: unicode """ self.LOGGER.debug("end of agriculture or HV calculation") self.calculation_thread.quit() self.dlg.disable_load_mode() if status is False: self.dlg.show_error_message(self.getTranslation("Calculation error"), self.getTranslation(message)) else: self.openAgricultureOrHvFile(output_file_name) def openAgricultureOrHvFile(self, input_file_name): """ Open file with agriculture or healthy vegetation. :param input_file_name: path to file with agriculture or healthy vegetation :type: unicode """ self.LOGGER.info("opening agriculture or HV file %s", input_file_name) if self.dlg.rbtn_agr_agriculture.isChecked(): layer_name = "Agriculture" elif self.dlg.rbtn_agr_hv.isChecked(): layer_name = "Healthy_Vegetation" else: return raster_layer = QgsRasterLayer(input_file_name, layer_name) map_layer_registry = QgsMapLayerRegistry.instance() map_layer_registry.addMapLayer(raster_layer)
input_AH_path=self.lqf_path, output_path=self.save_refined_file, lat=self.lat, lon=self.lon, hgt=self.site_height, UTC_offset_h=UTC_offset_h, rainAmongN=self.rainy_hours) thr = QThread(self.dlg) worker.moveToThread(thr) worker.finished.connect(self.refine_worker_finished) worker.error.connect(self.refine_worker_error) worker.update.connect(self.update_progress) thr.started.connect(worker.run) thr.start() self.refine_thread = thr self.refine_worker = worker self.dlg.progressBar.setValue(0) def select_point(self): # Connected to "Seelct Point on Canves" self.canvas.setMapTool(self.pointTool) # Calls a canvas click and create_point self.dlg.setEnabled(False) def create_point(self, point): # Var kommer point ifran??? # report map coordinates from a canvas click self.dlg.setEnabled(True) self.dlg.activateWindow() canvas = self.iface.mapCanvas() srs = canvas.mapSettings().destinationCrs()
class MainWindow(QMainWindow): def __init__(self, width=None, height=None): super(QMainWindow, self).__init__() self.requested_width = width self.requested_height = height if self.requested_width and self.requested_height: self.resize(self.requested_width, self.requested_height) self.setWindowTitle(Constants.APP_TITLE) self.setWindowIcon(QIcon(Constants.intpath(Constants.PNG_ICON))) self.setWindowFlags(Qt.WindowMinimizeButtonHint) self.reset() QMessageBox.information( self, Constants.APP_TITLE, "Cette application vous permet d'exporter les données d'une " "collecte mobile effectuée avec ODK Collect et ODK Aggregate.\n\n" "Commencez par exporter vos données au format JSON depuis " "l'interface web d'ODK Aggregate ({odk_url}) puis utilisez cette " "application pour générer les formulaires d'enquêtes remplis.\n\n" "Ils seront ensuite disponible au format PDF pour impression et " "seront accompagnés de tous les médias (photos) pour transfert " "aux autres département par clé USB.\n\n" "{author} - {email} - {phone}\n{copy} - {date}" .format(author=Constants.AUTHOR, copy=Constants.AUTHOR_COPY, date=Constants.APP_DATE, email=Constants.AUTHOR_EMAIL, phone=Constants.AUTHOR_PHONE, odk_url=Constants.AGGREGATE_URL), QMessageBox.Ok, QMessageBox.NoButton) def change_context(self, context_widget, *args, **kwargs): self.view_widget = context_widget(parent=self, *args, **kwargs) self.setCentralWidget(self.view_widget) def open_dialog(self, dialog, modal=False, opacity=0.98, *args, **kwargs): d = dialog(parent=self, *args, **kwargs) d.setModal(modal) d.setWindowOpacity(opacity) d.exec_() # override def closeEvent(self, event): if self.exporter.is_running: event.ignore() logger.debug("exporter running, cancelling...") self.exporter.cancel() # wait for canceled signal to exit (after warning popup) self.is_exiting = True return super(MainWindow, self).closeEvent(event) def do_close(self): self.is_exiting = False self.close() def reset(self): self.statusbar = StatusBar(self) self.setStatusBar(self.statusbar) self.change_context(HomeWidget) # exporter self.exporter = RamedExporter(main_window=self) self.exporter_thread = QThread() self.exporter.moveToThread(self.exporter_thread) self.exporter.export_ended.connect(self.exporter_thread.quit) self.exporter.export_canceled.connect(self.exporter_thread.quit) self.exporter_thread.started.connect(self.exporter.start) self.is_exiting = False def resizeEvent(self, event): """lancé à chaque redimensionnement de la fenêtre""" # Ajustement en fonction du container self.wc = self.width() self.hc = self.height() @pyqtSlot() def check_started(self): logger.debug("check started") @pyqtSlot(bool, str) def check_ended(self, succeeded, error_message): logger.debug("check ended: {}, {}".format(succeeded, error_message)) if succeeded: self.view_widget.start_export() else: self.view_widget.display_missing_aggregate_confirmation() @pyqtSlot() def parsing_started(self): logger.debug("parsing started") @pyqtSlot(bool, int, str) def parsing_ended(self, succeeded, nb_instances, error_message): logger.debug("parsing ended: {}, {}, {}" .format(succeeded, nb_instances, error_message)) if succeeded: self.exporter_thread.start() @pyqtSlot(str) def export_started(self): logger.debug("export_started") @pyqtSlot(str, int) def exporting_instance(self, ident, index): logger.debug("exporting_instance") self.statusbar.showMessage(ident) @pyqtSlot(bool, int) def instance_completed(self, succeeded, index): logger.debug("instance_completed") @pyqtSlot(int, int, int, int) def export_ended(self, nb_instances_successful, nb_instances_failed, nb_medias_successful, nb_medias_failed): logger.debug("export_ended: {}, {}" .format(nb_instances_successful, nb_instances_failed)) self.statusbar.reset() self.change_context(ConfirmationWidget, nb_instances_successful=nb_instances_successful, nb_instances_failed=nb_instances_failed, nb_medias_successful=nb_medias_successful, nb_medias_failed=nb_medias_failed, from_date=self.view_widget.from_date, to_date=self.view_widget.to_date) @pyqtSlot() def export_canceled(self): self.statusbar.reset() @pyqtSlot(str) def export_error_raised(self, error_message): logger.debug("export_error_raised: {}".format(error_message))
class Photo2ShapeDialog(BASE, WIDGET): def __init__(self, iface, parent=None): super(Photo2ShapeDialog, self).__init__(parent) self.setupUi(self) self.iface = iface self.settings = QSettings('alexbruy', 'photo2shape') self.thread = QThread() self.importer = PhotoImporter() self.btnOk = self.buttonBox.button(QDialogButtonBox.Ok) self.btnClose = self.buttonBox.button(QDialogButtonBox.Close) self.btnSelectInput.clicked.connect(self.selectDirectory) self.btnSelectOutput.clicked.connect(self.selectFile) self.importer.moveToThread(self.thread) self.importer.importError.connect(self.thread.quit) self.importer.importError.connect(self.importCanceled) self.importer.importMessage.connect(self.logMessage) self.importer.importFinished.connect(self.thread.quit) self.importer.importFinished.connect(self.importCompleted) self.importer.photoProcessed.connect(self.updateProgress) self.thread.started.connect(self.importer.importPhotos) self.manageGui() def manageGui(self): self.chkRecurse.setChecked(self.settings.value('recurse', True, bool)) self.chkAppend.setChecked(self.settings.value('append', True, bool)) self.chkLoadLayer.setChecked( self.settings.value('loadLayer', True, bool)) def closeEvent(self, event): self._saveSettings() QDialog.closeEvent(self, event) def selectDirectory(self): lastDir = self.settings.value('lastPhotosDir', '.') dirName = QFileDialog.getExistingDirectory(self, self.tr('Select directory'), lastDir) if dirName == '': return self.lePhotosPath.setText(dirName) self.settings.setValue('lastPhotosDir', os.path.dirname(dirName)) def selectFile(self): lastDir = self.settings.value('lastShapeDir', '.') shpFilter = self.tr('ESRI Shapefiles (*.shp *.SHP)') self.encoding = self.settings.value('encoding', 'System') fileDialog = QgsEncodingFileDialog(self, self.tr('Save file'), lastDir, shpFilter, self.encoding) fileDialog.setDefaultSuffix('shp') fileDialog.setFileMode(QFileDialog.AnyFile) fileDialog.setAcceptMode(QFileDialog.AcceptSave) fileDialog.setConfirmOverwrite(True) if fileDialog.exec_(): fileName = fileDialog.selectedFiles()[0] self.encoding = fileDialog.encoding() self.leOutputShape.setText(fileName) self.settings.setValue( 'lastShapeDir', QFileInfo(fileName).absoluteDir().absolutePath()) self.settings.setValue('encoding', self.encoding) def reject(self): self._saveSettings() QDialog.reject(self) def accept(self): self._saveSettings() dirName = self.lePhotosPath.text() if dirName == '': self.iface.messageBar().pushWarning( self.tr('Path not set'), self.tr('Path to photos is not set. Please specify directory ' 'with photos and try again.')) return fileName = self.leOutputShape.text() if fileName == '': self.iface.messageBar().pushWarning( self.tr('Output file is not set'), self.tr('Output file name is missing. Please specify correct ' 'output file and try again.')) return self.importer.setPhotosDirectory(dirName) self.importer.setOutputPath(fileName) self.importer.setEncoding(self.encoding) self.importer.setRecurseDirs(self.chkRecurse.isChecked()) self.importer.setAppendFile(self.chkAppend.isChecked()) self.thread.start() self.btnOk.setEnabled(False) self.btnClose.setEnabled(False) def updateProgress(self, value): self.progressBar.setValue(value) def logMessage(self, message, level=QgsMessageLog.INFO): QgsMessageLog.logMessage(message, 'Photo2Shape', level) def importCanceled(self, message): self.iface.messageBar().pushWarning(message) self._restoreGui() def importCompleted(self): self.iface.messageBar().pushSuccess( self.tr('Import completed'), self.tr('Shapefile from photos sucessfully created')) if self.chkLoadLayer.isChecked(): self._loadLayer() self._restoreGui() def _loadLayer(self): fName = self.leOutputShape.text() layer = QgsVectorLayer(fName, QFileInfo(fName).baseName(), 'ogr') if layer.isValid(): layer.loadNamedStyle( os.path.join(pluginPath, 'resources', 'photos.qml')) QgsMapLayerRegistry.instance().addMapLayer(layer) else: self.iface.messageBar().pushWarning( self.tr('No output'), self.tr('Cannot load output shapefile')) def _restoreGui(self): self.progressBar.setValue(0) self.btnOk.setEnabled(True) self.btnClose.setEnabled(True) def _saveSettings(self): self.settings.setValue('recurse', self.chkRecurse.isChecked()) self.settings.setValue('append', self.chkAppend.isChecked()) self.settings.setValue('loadLayer', self.chkLoadLayer.isChecked())
def start(self): """Ensure thread is stopped, and start it """ self.stop() self._exit = False QThread.start(self)
class AddonManagerDialog(QDialog): _packages = None def __init__(self, parent=None, **kwargs): super().__init__(parent, acceptDrops=True, **kwargs) self.setLayout(QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.addonwidget = AddonManagerWidget() self.layout().addWidget(self.addonwidget) info_bar = QWidget() info_layout = QHBoxLayout() info_bar.setLayout(info_layout) self.layout().addWidget(info_bar) buttons = QDialogButtonBox(orientation=Qt.Horizontal, standardButtons=QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttons.accepted.connect(self.__accepted) buttons.rejected.connect(self.reject) self.layout().addWidget(buttons) # No system access => install into user site-packages self.user_install = not os.access(sysconfig.get_path("purelib"), os.W_OK) self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) if AddonManagerDialog._packages is None: self._f_pypi_addons = self._executor.submit(list_pypi_addons) else: self._f_pypi_addons = concurrent.futures.Future() self._f_pypi_addons.set_result(AddonManagerDialog._packages) self._f_pypi_addons.add_done_callback( method_queued(self._set_packages, (object, ))) self.__progress = QProgressDialog( self, Qt.Sheet, minimum=0, maximum=0, labelText=self.tr("Retrieving package list"), sizeGripEnabled=False, windowTitle="Progress") self.__progress.rejected.connect(self.reject) self.__thread = None self.__installer = None @Slot(object) def _set_packages(self, f): if self.__progress.isVisible(): self.__progress.close() try: packages = f.result() except (IOError, OSError) as err: message_warning("Could not retrieve package list", title="Error", informative_text=str(err), parent=self) packages = [] except Exception: raise else: AddonManagerDialog._packages = packages installed = list_installed_addons() dists = {dist.project_name: dist for dist in installed} packages = {pkg.name: pkg for pkg in packages} # For every pypi available distribution not listed by # list_installed_addons, check if it is actually already # installed. ws = pkg_resources.WorkingSet() for pkg_name in set(packages.keys()).difference(set(dists.keys())): try: d = ws.find(pkg_resources.Requirement.parse(pkg_name)) except pkg_resources.VersionConflict: pass except ValueError: # Requirements.parse error ? pass else: if d is not None: dists[d.project_name] = d project_names = unique(itertools.chain(packages.keys(), dists.keys())) items = [] for name in project_names: if name in dists and name in packages: item = Installed(packages[name], dists[name]) elif name in dists: item = Installed(None, dists[name]) elif name in packages: item = Available(packages[name]) else: assert False items.append(item) self.addonwidget.set_items(items) def showEvent(self, event): super().showEvent(event) if not self._f_pypi_addons.done(): QTimer.singleShot(0, self.__progress.show) def done(self, retcode): super().done(retcode) self._f_pypi_addons.cancel() self._executor.shutdown(wait=False) if self.__thread is not None: self.__thread.quit() self.__thread.wait(1000) def closeEvent(self, event): super().closeEvent(event) self._f_pypi_addons.cancel() self._executor.shutdown(wait=False) if self.__thread is not None: self.__thread.quit() self.__thread.wait(1000) ADDON_EXTENSIONS = ('.zip', '.whl', '.tar.gz') def dragEnterEvent(self, event): urls = event.mimeData().urls() if any((OSX_NSURL_toLocalFile(url) or url.toLocalFile() ).endswith(self.ADDON_EXTENSIONS) for url in urls): event.acceptProposedAction() def dropEvent(self, event): """Allow dropping add-ons (zip or wheel archives) on this dialog to install them""" packages = [] names = [] for url in event.mimeData().urls(): path = OSX_NSURL_toLocalFile(url) or url.toLocalFile() if path.endswith(self.ADDON_EXTENSIONS): name, vers, summary, descr = (get_meta_from_archive(path) or (os.path.basename(path), '', '', '')) names.append(name) packages.append( Installable(name, vers, summary, descr or summary, path, [path])) future = concurrent.futures.Future() future.set_result((AddonManagerDialog._packages or []) + packages) self._set_packages(future) self.addonwidget.set_install_projects(names) def __accepted(self): steps = self.addonwidget.item_state() if steps: # Move all uninstall steps to the front steps = sorted(steps, key=lambda step: 0 if step[0] == Uninstall else 1) self.__installer = Installer(steps=steps, user_install=self.user_install) self.__thread = QThread(self) self.__thread.start() self.__installer.moveToThread(self.__thread) self.__installer.finished.connect(self.__on_installer_finished) self.__installer.error.connect(self.__on_installer_error) self.__installer.installStatusChanged.connect( self.__progress.setLabelText) self.__progress.show() self.__progress.setLabelText("Installing") self.__installer.start() else: self.accept() def __on_installer_error(self, command, pkg, retcode, output): message_error( "An error occurred while running a subprocess", title="Error", informative_text="{} exited with non zero status.".format(command), details="".join(output), parent=self) self.reject() def __on_installer_finished(self): message = ( ("Changes successfully applied in <i>{}</i>.<br>".format(USER_SITE) if self.user_install else '') + "Please restart Orange for changes to take effect.") message_information(message, parent=self) self.accept()
def init_resync_manager(): global resync_manager if resync_manager is None: resync_manager = QThread() resync_manager.start()
app = QApplication(sys.argv) t = QThread() w = MTWorker() dlg = MTDialog() # ensure no premature exit of the application event loop app.setQuitOnLastWindowClosed(False) # application dependent start of worker, or quit dlg.accepted.connect(w.start) dlg.rejected.connect(t.quit) dlg.rejected.connect(app.quit) # this is boiler plate, clean-up and quit w.finished.connect(w.deleteLater) w.finished.connect(t.quit) w.finished.connect(app.quit) app.aboutToQuit.connect(t.wait) # move worker to thread w.moveToThread(t) # start worker thread (event loop) t.start() # main application thread (event loop) logger.info('starting application') dlg.show() sys.exit(app.exec_())
def startMeasurement(self): sampleRate = self.sampleRateSb.value()*1E3 deviceName = str(self.deviceCombo.currentText()) aoChannel = str(self.aoChannelCombo.currentText()) aiChannel = str(self.aiChannelCombo.currentText()) aiTerminalConfig = self.terminalConfiguration() aiRange = self.aiRanges[self.aiRangeCombo.currentIndex()] aoRange = self.aoRanges[self.aoRangeCombo.currentIndex()] offset = self.offsetSb.value() amplitude = self.amplitudeSb.value() fStart = self.fStartSb.value() fStop = self.fStopSb.value() fSteps = self.fStepsSb.value() settlePeriods = self.settlePeriodsSb.value(); measurePeriods = self.measurePeriodsSb.value() minSettleTime = self.minSettleTimeSb.value(); minMeasureTime = self.minMeasureTimeSb.value() s = QSettings('WiscXrayAstro', application='ADR3RunInfo') path = str(s.value('runPath', '', type=str)) fileName = path+'/TF/%s_%s.h5' % (self.sampleLe.text(), time.strftime('%Y%m%d_%H%M%S')) self._fileName = fileName hdfFile = hdf.File(fileName, mode='w') hdfFile.attrs['Program'] = ApplicationName hdfFile.attrs['Version'] = Version hdfFile.attrs['Sample'] = str(self.sampleLe.text()) hdfFile.attrs['Comment'] = str(self.commentLe.text()) hdfFile.attrs['StartTimeLocal'] = time.strftime('%Y-%m-%d %H:%M:%S') hdfFile.attrs['StartTimeUTC'] = time.strftime('%Y-%m-%d %H:%M:%SZ', time.gmtime()) hdfFile.attrs['sampleRate'] = sampleRate hdfFile.attrs['offset'] = offset hdfFile.attrs['amplitude'] = amplitude hdfFile.attrs['fStart'] = fStart hdfFile.attrs['fStop'] = fStop hdfFile.attrs['fSteps'] = fSteps hdfFile.attrs['settlePeriods'] = settlePeriods hdfFile.attrs['measurePeriods'] = measurePeriods hdfFile.attrs['minSettleTime'] = minSettleTime hdfFile.attrs['minMeasureTime'] = minMeasureTime hdfFile.attrs['deviceName'] = deviceName hdfFile.attrs['aoChannel'] = aoChannel hdfFile.attrs['aoRangeMin'] = aoRange.min; hdfFile.attrs['aoRangeMax'] = aoRange.max hdfFile.attrs['aiChannel'] = aiChannel hdfFile.attrs['aiRangeMin'] = aiRange.min; hdfFile.attrs['aiRangeMax'] = aiRange.max hdfFile.attrs['aiTerminalConfig'] = str(self.aiTerminalConfigCombo.currentText()) if self.auxAoTask is not None: hdfFile.attrs['auxAoChannel'] = str(self.auxAoTask.channels[0]) auxAoRange = self.aoRanges[self.auxAoRangeCombo.currentIndex()] hdfFile.attrs['auxAoRangeMin'] = auxAoRange.min; hdfFile.attrs['auxAoRangeMax'] = auxAoRange.max hdfFile.attrs['auxAoValue'] = self.auxAoSb.value() hkGroup = hdfFile.require_group('HK') self.hkLogger = HkLogger(hkGroup, self.hkSub) # Should remove stuff below soon - only kept for backwards compatibility self.dsTimeStamps = hdfFile.create_dataset('AdrResistance_TimeStamps', (0,), maxshape=(None,), chunks=(500,), dtype=np.float64) self.dsTimeStamps.attrs['units'] = 's' self.dsAdrResistance = hdfFile.create_dataset('AdrResistance', (0,), maxshape=(None,), chunks=(500,), dtype=np.float64) self.dsAdrResistance.attrs['units'] = 'Ohms' self.hdfFile = hdfFile lia = LockIn(); self.lia = lia liaThread = QThread(parent=self); self.liaThread = liaThread lia.moveToThread(liaThread) lia.integrationComplete.connect(self.collectData) # if self.recordDriveCb.isChecked(): # aiDriveChannel = str(self.aiDriveChannelCombo.currentText()) # hdfFile.attrs['aiDriveChannel'] = aiDriveChannel # thread.enableDriveRecording(aiDriveChannel) daqThread = DaqThread(deviceName, aoChannel, aoRange, aiChannel, aiRange, aiTerminalConfig, parent=self); self.daqThread = daqThread daqThread.setSampleRate(sampleRate) daqThread.setParameters(self.fGoals, self.nSettle, self.nMeasure) daqThread.setExcitation(amplitude, offset) self.sweep = Sweep(nPoints = len(self.fGoals), parent=self) if self.enablePlotCb.isChecked(): daqThread.waveformAvailable.connect(self.showWaveform) daqThread.waveformAvailable.connect(lia.integrateData) daqThread.waveformComplete.connect(lia.completeIntegration) daqThread.error.connect(self.reportError) self.enableWidgets(False) daqThread.finished.connect(self.daqThreadFinished) liaThread.finished.connect(self.liaThreadFinished) nCollected = gc.collect() print('GC collected:', nCollected) gc.disable() liaThread.started.connect(lambda: daqThread.start(QThread.HighestPriority)) # Start producer after consumer liaThread.start()
class NGWResourcesModelJob(QObject): started = pyqtSignal() statusChanged = pyqtSignal(unicode) warningOccurred = pyqtSignal(object) errorOccurred = pyqtSignal(object) finished = pyqtSignal() def __init__(self, parent, worker, model_response=None): """Create job. Arguments: job_id -- Job identification worker -- The class object inherits from NGWResourceModelJob """ QObject.__init__(self, parent) self.__result = None self.__worker = worker self.__job_id = self.__worker.id self.__error = None self.__warnings = [] # self.__job_id = "%s_%s" % (self.__worker.id, str(uuid.uuid1())) self.__worker.started.connect(self.started.emit) self.__worker.dataReceived.connect(self.__rememberResult) self.__worker.statusChanged.connect(self.statusChanged.emit) self.__worker.errorOccurred.connect(self.processJobError) self.__worker.warningOccurred.connect(self.processJobWarnings) self.model_response = model_response def setResponseObject(self, resp): self.model_response = resp self.model_response.job_id = self.__job_id def __rememberResult(self, result): self.__result = result def getJobId(self): return self.__job_id def getResult(self): return self.__result def error(self): return self.__error def processJobError(self, job_error): self.__error = job_error self.errorOccurred.emit(job_error) def processJobWarnings(self, job_error): if self.model_response: self.model_response._warnings.append(job_error) # self.warningOccurred.emit(job_error) def start(self): self.__thread = QThread(self) self.__worker.moveToThread(self.__thread) self.__worker.finished.connect(self.finishProcess) self.__thread.started.connect(self.__worker.run) self.__thread.start() def finishProcess(self): self.__worker.started.disconnect() self.__worker.dataReceived.disconnect() self.__worker.statusChanged.disconnect() self.__worker.errorOccurred.disconnect() self.__worker.warningOccurred.disconnect() self.__worker.finished.disconnect() self.__thread.quit() self.__thread.wait() self.finished.emit()
def startMeasurement(self): self.tProduce = {} sampleRate = int(self.sampleRateSb.value()*1E3) inputDecimation = int(str(self.inputDecimationCombo.currentText())) deviceName = str(self.deviceCombo.currentText()) aoChannel = str(self.aoChannelCombo.currentText()) aiChannel = str(self.aiChannelCombo.currentText()) aiTerminalConfig = self.terminalConfiguration() aiRange = self.aiRanges[self.aiRangeCombo.currentIndex()] aoRange = self.aoRanges[self.aoRangeCombo.currentIndex()] offset = self.offsetSb.value() active = self.tableColumnValues('active') activeRows = np.where(active)[0] self.activeRows = activeRows fRefs = self.tableColumnValues('f')[activeRows] As = self.tableColumnValues('A')[activeRows] phases = self.tableColumnValues('phase')[activeRows]*deg2rad bws = self.tableColumnValues('bw')[activeRows] orders = self.tableColumnValues('order')[activeRows] subtractOffset = bool(self.subtractDcOffsetCb.isChecked()) self._fileName = '%s_%s.h5' % (self.sampleLe.text(), time.strftime('%Y%m%d_%H%M%S')) hdfFile = hdf.File(self._fileName, mode='w') hdfFile.attrs['Program'] = ApplicationName hdfFile.attrs['Copyright'] = Copyright hdfFile.attrs['Version'] = Version hdfFile.attrs['Sample'] = str(self.sampleLe.text()) hdfFile.attrs['Comment'] = str(self.commentLe.text()) hdfFile.attrs['StartTimeLocal'] = time.strftime('%Y-%m-%d %H:%M:%S') hdfFile.attrs['StartTimeUTC'] = time.strftime('%Y-%m-%d %H:%M:%SZ', time.gmtime()) hdfFile.attrs['sampleRate'] = sampleRate hdfFile.attrs['inputDecimation'] = inputDecimation hdfFile.attrs['offset'] = offset hdfFile.attrs['deviceName'] = deviceName hdfFile.attrs['aoChannel'] = aoChannel hdfFile.attrs['aoRangeMin'] = aoRange.min; hdfFile.attrs['aoRangeMax'] = aoRange.max hdfFile.attrs['aiChannel'] = aiChannel hdfFile.attrs['aiRangeMin'] = aiRange.min; hdfFile.attrs['aiRangeMax'] = aiRange.max hdfFile.attrs['aiTerminalConfig'] = str(self.aiTerminalConfigCombo.currentText()) hdfFile.attrs['excitationFrequencies'] = fRefs hdfFile.attrs['lockinFilterBandwidths'] = bws hdfFile.attrs['excitationAmplitudes'] = As hdfFile.attrs['excitationPhases'] = phases hdfFile.attrs['lockinFilterOrders'] = orders hdfFile.attrs['rawCount'] = 0 hdfFile.attrs['rampRate'] = self.rampRateSb.value() hdfFile.attrs['subtractDcOffset'] = subtractOffset self.fs = fRefs variables = [('tGenerated', np.float64), ('tAcquired', np.float64), ('Vdc', np.float64), ('Vrms', np.float64), ('Vmin', np.float64), ('Vmax', np.float64), ('offset', np.float64)] self.hdfVector = HdfVectorWriter(hdfFile, variables) g = hdfFile.create_group('HK') self.hkLogger = HkLogger(g, self.hkSub) self.hdfFile = hdfFile sampleRateDecimated = sampleRate/inputDecimation self.__logger.info("Decimated sample rate %f S/s", sampleRateDecimated) desiredChunkSize = int(min(0.5*sampleRateDecimated, 2**18)) # Would like pretty big chunks, but update at least twice/second # Compute phase delays due to the pre-lockin FIR halfband decimator cascade dec = DecimatorCascade(inputDecimation, desiredChunkSize) phaseDelays = TwoPi*fRefs/sampleRate*(dec.sampleDelay()+1.0) self.__logger.info("Phase delays: %s deg", str(phaseDelays*rad2deg)) phaseDelays = np.mod(phaseDelays, TwoPi) dcBw = self.dcBwSb.value(); dcFilterOrder = self.dcFilterOrderSb.value() lias = LockIns(sampleRateDecimated, fRefs, phases-phaseDelays, bws, orders, desiredChunkSize=desiredChunkSize, dcBw=dcBw, dcFilterOrder=dcFilterOrder, subtractOffset=subtractOffset) # Set up the lock-ins self.liaStreamWriters = [] outputSampleRates = lias.outputSampleRates hdfFile.attrs['outputSampleRates'] = outputSampleRates saveRawDemod = bool(self.saveRawDemodCb.isChecked()) hdfFile.attrs['saveRawDemod'] = saveRawDemod streams = [('Z',np.complex64)] if saveRawDemod: streams.append(('Xdec', np.float32)) streams.append(('Ydec', np.float32)) for i,f in enumerate(fRefs): grp = hdfFile.create_group('F_%02d' % i) grp.attrs['fRef'] = f grp.attrs['fs'] = outputSampleRates[i] streamWriter = HdfStreamsWriter(grp, streams, scalarFields=[('tGenerated', np.float64), ('tAcquired', np.float64), ('A', np.float64)], compression=False, parent=self) self.liaStreamWriters.append(streamWriter) grp = hdfFile.create_group('DC') grp.attrs['sampleRate'] = lias.dcSampleRate grp.attrs['lpfBw'] = dcBw grp.attrs['lpfOrder'] = dcFilterOrder streams = [('DC', np.float)] if saveRawDemod: streams.append(('DCdec', np.float)) self.dcStreamWriter = HdfStreamsWriter(grp, streams, scalarFields=[('tGenerated', np.float64), ('tAcquired', np.float64)], compression=False, parent=self) self.lias = lias lias.chunkAnalyzed.connect(self.chunkAnalyzed) chunkSize = lias.chunkSize self.__logger.info("Lias chunk size: %d", chunkSize) self.t = np.linspace(0, chunkSize/sampleRateDecimated, chunkSize) self.wavePlot.setXRange(0,self.t[-1]) daqThread = DaqThread(sampleRate, fRefs, As, phases, chunkSize*inputDecimation, inputDecimation); self.daqThread = daqThread daqThread.setRampRate(self.rampRateSb.value()) daqThread.setOffset(self.offsetSb.value()) self.rampRateSb.valueChanged.connect(daqThread.setRampRate) daqThread.configureDaq(deviceName, aoChannel, aoRange, aiChannel, aiRange, aiTerminalConfig) daqThread.chunkProduced.connect(self.chunkProduced) daqThread.inputOverload.connect(self.overloadLed.flashOnce) daqThread.error.connect(self.reportError) daqThread.finished.connect(self.daqThreadFinished) self.offsetSb.valueChanged.connect(self.daqThread.setOffset) if self.enablePlotCb.isChecked(): daqThread.dataReady.connect(self.showWaveform) if self.saveRawDataCb.isChecked(): self.toggleRawDataCollection(True) self.resultsCollected = 0 self.chunksProduced = None liaThread = QThread(); self.liaThread = liaThread lias.moveToThread(liaThread) daqThread.dataReady.connect(lias.integrateData) lias.resultsAvailable.connect(self.collectLockinResults) liaThread.started.connect(lambda: daqThread.start(QThread.HighestPriority)) liaThread.started.connect(lambda: self.enableWidgets(False)) liaThread.finished.connect(self.liaThreadFinished) liaThread.start() # Start lock-in amplifier(s)
class AbstractBuildRunner(QObject): """ Base class to run a build. Create the required test runner and build manager, along with a thread that should be used for blocking tasks. """ running_state_changed = Signal(bool) worker_created = Signal(object) worker_class = None def __init__(self, mainwindow): QObject.__init__(self) self.mainwindow = mainwindow self.thread = None self.worker = None self.pending_threads = [] self.test_runner = None self.download_manager = None self.options = None self.stopped = False def init_worker(self, fetch_config, options): """ Create and initialize the worker. Should be subclassed to configure the worker, and should return the worker method that should start the work. """ self.options = options # global preferences global_prefs = get_prefs() self.global_prefs = global_prefs # apply the global prefs now apply_prefs(global_prefs) if fetch_config.is_nightly(): fetch_config.set_base_url(global_prefs['archive_base_url']) download_dir = global_prefs['persist'] if not download_dir: download_dir = self.mainwindow.persist persist_limit = int( abs(global_prefs['persist_size_limit']) * 1073741824) self.download_manager = GuiBuildDownloadManager( download_dir, persist_limit) self.test_runner = GuiTestRunner() self.thread = QThread() # options for the app launcher launcher_kwargs = {} for name in ('profile', 'preferences'): if name in options: value = options[name] if value: launcher_kwargs[name] = value # add add-ons paths to the app launcher launcher_kwargs['addons'] = options['addons'] self.test_runner.launcher_kwargs = launcher_kwargs if options['profile_persistence'] in ('clone-first', 'reuse') or options['profile']: launcher_kwargs['cmdargs'] = launcher_kwargs.get( 'cmdargs', []) + ['--allow-downgrade'] self.worker = self.worker_class(fetch_config, self.test_runner, self.download_manager) # Move self.bisector in the thread. This will # allow to the self.bisector slots (connected after the move) # to be automatically called in the thread. self.worker.moveToThread(self.thread) self.worker_created.emit(self.worker) def start(self, fetch_config, options): action = self.init_worker(fetch_config, options) assert callable(action), "%s should be callable" % action self.thread.start() # this will be called in the worker thread. QTimer.singleShot(0, action) self.stopped = False self.running_state_changed.emit(True) @Slot() def stop(self, wait=True): self.stopped = True if self.options: if self.options['profile'] and \ self.options['profile_persistence'] == 'clone-first': self.options['profile'].cleanup() if self.download_manager: self.download_manager.cancel() if self.thread: self.thread.quit() if wait: if self.download_manager: self.download_manager.wait(raise_if_error=False) if self.thread: # wait for thread(s) completion - this is the case when # user close the application self.thread.wait() for thread in self.pending_threads: thread.wait() self.thread = None elif self.thread: # do not block, just keep track of the thread - we got here # when user uses the stop button. self.pending_threads.append(self.thread) self.thread.finished.connect(self._remove_pending_thread) if self.test_runner: self.test_runner.finish(None) self.running_state_changed.emit(False) log('Stopped') @Slot() def _remove_pending_thread(self): for thread in self.pending_threads[:]: if thread.isFinished(): self.pending_threads.remove(thread)
def start(self): from .parameters import instance if instance.use_thread: QThread.start(self) else: self.run()
def __init__(self, font, parent=None): super(MainWindow, self).__init__(parent) self.font = font self.setWindowTitle("Meridien") central_widget = QtGui.QWidget(self) self.setCentralWidget(central_widget) #Center on screen resolution = QtGui.QDesktopWidget().screenGeometry() self.move((resolution.width() / 2) - (self.frameSize().width() / 2), (resolution.height() / 2) - (self.frameSize().height() / 2)) """ Setting up menu bar """ close_action = QtGui.QAction('Close', self) close_action.setShortcut("Ctrl+Q") close_action.setStatusTip('Leave the app') close_action.triggered.connect(lambda: self.close()) open_refinement_folder = QtGui.QAction('Open Refinement Folder', self) open_refinement_folder.triggered.connect(self.open_refinement_folder) self.mainMenu = self.menuBar() self.fileMenu = self.mainMenu.addMenu('&File') self.fileMenu.addAction(open_refinement_folder) self.fileMenu.addAction(close_action) self.refinement_folder = "" create_new_fsc_plot = QtGui.QAction('&New FSC plot', self) create_new_fsc_plot.triggered.connect(self.event_ontriggered_show_fsc_plot) create_new_overview_plot = QtGui.QAction('&New resolution overview plot', self) create_new_overview_plot.triggered.connect(self.event_show_resolution_overview_plot) self.plotMenu = self.mainMenu.addMenu('&Plot') self.plotMenu.addAction(create_new_fsc_plot) self.plotMenu.addAction(create_new_overview_plot) """ Setup other components """ self.layout = QGridLayout(central_widget) self.setMenuBar(self.mainMenu) self.tree = QTreeWidget(self) self.tree.setHeaderHidden(True) self.layout.addWidget(self.tree, 1, 0) self.root_items_path_dictionary = {} # Threads self.threadpool = QThreadPool() self.thread_list = [] thr = QThread(self) thr.start() self.reader = DriverFileReader() self.reader.moveToThread(thr) self.thread_list.append(thr) self.timer = QTimer(self) # Connect signals self.reader.sig_sendfolders.connect(self.fill_tree) self.reader.sig_sendfsc.connect(self.show_dialog_fsc) self.tree.itemChanged.connect(self._event_select_deselect_all) self.sig_update_tree.connect(self.update_tree) self.sig_show_overview_plot.connect(self.event_show_resolution_overview_plot) self.show() self.open_refinement_folder() self.monitor = None
class AddonManagerDialog(QDialog): _packages = None def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.setLayout(QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.addonwidget = AddonManagerWidget() self.layout().addWidget(self.addonwidget) info_bar = QWidget() info_layout = QHBoxLayout() info_bar.setLayout(info_layout) info_icon = QLabel() info_text = QLabel() info_layout.addWidget(info_icon) info_layout.addWidget(info_text) self.layout().addWidget(info_bar) buttons = QDialogButtonBox( orientation=Qt.Horizontal, standardButtons=QDialogButtonBox.Ok | QDialogButtonBox.Cancel ) buttons.accepted.connect(self.__accepted) buttons.rejected.connect(self.reject) self.layout().addWidget(buttons) if not os.access(sysconfig.get_path("purelib"), os.W_OK): if sysconfig.get_platform().startswith("macosx"): info = "You must install Orange by dragging it into" \ " Applications before installing add-ons." else: info = "You do not have permissions to write into Orange " \ "directory.\nYou may need to contact an administrator " \ "for assistance." info_text.setText(info) style = QApplication.instance().style() info_icon.setPixmap(style.standardIcon( QStyle.SP_MessageBoxCritical).pixmap(14, 14)) buttons.button(QDialogButtonBox.Ok ).setEnabled(False) self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) if AddonManagerDialog._packages is None: self._f_pypi_addons = self._executor.submit(list_pypi_addons) else: self._f_pypi_addons = concurrent.futures.Future() self._f_pypi_addons.set_result(AddonManagerDialog._packages) self._f_pypi_addons.add_done_callback( method_queued(self._set_packages, (object,)) ) self.__progress = QProgressDialog( self, Qt.Sheet, minimum=0, maximum=0, labelText=self.tr("Retrieving package list"), sizeGripEnabled=False, windowTitle="Progress" ) self.__progress.rejected.connect(self.reject) self.__thread = None self.__installer = None @Slot(object) def _set_packages(self, f): if self.__progress.isVisible(): self.__progress.close() try: packages = f.result() except (IOError, OSError) as err: message_warning( "Could not retrieve package list", title="Error", informative_text=str(err), parent=self ) packages = [] except Exception: raise else: AddonManagerDialog._packages = packages installed = list_installed_addons() dists = {dist.project_name: dist for dist in installed} packages = {pkg.name: pkg for pkg in packages} # For every pypi available distribution not listed by # list_installed_addons, check if it is actually already # installed. ws = pkg_resources.WorkingSet() for pkg_name in set(packages.keys()).difference(set(dists.keys())): try: d = ws.find(pkg_resources.Requirement.parse(pkg_name)) except pkg_resources.VersionConflict: pass except ValueError: # Requirements.parse error ? pass else: if d is not None: dists[d.project_name] = d project_names = unique( itertools.chain(packages.keys(), dists.keys()) ) items = [] for name in project_names: if name in dists and name in packages: item = Installed(packages[name], dists[name]) elif name in dists: item = Installed(None, dists[name]) elif name in packages: item = Available(packages[name]) else: assert False items.append(item) self.addonwidget.set_items(items) def showEvent(self, event): super().showEvent(event) if not self._f_pypi_addons.done(): QTimer.singleShot(0, self.__progress.show) def done(self, retcode): super().done(retcode) self._f_pypi_addons.cancel() self._executor.shutdown(wait=False) if self.__thread is not None: self.__thread.quit() self.__thread.wait(1000) def closeEvent(self, event): super().closeEvent(event) self._f_pypi_addons.cancel() self._executor.shutdown(wait=False) if self.__thread is not None: self.__thread.quit() self.__thread.wait(1000) def __accepted(self): steps = self.addonwidget.item_state() if steps: # Move all uninstall steps to the front steps = sorted( steps, key=lambda step: 0 if step[0] == Uninstall else 1 ) self.__installer = Installer(steps=steps) self.__thread = QThread(self) self.__thread.start() self.__installer.moveToThread(self.__thread) self.__installer.finished.connect(self.__on_installer_finished) self.__installer.error.connect(self.__on_installer_error) self.__installer.installStatusChanged.connect( self.__progress.setLabelText) self.__progress.show() self.__progress.setLabelText("Installing") self.__installer.start() else: self.accept() def __on_installer_error(self, command, pkg, retcode, output): message_error( "An error occurred while running a subprocess", title="Error", informative_text="{} exited with non zero status.".format(command), details="".join(output), parent=self ) self.reject() def __on_installer_finished(self): message_information( "Please restart Orange for changes to take effect.", parent=self) self.accept()
class MainWindow(QMainWindow): """ MainWindow widget. Connects GUI and worker threads. Inherits from: QMainWindow Buttons: None Signals: None """ def __init__(self, content_gui, content_pipeline, settings_folder, mount_directory, version, parent=None): """ Setup the layout for the widget Arguments: content_gui - Content used to create the GUI outfit. content_pipeline - Content used to start processing threads. settings_folder - Name of the folder containing settings. mount_directory - Name of the folder containing mount points. version - Version of TranSPHIRE. parent - Parent widget (default None) Return: None """ super(MainWindow, self).__init__(parent) # Ask for sudo password if needed self.password = '' need_sudo_password = False for content in content_gui: if content['name'] == 'Status': for entry in content['content']: for widget in entry: for key in widget: if key == 'Mount/umount needs sudo password?': if widget[key][0] == 'True': need_sudo_password = True else: pass else: pass elif content['name'] == 'Mount': for entry in content['content_mount']: for widget in entry: for key in widget: if key == 'Need sudo for copy?': if widget[key][0] == 'True': need_sudo_password = True else: pass else: pass else: pass if need_sudo_password: dialog = SudoPasswordDialog(self) dialog.exec_() if not dialog.result(): QCoreApplication.instance().quit() sys.exit() else: self.password = dialog.password else: pass # Start TranSPHIRE from HOME directory. self.path = os.environ['HOME'] # Window title self.setWindowTitle('{0} v{1} - {2} - {3}'.format( 'TranSPHIRE', version, self.path, os.uname()[1])) # Initiate contents self.central_widget = None self.content = None self.layout = None # Settings folder self.settings_folder = settings_folder self.mount_directory = mount_directory self.temp_save = '{0}/temp_save'.format(settings_folder) # Threads self.mount_worker = None self.process_worker = None self.plot_worker = None self.mount_calculation_ssh = None self.mount_calculation_get = None self.mount_calculation_df = None self.thread_mount = None self.thread_process = None self.thread_plot = None self.mount_thread_list = None # Fill GUI self.reset_gui(content_gui=content_gui, content_pipeline=content_pipeline) def start_threads(self, content_pipeline): """ Start threads used in TranSPHIRE. Arguments: content_pipeline - Content used to start processing threads. Return: None """ # Stop threads if already started. if self.mount_worker is not None: self.mount_worker.setParent(None) if self.process_worker is not None: self.process_worker.setParent(None) if self.plot_worker is not None: self.plot_worker.setParent(None) if self.thread_mount is not None: self.thread_mount.quit() self.thread_mount.wait() self.thread_mount.setParent(None) if self.thread_process is not None: self.thread_process.quit() self.thread_process.wait() self.thread_process.setParent(None) if self.thread_plot is not None: self.thread_plot.quit() self.thread_plot.wait() self.thread_plot.setParent(None) if self.mount_thread_list is not None: for setting in self.content['Mount'].get_settings(): for key in setting: thread = self.mount_thread_list[key]['thread'] calculator = self.mount_thread_list[key]['object'] calculator.kill_thread = True thread.quit() thread.wait() # Create objects used in threads self.mount_worker = MountWorker(password=self.password, settings_folder=self.settings_folder, mount_directory=self.mount_directory) self.process_worker = ProcessWorker( password=self.password, content_process=content_pipeline, mount_directory=self.mount_directory) self.plot_worker = PlotWorker() # Create threads self.thread_mount = QThread(self) self.thread_process = QThread(self) self.thread_plot = QThread(self) # Start threads self.thread_mount.start() self.thread_process.start() self.thread_plot.start() # Start objects in threads self.mount_worker.moveToThread(self.thread_mount) self.process_worker.moveToThread(self.thread_process) self.plot_worker.moveToThread(self.thread_plot) def reset_gui(self, content_gui, content_pipeline, load_file=None): """ Reset the content of the mainwindow. Arguments: content_gui - Content used to fill the GUI. content_pipeline - Content used to start processing threads. load_file - Settings file (default None). Return: None """ # Fill MainWindow self.set_central_widget() self.set_layout_structure() self.start_threads(content_pipeline=content_pipeline) postprocess_content = self.fill_content(content_gui=content_gui) self.postprocess_content(postprocess_content) for content in content_gui: if content['name'] == 'Status': for entry in content['content']: for widget in entry: for key in widget: if key == 'Project name pattern': self.project_name_pattern = widget[key][0] elif key == 'Project name pattern example': self.project_name_pattern_example = widget[ key][0] else: pass else: pass # Load settings saved in load_file if load_file is not None: self.load(file_name=load_file) os.remove('{0}.txt'.format(load_file)) elif os.path.exists('{0}.txt'.format(self.temp_save)): # Result is True if answer is No result = tu.question(head='Restore previous session.', text='Restore previous session?', parent=self) if result: pass else: self.load(file_name=self.temp_save) else: pass self.mount_worker.sig_load_save.emit() def save_temp_settings(self): """ Save the status of the GUI in a temp file. Arguments: None Return: True, if saving was succesful. """ if os.path.exists('{0}.txt'.format(self.temp_save)): os.remove('{0}.txt'.format(self.temp_save)) else: pass value = self.save(file_name=self.temp_save, temp=True) return value def postprocess_content(self, error_list): """ Do postprocessing of creating GUI content, like connecting signals. Arguments: error_list - List of errors that occured. Return: True, if saving was succesful. """ for entry in error_list: tu.message(entry) self.process_worker.sig_finished.connect(self._finished) self.process_worker.sig_plot_ctf.connect( self.plot_worker.calculate_array_ctf) self.process_worker.sig_plot_motion.connect( self.plot_worker.calculate_array_motion) self.plot_worker.sig_message.connect(lambda msg: tu.message(msg)) self.mount_thread_list = {} for key in self.content['Mount'].content: thread = QThread(self) thread.start() mount_calculator = MountCalculator(name=key) mount_calculator.moveToThread(thread) self.mount_thread_list[key] = { 'thread': thread, 'object': mount_calculator } mount_calculator.sig_finished.connect( self.content['Status'].refresh_quota) mount_calculator.sig_finished.connect(self.abort_finished) self.mount_worker.sig_calculate_ssh_quota.connect( mount_calculator.calculate_ssh_quota) self.mount_worker.sig_calculate_df_quota.connect( mount_calculator.calculate_df_quota) self.mount_worker.sig_calculate_get_quota.connect( mount_calculator.calculate_get_quota) self.content['Mount'].set_threadlist( thread_list=self.mount_thread_list) def abort_finished(self, *args, **kwargs): """ Set the mount worker abort variable to True. Arguments: None Return: None """ self.mount_worker.abort_finished = True def set_central_widget(self): """ Reset the central widget of the MainWindow. Arguments: None Return: None """ if self.central_widget is not None: self.central_widget.setParent(None) else: pass self.central_widget = QWidget(self) self.central_widget.setObjectName('central') self.setCentralWidget(self.central_widget) def set_layout_structure(self): """ Setup the layout structure for the central widget. Arguments: None Return: None """ # Layout dictionary self.layout = {} self.layout['h1'] = QHBoxLayout(self.central_widget) self.layout['h2'] = QHBoxLayout() self.layout['h3'] = QHBoxLayout() self.layout['v'] = QVBoxLayout() # Layout architecture self.layout['h1'].addLayout(self.layout['v'], stretch=1) self.layout['v'].addLayout(self.layout['h2'], stretch=0) self.layout['v'].addWidget(Separator(typ='horizontal', color='grey'), stretch=0) self.layout['v'].addLayout(self.layout['h3'], stretch=1) def fill_content(self, content_gui): """ Fill the layouts of the central widget. Arguments: content_gui - Content used to create the GUI outfit. Return: List of errors that occured. """ self.content = {} exclude_set = tu.get_exclude_set(content=content_gui) error_list = [] tab_list = [] for entry in content_gui: key = entry['name'] if key in exclude_set: continue elif entry['layout'] in exclude_set: continue elif key == 'Stretch': layout = entry['layout'] self.layout[layout].addStretch(1) continue elif key == 'Separator': layout = entry['layout'] separator = entry['separator'] self.layout[layout].addWidget(separator) continue elif key == 'Path': tu.reduce_path_widget(exclude_set=exclude_set, content=entry['content']) elif key == 'Copy': tu.reduce_copy_entries(exclude_set=exclude_set, content=entry['content']) else: pass layout = entry['layout'] plot_labels = '' plot_name = '' try: plot_name = layout.replace('Plot ', '') plot_labels = ti.get_dtype_dict()[tu.get_function_dict() [plot_name]['typ']] except KeyError: pass # Create widget self.content[key] = entry['widget']( mount_worker=self.mount_worker, process_worker=self.process_worker, plot_worker=self.plot_worker, settings_folder=self.settings_folder, plot_labels=plot_labels, plot_name=plot_name, parent=self, **entry) if isinstance(self.content[key], TabDocker): tab_list.append(key) else: pass if layout in tab_list: self.content[layout].add_tab(self.content[key], key) else: self.layout[layout].addWidget(self.content[key]) if key == 'Button': self.content[key].sig_load.connect(self.load) self.content[key].sig_save.connect(self.save) self.content[key].sig_start.connect(self.start) self.content[key].sig_stop.connect(self.stop_dialog) self.content[key].sig_check_quota.connect(self.check_quota) else: pass if key == 'Notification': self.content[key].update_telegram() self.content[key].update_email() self.content[key].update() self.content[key].sig_stop.connect(self.stop) timer = QTimer(self) timer.setInterval(20000) timer.timeout.connect(self.content[key].get_telegram_messages) timer.start() else: pass if key == 'Plot per micrograph' or key == 'Plot histogram': self.plot_worker.sig_data.connect( self.content[key].update_figure) else: pass return error_list def check_quota(self): """ Check the quota for the project and scratch directory. Arguments: None Return: None """ global_settings = self.content['General'].get_settings() global_settings = {'General': global_settings[0]} self.mount_worker.sig_set_settings.emit(global_settings) def load(self, file_name=None): """ Load settings from settings file. Arguments: file_name - Name of the file (default None) Return: None """ if file_name is None: file_name = QFileDialog.getOpenFileName( caption='Load settings', directory=self.path, options=QFileDialog.DontUseNativeDialog) if QT_VERSION == 4: file_name = file_name elif QT_VERSION == 5: file_name = file_name[0] else: raise ImportError( 'QT version unknown! Please contact the transphire authors!' ) if not file_name: return else: pass if file_name.endswith('.txt'): pass elif os.path.exists(file_name): pass else: file_name = '{0}.txt'.format(file_name) settings = [] with open(file_name, 'r') as read: for line in read: line = line.replace('\n', '') key, *value = line.split('\t') if isinstance(value, list): settings.append([key, *value]) else: settings.append([key, value]) settings = self.settings_to_dict(settings=settings) self.set_settings(settings=settings) def set_settings(self, settings): """ Load settings from settings file. Arguments: settings - Settings as dictionary. Return: None """ for key in settings: if key == 'End': continue else: try: self.content[key].set_settings(settings[key]) except KeyError: print('Key', key, 'no longer exists') continue @staticmethod def settings_to_dict(settings): """ Make the settings readable for the widgets set settings method. Arguments: settings - Settings as dictionary. Return: None """ settings_dict = {} idx = -1 while idx < len(settings) - 1: idx += 1 if settings[idx][0] == '###': key = settings[idx][1] if key == 'Frames': settings_dict[key] = [] else: settings_dict[key] = {} continue if len(settings[idx]) == 1: settings[idx].append('') if key == 'Frames': setting = {} setting[settings[idx][0]] = settings[idx][1] for i in range(3): idx += 1 setting[settings[idx][0]] = settings[idx][1] settings_dict[key].append(setting) else: if key == 'Notification': settings_dict[key].update({ settings[idx][0]: [settings[idx][1], settings[idx][2]] }) else: settings_dict[key].update( {settings[idx][0]: settings[idx][1]}) return settings_dict def save(self, file_name=None, temp=False): """ Save GUI status to file. Arguments: file_name - File name to save settings to. temp - File is a temporary save file. Return: True, if saving was succesful. """ if file_name is None: file_name = QFileDialog.getSaveFileName( caption='Save settings', directory=self.path, options=QFileDialog.DontUseNativeDialog, filter="Text files (*.txt)") if QT_VERSION == 4: file_name = file_name elif QT_VERSION == 5: file_name = file_name[0] else: raise ImportError( 'QT version unknown! Please contact the transphire authors!' ) if not file_name: return None else: pass else: pass if file_name.endswith('.txt'): pass else: file_name = '{0}.txt'.format(file_name) # Do not override settings if os.path.exists(file_name): old_filename = file_name for number in range(9999): file_name = '{0}_{1}'.format(old_filename, number) if os.path.exists(file_name): continue else: break else: pass error = False with open(file_name, 'w') as write: for key in self.content: if key == 'Mount': continue else: pass try: settings = self.content[key].get_settings() except AttributeError: continue if settings is not None: write.write('###\t{0}\n'.format(key)) for entry in settings: for key_entry in entry: write.write('{0}\t{1}\n'.format( key_entry, entry[key_entry])) else: error = True message = 'Setting of {0} not valid!'.format(key) tu.message(message) continue write.write('###\tEnd\n') write.write('The\tEnd\n') message_pass = '******'.format(file_name) message_error = 'Invalid setting detected! Saveing failed!' if error: os.remove(file_name) tu.message(message_error) print(message_error) return False else: if temp: pass else: tu.message(message_pass) print(message_pass) return True @pyqtSlot() def start(self): """ Start TranSPHIRE processing. Arguments: None Return: None """ self.enable(False) settings = {} # Load settings to pass them to the working threads error_list = [] skip_list = ['Mount', 'Notification', 'Path', 'Frames'] for key in self.content: try: settings_widget = self.content[key].get_settings() except AttributeError: continue else: settings[key] = {} if settings_widget is None: self.enable(True) return None elif key == 'Frames': settings_motion = {} else: pass if key == 'Frames': skip_name_list = [] else: skip_name_list = tu.get_function_dict()[key]['allow_empty'] for entry in settings_widget: if key not in skip_list: for name in entry: if not entry[name] and name not in skip_name_list: error_list.append( '{0}:{1} is not allowed to be emtpy!'.format( key, name)) else: pass else: pass for idx, entry in enumerate(settings_widget): if key == 'Frames': settings_motion[idx] = entry else: settings[key].update(entry) if key == 'Frames': settings['motion_frames'] = settings_motion else: pass if error_list: tu.message('\n'.join(error_list)) self.enable(True) return None else: pass # Get mount information for key in settings['Mount']: device_name = key.replace(' ', '_') save_file = os.path.join(self.settings_folder, device_name) try: with open(save_file, 'r') as read: lines = read.readlines() except FileNotFoundError: continue for line in lines: name = line.split('\t')[0] settings['user_{0}'.format(device_name)] = name settings['user_Later'] = None if not re.match(self.project_name_pattern, settings['General']['Project name']): self.enable(True) tu.message( 'Project name needs to match pattern:\n{0}\n For example: {1}'. format(self.project_name_pattern, self.project_name_pattern_example)) return None else: pass # Project folder names settings['project_folder'] = os.path.join( settings['General']['Project directory'], settings['General']['Project name']) settings['compress_folder'] = os.path.join( settings['General']['Project directory'], settings['General']['Project name']) settings['motion_folder'] = os.path.join( settings['General']['Project directory'], settings['General']['Project name']) settings['ctf_folder'] = os.path.join( settings['General']['Project directory'], settings['General']['Project name']) settings['scratch_folder'] = os.path.join( settings['General']['Scratch directory'], settings['General']['Project name']) settings['Copy_hdd_folder'] = self.mount_directory settings['Copy_backup_folder'] = self.mount_directory settings['Copy_work_folder'] = self.mount_directory settings['settings_folder'] = os.path.join(settings['project_folder'], 'settings') settings['queue_folder'] = os.path.join(settings['project_folder'], 'queue') settings['error_folder'] = os.path.join(settings['project_folder'], 'error') # Check for continue mode if os.path.exists(settings['project_folder']): result = self.continue_dialog( text1='Output project folder already exists!', text2='Do you really want to continue the old run?\nType: YES!' ) if result: result_session = self.continue_dialog( text1='Software metafiles', text2='Software metafiles (Atlas, ...) might be already copied!\n' + \ 'Do you want to copy them again?\nType: YES!' ) settings['Copy_software_meta'] = bool(result_session) else: settings['Copy_software_meta'] = True else: settings['Copy_software_meta'] = True result = True # Create project and settings folder for name in [ 'project_folder', 'settings_folder', 'scratch_folder', 'queue_folder', 'error_folder' ]: try: tu.mkdir_p(settings[name]) except FileNotFoundError: tu.message('Project name cannot be empty') self.enable(True) return None # Start or stop procedure if result: self.content['Button'].start_button.setText('Stop') self.plot_worker.settings = settings self.process_worker.sig_start.emit(settings) self.mount_worker.set_settings(settings=settings) self.save( file_name=os.path.join(settings['settings_folder'], settings['General']['Project name'])) self.save_temp_settings() else: tu.message('Input needs to be "YES!" to work') self.enable(True) def continue_dialog(self, text1, text2): """ Check if the user wants to run the continue mode. Arguments: text1 - Dialog window name. text2 - Text of the dialog. Return: True, if the input is YES! """ dialog = QInputDialog(self) result = dialog.getText(self, text1, text2) return bool(result[0] == 'YES!') @pyqtSlot() def stop_dialog(self): """ Check if the user really wants to stop the process. Arguments: None Return: None """ result = self.continue_dialog( text1='Do you really want to stop?', text2='Do you really want to stop!\nType: YES!') if result: self.stop() else: tu.message('Input needs to be "YES!" to work') @pyqtSlot() def stop(self): """ Stop the process. Arguments: None Return: None """ self.process_worker.stop = True self.content['Button'].start_button.setEnabled(False) @pyqtSlot() def _finished(self): """ Rename the Stop Button to start and enable everything. Arguments: None Return: None """ self.enable(True) self.content['Button'].start_button.setText('Start') self.content['Button'].start_button.setEnabled(True) @pyqtSlot(bool) def enable(self, var, use_all=False): """Enable or disable widgets Arguments: var - Enable status of the widgets. use_all - Disable/Enable everything (Default False) Return: None """ for key in self.content: try: self.content[key].enable(var=var, use_all=use_all) except AttributeError: continue def closeEvent(self, event): """ Quit threads before close and check if the process is still running Arguments: event - QCloseEvent. Return: None """ if self.content['Button'].start_button.text() == 'Stop': event.ignore() tu.message('First stop the program before closing') return None elif not self.save_temp_settings(): result = tu.question( head='Error saving file!', text='Wrong setting detected! Quit without saving?', ) # Result is true if the answer is No if not result: pass else: event.ignore() return None else: pass self.thread_mount.quit() self.thread_mount.wait() self.thread_process.quit() self.thread_process.wait() self.thread_plot.quit() self.thread_plot.wait() for key in self.content['Mount'].content: thread = self.mount_thread_list[key]['thread'] calculator = self.mount_thread_list[key]['object'] calculator.kill_thread = True thread.quit() thread.wait() message = 'Bye Bye' print(message) super(MainWindow, self).closeEvent(event)
def start(self, prio=QThread.InheritPriority): self._shouldStop = False QThread.start(self, prio)
class VNCClient(QObject): started = pyqtSignal() finished = pyqtSignal() imageSizeChanged = pyqtSignal(QSize) imageChanged = pyqtSignal(int, int, int, int) passwordRequested = pyqtSignal(bool) textCut = pyqtSignal(unicode) def __init__(self, host, port, settings, parent=None): super(VNCClient, self).__init__(parent) self.thread = QThread() self.moveToThread(self.thread) self.host = host self.port = port self.settings = settings self.username = None self.password = None self.rfb_client = None self.socket_notifier = None self.thread.started.connect(self._SH_ThreadStarted) self.thread.finished.connect(self._SH_ThreadFinished) def _get_settings(self): return self.__dict__['settings'] def _set_settings(self, settings): old_settings = self.__dict__.get('settings', None) if settings == old_settings: return self.__dict__['settings'] = settings if self.thread.isRunning(): QApplication.postEvent(self, RFBConfigureClientEvent()) settings = property(_get_settings, _set_settings) del _get_settings, _set_settings @property def image(self): return self.rfb_client.image if self.rfb_client is not None else None def start(self): self.thread.start() def stop(self): self.thread.quit() def key_event(self, key, down): if self.thread.isRunning(): QApplication.postEvent(self, RFBKeyEvent(key, down)) def mouse_event(self, x, y, button_mask): if self.thread.isRunning(): QApplication.postEvent(self, RFBMouseEvent(x, y, button_mask)) def cut_text_event(self, text): if text and self.thread.isRunning(): QApplication.postEvent(self, RFBCutTextEvent(text)) def _SH_ThreadStarted(self): self.started.emit() notification_center = NotificationCenter() notification_center.post_notification('VNCClientWillStart', sender=self) self.rfb_client = RFBClient(parent=self) try: self.rfb_client.connect() except RFBClientError: self.thread.quit() else: self.socket_notifier = QSocketNotifier(self.rfb_client.socket, QSocketNotifier.Read, self) self.socket_notifier.activated.connect(self._SH_SocketNotifierActivated) notification_center.post_notification('VNCClientDidStart', sender=self) def _SH_ThreadFinished(self): self.finished.emit() notification_center = NotificationCenter() notification_center.post_notification('VNCClientWillEnd', sender=self) if self.socket_notifier is not None: self.socket_notifier.activated.disconnect(self._SH_SocketNotifierActivated) self.socket_notifier = None self.rfb_client = None notification_center.post_notification('VNCClientDidEnd', sender=self) def _SH_SocketNotifierActivated(self, sock): self.socket_notifier.setEnabled(False) try: self.rfb_client.handle_server_message() except RFBClientError: self.thread.quit() else: self.socket_notifier.setEnabled(True) def _SH_ConfigureRFBClient(self): if self.rfb_client is not None: self.rfb_client.configure() def customEvent(self, event): handler = getattr(self, '_EH_%s' % event.name, Null) handler(event) def _EH_RFBConfigureClientEvent(self, event): if self.rfb_client is not None: self.rfb_client.configure() def _EH_RFBKeyEvent(self, event): if self.rfb_client is not None: self.rfb_client.send_key_event(event.key, event.down) def _EH_RFBMouseEvent(self, event): if self.rfb_client is not None: self.rfb_client.send_pointer_event(event.x, event.y, event.button_mask) def _EH_RFBCutTextEvent(self, event): if self.rfb_client is not None: self.rfb_client.send_client_cut_text(event.text)
class DrainageChannelBuilderDialog(QtGui.QDialog, FORM_CLASS): def __init__(self, iface, parent=None): """Constructor.""" super(DrainageChannelBuilderDialog, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.iface = iface self.setupUi(self) self.resWarning = False self.btnOk = self.buttonBox.button(QtGui.QDialogButtonBox.Ok) self.btnOk.setText(self.tr("Run 2D")) self.btnClose = self.buttonBox.button(QtGui.QDialogButtonBox.Close) self.cbDEM.currentIndexChanged.connect(self.updateRasterRes) self.cbDEM.currentIndexChanged.connect(self.checkLayerExtents) self.cbCL.currentIndexChanged.connect(self.checkLayerExtents) self.spinElevStart.valueChanged.connect(self.calcDepth) self.spinElevEnd.valueChanged.connect(self.calcDepth) self.spinRightSideSlope.valueChanged.connect(self.updateMaxBankWidth) self.spinLeftSideSlope.valueChanged.connect(self.updateMaxBankWidth) self.spinWidth.valueChanged.connect(self.checkRes) self.spinRes.valueChanged.connect(self.checkRes) self.browseBtn.clicked.connect(self.writeDirName) self.btn1Dsave.clicked.connect(self.writeOut1Dresults) # add matplotlib figure to dialog self.figure = Figure() self.axes = self.figure.add_subplot(111) self.figure.subplots_adjust(left=.1, bottom=0.1, right=.95, top=.9, wspace=None, hspace=.2) self.canvas = FigureCanvas(self.figure) self.widgetPlotToolbar = NavigationToolbar(self.canvas, self.widgetPlot) lstActions = self.widgetPlotToolbar.actions() self.widgetPlotToolbar.removeAction(lstActions[7]) self.gPlot.addWidget(self.canvas) self.gPlot.addWidget(self.widgetPlotToolbar) self.figure.patch.set_visible(False) # and configure matplotlib params rcParams["font.serif"] = "Verdana, Arial, Liberation Serif" rcParams["font.sans-serif"] = "Tahoma, Arial, Liberation Sans" rcParams["font.cursive"] = "Courier New, Arial, Liberation Sans" rcParams["font.fantasy"] = "Comic Sans MS, Arial, Liberation Sans" rcParams["font.monospace"] = "Courier New, Liberation Mono" self.manageGui() def manageGui(self): print 'manageGui' self.cbCL.clear() self.cbCL.addItems(utils.getLineLayerNames()) self.cbDEM.clear() self.cbDEM.addItems(utils.getRasterLayerNames()) def refreshPlot(self): self.axes.clear() results1D = utils.getPlotArray(self.vLayer, self.rLayer, self.spinElevStart.value(), self.spinElevEnd.value(), self.xRes) self.stationA = results1D[0, :] self.zExistA = results1D[1, :] self.zPropA = results1D[2, :] self.xA = results1D[3, :] self.yA = results1D[4, :] self.calcCutAvgAreaEndMeth() self.axes.plot(self.stationA, self.zExistA) self.axes.plot(self.stationA, self.zPropA) self.axes.grid() formatter = ScalarFormatter(useOffset=False) self.axes.yaxis.set_major_formatter(formatter) self.axes.set_ylabel(unicode(self.tr("Elevation, z field units"))) self.axes.set_xlabel(unicode(self.tr('Station, layer units'))) self.axes.set_title( unicode( self.tr('Channel {} Profile and 1D Calculation Results'.format( self.vLayer.name())))) at = AnchoredText( self.outText, prop=dict(size=12), frameon=True, loc=1, ) at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") self.axes.add_artist(at) self.canvas.draw() def refreshPlotText(self): at = AnchoredText( self.outText, prop=dict(size=12), frameon=True, loc=1, ) at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") self.axes.add_artist(at) self.canvas.draw() def calcCutAvgAreaEndMeth(self): self.depth1D = self.zExistA - self.zPropA # find max bank width based on CL depth self.maxCLdepth = np.max(self.depth1D) leftSlope = self.spinLeftSideSlope.value() rightSlope = self.spinRightSideSlope.value() width = self.spinWidth.value() self.area = self.depth1D * (width + self.depth1D * leftSlope / 2.0 + self.depth1D * rightSlope / 2.0) self.length = self.stationA[1:] - self.stationA[:-1] self.vol1D = (self.area[1:] + self.area[:-1]) * self.length / 2.0 self.vol1D = np.append(0, self.vol1D) self.totVol1D = np.sum(self.vol1D) self.maxDepth1D = np.max(self.depth1D) self.totLength = np.max(self.stationA) self.channelSlope = (self.zPropA[-1] - self.zPropA[0]) / self.totLength self.outText = 'Length: {2:.2f} layer units\nChannel Slope: {3:.6f}\n1D Max. Cut Depth: {0:,.2f} layer units\n1D Tot. Vol.: {1:,.2f} layer units$^3$'.format( self.maxDepth1D, self.totVol1D, self.totLength, self.channelSlope) #self.canvas.draw() def writeOut1Dresults(self): # assign results to numpy array for quick csv dump self.out1Dresults = np.zeros((self.stationA.size, 8)) self.out1Dresults[:, 0] = self.stationA self.out1Dresults[:, 1] = self.vol1D self.out1Dresults[:, 2] = self.zExistA self.out1Dresults[:, 3] = self.zPropA self.out1Dresults[:, 4] = self.depth1D self.out1Dresults[:, 5] = self.area self.out1Dresults[:, 6] = self.xA self.out1Dresults[:, 7] = self.yA outPath = self.outputDir.text() home = os.path.expanduser("~") if outPath == '': outPath = os.path.join(home, 'Desktop', 'QGIS2DrainageChannelBuilderFiles') self.outputDir.setText(outPath) if not os.path.exists(outPath): os.makedirs(outPath) os.chdir(outPath) fileName = 'Channel1Dresults_{}.txt'.format(self.vLayer.name()) outHeader = 'DEM Layer:\t{0}\nChannel Centerline:\t{1}\nProjection (Proj4 format):\t{13}\nChannel Bottom Width:\t{3}\nChannel Start Elevation:\t{4}\nChannel End Elevation:\t{5}\nChannel Slope:\t{12:.06f}\nLeft Side Slope:\t{6}\nRight Side Slope:\t{7}\nLength:\t{9:,.2f}\n1D Max. Cut Depth:\t{10:,.2f}\n1D Tot. Vol:\t{11:,.2f}\nNote:\tAll units of length correspond to layer units, all units of area and volume are a combination of layer units and raster elevation units\n\nstation\tvol\tzExist\tzProp\tdepth\tcutArea\tx\ty\n'.format( self.cbDEM.currentText(), self.cbCL.currentText(), self.spinRes.value(), self.spinWidth.value(), self.spinElevStart.value(), self.spinElevEnd.value(), self.spinLeftSideSlope.value(), self.spinRightSideSlope.value(), self.spinBankWidth.value(), self.totLength, self.maxDepth1D, self.totVol1D, self.channelSlope, self.rLayer.crs().toProj4()) np.savetxt(fileName, self.out1Dresults, fmt='%.3f', header=outHeader, delimiter='\t') self.canvas.print_figure('Channel1DresultsFigure_{}'.format( self.vLayer.name())) def updateMaxBankWidth(self): self.maxBankWidth = self.maxCLdepth * np.max( np.array([ self.spinLeftSideSlope.value(), self.spinRightSideSlope.value() ])) self.spinBankWidth.setValue(self.maxBankWidth + self.spinRes.value() / 2.) self.calcCutAvgAreaEndMeth() self.refreshPlotText() def writeDirName(self): self.outputDir.clear() self.dirName = QtGui.QFileDialog.getExistingDirectory( self, 'Select Output Directory') self.outputDir.setText(self.dirName) self.checkBoxLoadLayers.setCheckState(True) def updateRasterRes(self): layer = utils.getRasterLayerByName( self.cbDEM.currentText().split(' EPSG')[0]) if layer.isValid(): self.xRes = layer.rasterUnitsPerPixelX() self.yRes = layer.rasterUnitsPerPixelY() self.labelDEMres.setText( 'DEM Layer Resolution = {:.2f} X {:.2f} Y'.format( self.xRes, self.yRes)) def checkRes(self): if self.spinWidth.value() <= self.spinRes.value() * 10: self.resWarning = True self.errOutput() else: self.resWarning = False self.errOutput() self.calcCutAvgAreaEndMeth() self.refreshPlotText() def checkLayerExtents(self): self.rLayer = utils.getRasterLayerByName( self.cbDEM.currentText().split(' EPSG')[0]) self.vLayer = utils.getVectorLayerByName( self.cbCL.currentText().split(' EPSG')[0]) try: if self.rLayer.isValid() and self.vLayer.isValid(): if self.rLayer.extent().xMaximum() >= self.vLayer.extent( ).xMaximum() and self.rLayer.extent().xMinimum( ) <= self.vLayer.extent().xMinimum() and self.rLayer.extent( ).yMaximum() >= self.vLayer.extent().yMaximum( ) and self.rLayer.extent().yMinimum() <= self.vLayer.extent( ).yMinimum(): self.layersOverlap = True self.calcElev() self.btn1Dsave.setEnabled(True) self.btnOk.setEnabled(True) self.errOutput() else: self.btnOk.setEnabled(False) self.spinElevStart.setEnabled(False) self.spinElevEnd.setEnabled(False) self.layersOverlap = False self.errOutput() except: self.btnOk.setEnabled(False) self.spinElevStart.setEnabled(False) self.spinElevEnd.setEnabled(False) self.btn1Dsave.setEnabled(False) self.overlayError = True self.layersOverlap = False self.errOutput() pass def calcDepth(self): if self.layersOverlap: self.layersOverlap = utils.calcDepth(self) if self.layersOverlap: self.refreshPlot() def calcElev(self): if self.layersOverlap: self.spinElevStart.setEnabled(True) self.spinElevEnd.setEnabled(True) self.spinElevStart.setValue(utils.calcElev(self)[0]) self.spinElevEnd.setValue(utils.calcElev(self)[1]) self.refreshPlot() def updateProgressText(self, message): self.labelProgress.setText(message) def updateOutputText(self, message): self.textBrowser.setPlainText(message) def workerError(self, e, exception_string): print 'workerError\n{}\n'.format(exception_string) QgsMessageLog.logMessage( 'Worker thread raised an exception:\n{}'.format(exception_string), level=QgsMessageLog.CRITICAL) self.iface.messageBar().pushMessage( "Drainge Channel Builder", 'It didnt work, see log for details', level=QgsMessageBar.CRITICAL, duration=3) self.progressBar.setMaximum(100) self.btnOk.setEnabled(True) self.btn1Dsave.setEnabled(True) def stopWorker(self): self.worker.kill() if self.worker is not None: self.worker.deleteLater() self.thread.quit() self.thread.wait() self.thread.deleteLater() def errOutput(self): if self.resWarning and not self.layersOverlap: self.labelErrMessage.setText( 'Error: Vector is not completely within raster domain\nWarning: For best 2D results, channel bottom width should be at least ten times greater than 2D grid calculation resolution' ) elif self.resWarning: self.labelErrMessage.setText( 'Warning: For best 2D results, channel bottom width should be at least ten times greater than 2D grid calculation resolution' ) elif not self.layersOverlap: self.labelErrMessage.setText( 'Error: Vector is not completely within raster domain') else: self.labelErrMessage.setText('') def workerFinished(self, values): # print 'values = ',values self.stopWorker() self.values = values maxCut = values[0][0] avgCut = values[0][1] totVol = values[0][2] self.dirName = values[0][3] self.outputDir.setText(self.dirName) demChannelPath = values[0][4] demCutDepth = values[0][5] length = values[0][6] outText = 'Summary of 2D Results:\nTot. Vol.\t{2:,.2f}\nLength\t{3:,.2f}\nMax. Cut Depth\t{0:.2f}\nAvg. Cut Depth\t{1:.2f}'.format( maxCut, avgCut, totVol, length) self.updateOutputText(outText) # for layer in self.layers: # utils.addSlLayer(self.iface,self.dbase,layer) self.iface.messageBar().pushMessage( "Drainge Channel Builder", 'Channel elevation DEM, channel depth of cut DEM, and channel grid points located at {}. Please delete when finished' .format(self.dirName), duration=30) # set channel dem qml by coping dem layer style to channel dem qml self.rLayer.saveNamedStyle( os.path.join(self.dirName, demChannelPath.split('.tif')[0] + '.qml')) # set depth qml w/ function xmlString = utils.depthQMLwriter(maxCut) demCutDepthQML = open(demCutDepth.split('.tif')[0] + '.qml', 'w') demCutDepthQML.write(xmlString) demCutDepthQML.close() if self.checkBoxLoadLayers.checkState(): for fileName in [demChannelPath, demCutDepth]: fileInfo = QFileInfo(fileName) baseName = fileInfo.baseName() layer = QgsRasterLayer(fileName, baseName) if not layer.isValid(): print "Layer failed to load!" QgsMapLayerRegistry.instance().addMapLayer(layer) self.progressBar.setMaximum(100) self.cbCL.setEnabled(True) self.cbDEM.setEnabled(True) self.spinRes.setEnabled(True) self.spinWidth.setEnabled(True) self.spinElevStart.setEnabled(True) self.spinElevEnd.setEnabled(True) self.spinLeftSideSlope.setEnabled(True) self.spinRightSideSlope.setEnabled(True) self.spinBankWidth.setEnabled(True) self.browseBtn.setEnabled(True) self.btn1Dsave.setEnabled(True) self.btnClose.setEnabled(True) self.btnOk.setEnabled(True) self.writeOut1Dresults() fileName = 'Channel2DresultsSummary_{}.txt'.format(self.vLayer.name()) outText = 'DEM Layer:\t{0}\nChannel Centerline:\t{1}\nProjection (Proj4 format):\t{14}\n2D Grid Calc Res.:\t{2}\nChannel Bottom Width:\t{3}\nChannel Start Elevation:\t{4}\nChannel End Elevation:\t{5}\nChannel Slope:\t{13:.06f}\nLeft Side Slope:\t{6}\nRight Side Slope:\t{7}\n2D Maximum Bank Width:\t{8}\n\nLength:\t{9:,.2f}\n2D Max. Cut Depth:\t{10:,.2f}\n2D Avg. Cut Depth:\t{11:,.2f}\n2D Tot. Vol:\t{12:,.2f}\n\nNote:\tAll units of length correspond to layer units, all units of area and volume are a combination of layer units and raster elevation units\n\n'.format( self.cbDEM.currentText(), self.cbCL.currentText(), self.spinRes.value(), self.spinWidth.value(), self.spinElevStart.value(), self.spinElevEnd.value(), self.spinLeftSideSlope.value(), self.spinRightSideSlope.value(), self.spinBankWidth.value(), self.totLength, maxCut, avgCut, totVol, self.channelSlope, self.rLayer.crs().toProj4()) outFile = open(fileName, 'w') outFile.write(outText) outFile.close() self.checkBoxLoadLayers.setCheckState(False) self.iface.mapCanvas().refresh() def accept(self): print 'accepted' args = [ self.rLayer, self.vLayer, self.spinWidth.value(), self.spinRes.value(), self.spinElevStart.value(), self.spinElevEnd.value(), self.spinLeftSideSlope.value(), self.spinRightSideSlope.value(), self.spinBankWidth.value(), self.outputDir.text() ] self.thread = QThread() # create a new worker instance self.worker = DrainageChannelThread.DrainageChannelBuilder(args) # create a new worker instance self.worker.moveToThread(self.thread) self.worker.updateProgressText.connect(self.updateProgressText) self.worker.workerFinished.connect(self.workerFinished) self.worker.error.connect(self.workerError) self.btnClose.setEnabled(False) self.cbCL.setEnabled(False) self.cbDEM.setEnabled(False) self.spinRes.setEnabled(False) self.spinWidth.setEnabled(False) self.spinElevStart.setEnabled(False) self.spinElevEnd.setEnabled(False) self.spinLeftSideSlope.setEnabled(False) self.spinRightSideSlope.setEnabled(False) self.spinBankWidth.setEnabled(False) self.browseBtn.setEnabled(False) self.btn1Dsave.setEnabled(False) self.btnOk.setEnabled(False) self.tabWidgetOutput.setCurrentWidget(self.tabOutputSummary) #self.buttonBox.rejected.disconnect(self.reject) self.progressBar.setMaximum(0) self.thread.started.connect(self.worker.run) self.thread.start() def reject(self): print 'rejected' QtGui.QDialog.reject(self)
class GCode(object): def __init__(self, filename, controller, done_loading_callback, done_writing_callback): self.controller = controller self.done_loading_callback = done_loading_callback self.writing_done_callback = done_writing_callback self.data = defaultdict(list) self.all_data = [] self.data_keys = set() self.color_change_data = [] self.actual_z = '0.0' self.speed = 0.0 self.z_hop = False self.last_point = np.array([0.0, 0.0, 0.0]) self.actual_point = [0.0, 0.0, 0.0] self.printing_time = 0.0 self.filament_length = 0.0 #print("Filename type: " + str(type(filename))) #print("Filename: " + filename) #if type(filename)==: #self.filename = u'c:\\models\\super mega testovací Jindřich šložka čěýáéůú\\anubis_PLA_OPTIMAL.gcode' self.filename = filename self.is_loaded = False self.gcode_parser = GcodeParserRunner(controller, self.filename) self.gcode_parser_thread = QThread() self.gcode_copy = GcodeCopyRunner( self.filename, "", color_change_lst=self.color_change_data) self.gcode_copy_thread = QThread() def cancel_parsing_gcode(self): print("Cancel presset") if self.gcode_parser and self.gcode_parser_thread and self.gcode_parser_thread.isRunning( ): self.gcode_parser.is_running = False self.gcode_parser_thread.quit() self.gcode_parser_thread.wait() self.is_loaded = False self.data = {} self.all_data = [] self.data_keys = [] self.controller.set_progress_bar(0) def cancel_writing_gcode(self): print("Cancel writing gcode") if self.gcode_copy and self.gcode_copy_thread and self.gcode_copy_thread.isRunning( ): self.gcode_copy.quit() self.gcode_copy_thread.wait() def get_first_extruding_line_number_of_gcode_for_layers( self, layers_keys_lst): lines_number = [] for i in layers_keys_lst: line = self.data[i] for o in line: _a, _b, type, _speed, _extr, line_n = o if 'E' in type: lines_number.append(line_n) break return lines_number def read_in_thread(self, update_progressbar_function, after_done_function): print("reading in thread") self.gcode_parser.moveToThread(self.gcode_parser_thread) self.done_loading_callback = after_done_function # connect all signals to thread class self.gcode_parser_thread.started.connect( self.gcode_parser.load_gcode_file) # connect all signals to parser class self.gcode_parser.finished.connect(self.set_finished_read) self.gcode_parser.update_progressbar = True self.gcode_parser.set_update_progress.connect( update_progressbar_function) self.gcode_parser.set_data_keys.connect(self.set_data_keys) self.gcode_parser.set_data.connect(self.set_data) self.gcode_parser.set_all_data.connect(self.set_all_data) self.gcode_parser.set_printing_time.connect(self.set_printig_time) self.gcode_parser_thread.start() def read_in_realtime(self): print("Read in realtime") self.gcode_parser.set_data_keys.connect(self.set_data_keys) self.gcode_parser.set_data.connect(self.set_data) self.gcode_parser.set_all_data.connect(self.set_all_data) self.gcode_parser.set_printing_time.connect(self.set_printig_time) self.gcode_parser.update_progressbar = False print("start read procedure") self.gcode_parser.load_gcode_file() self.is_loaded = True def set_printig_time(self, time): self.printing_time = time def set_data_keys(self, data_keys): self.data_keys = data_keys def set_all_data(self, all_data): self.all_data = all_data def set_data(self, data): self.data = data def set_finished_read(self): self.gcode_parser_thread.quit() self.is_loaded = True self.done_loading_callback() #self.controller.set_gcode() def set_finished_copy(self): self.gcode_copy_thread.quit() #print(str(self.writing_done_callback)) self.writing_done_callback() def set_color_change_data(self, data): self.color_change_data = data def write_with_changes_in_thread(self, filename_in, filename_out, update_function): self.gcode_copy.filename_in = filename_in self.gcode_copy.filename_out = filename_out self.gcode_copy.color_change_lst = self.color_change_data self.gcode_copy.moveToThread(self.gcode_copy_thread) self.gcode_copy_thread.started.connect(self.gcode_copy.write_file) self.gcode_copy.finished.connect(self.set_finished_copy) self.gcode_copy.set_update_progress.connect(update_function) self.gcode_copy_thread.start()
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 GeoFollow: def __init__(self, iface): self.iface = iface self.connectAction = None self.disconnectAction = None self.menu = u'GeoFollow' self.toolbar = self.iface.addToolBar(u'GeoFollow') self.toolbar.setObjectName(u'GeoFollow') self.dlg = None self.thread = None self.worker = None def initGui(self): self.connectAction = QAction(QIcon(':/plugins/GeoFollow/icon_connect.png'), 'Connect', self.iface.mainWindow()) self.connectAction.triggered.connect(self.run) self.connectAction.setEnabled(True) self.toolbar.addAction(self.connectAction) self.iface.addPluginToMenu(self.menu, self.connectAction) self.disconnectAction = QAction(QIcon(':/plugins/GeoFollow/icon_disconnect.png'), 'Disconnect', self.iface.mainWindow()) self.disconnectAction.triggered.connect(self.stop) self.disconnectAction.setEnabled(False) self.toolbar.addAction(self.disconnectAction) self.iface.addPluginToMenu(self.menu, self.disconnectAction) def unload(self): self.stop() self.iface.removePluginMenu(u'GeoFollow', self.connectAction) self.iface.removeToolBarIcon(self.connectAction) self.iface.removePluginMenu(u'GeoFollow', self.disconnectAction) self.iface.removeToolBarIcon(self.disconnectAction) del self.toolbar def run(self): s = QSettings() self.dlg = GeoFollowDialog() self.dlg.hostLineEdit.setText(s.value("geofollow/host", "localhost")) self.dlg.portLineEdit.setText(s.value("geofollow/port", "13729")) self.dlg.trackerLineEdit.setText(s.value("geofollow/tracker", "web")) self.dlg.show() result = self.dlg.exec_() if result: s.setValue("geofollow/host", self.dlg.hostLineEdit.text()) s.setValue("geofollow/port", self.dlg.portLineEdit.text()) s.setValue("geofollow/tracker", self.dlg.trackerLineEdit.text()) self.start(self.dlg.hostLineEdit.text(), self.dlg.portLineEdit.text(), self.dlg.trackerLineEdit.text()) pass def info(self, msg): self.iface.messageBar().pushMessage("GeoFollow", msg, level=QgsMessageBar.INFO, duration=1) def error(self, exception): if (type(exception) == socket.gaierror) or (type(exception) == socket.error): self.iface.messageBar().pushMessage("GeoFollow", str(exception), level=QgsMessageBar.CRITICAL, duration=0) QgsMessageLog.logMessage(str(exception), 'GeoFollow', QgsMessageLog.CRITICAL) self.stop() else: self.stop() raise exception def update(self, data): canvas = self.iface.mapCanvas() repro = QgsCoordinateTransform( QgsCoordinateReferenceSystem(data['c']), canvas.mapSettings().destinationCrs() ) pos = data["b"].split(",") s_sw = QgsPoint(float(pos[0]), float(pos[1])) s_ne = QgsPoint(float(pos[2]), float(pos[3])) canvas.setExtent(QgsRectangle(repro.transform(s_sw), repro.transform(s_ne))) #cachingEnabled = self.iface.mapCanvas().isCachingEnabled() #for layer in self.iface.mapCanvas().layers(): # if cachingEnabled: # layer.setCacheImage(None) # layer.triggerRepaint() canvas.refresh() #self.iface.mapCanvas().refreshAllLayers() #qApp.processEvents def start(self, host, port, tracker): self.connectAction.setEnabled(False) self.thread = QThread() self.worker = Worker(host, port, tracker) self.worker.moveToThread(self.thread) self.worker.update.connect(self.update) self.worker.info.connect(self.info) self.worker.error.connect(self.error) self.thread.started.connect(self.worker.loop) self.worker.finished.connect(self.thread.quit) self.worker.finished.connect(self.reset) self.worker.finished.connect(self.worker.deleteLater) self.thread.finished.connect(self.thread.deleteLater) self.thread.start() self.disconnectAction.setEnabled(True) def reset(self): self.connectAction.setEnabled(True) def stop(self): self.disconnectAction.setEnabled(False) if self.worker: self.worker.kill()
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 ProjectUpdater(QObject): """ Object to handle reporting when new versions of projects are found on the update server. Emits foundProjects when a new projects are found """ foundProjects = pyqtSignal(list, list) projectUpdateStatus = pyqtSignal(str, str) projectInstalled = pyqtSignal(str) def __init__(self, server=None, projects_base=''): super(ProjectUpdater, self).__init__() self.updatethread = None self.server = server self.net = QgsNetworkAccessManager.instance() self.projects_base = projects_base self.create_worker() def quit(self): self.updatethread.quit() def create_worker(self): self.updatethread = QThread() self.worker = UpdateWorker(self.projects_base) self.worker.moveToThread(self.updatethread) self.worker.projectUpdateStatus.connect(self.projectUpdateStatus) self.worker.projectInstalled.connect(self.projectInstalled) self.updatethread.started.connect(self.worker.run) @property def configurl(self): url = urlparse.urljoin(add_slash(self.server), "projects/roam.txt") print "URL", url return url def check_updates(self, server, installedprojects): if not server: return self.server = server req = QNetworkRequest(QUrl(self.configurl)) reply = self.net.get(req) reply.finished.connect( functools.partial(self.list_versions, reply, installedprojects)) def update_server(self, newserverurl, installedprojects): self.check_updates(newserverurl, installedprojects) def list_versions(self, reply, installedprojects): if reply.error() == QNetworkReply.NoError: content = reply.readAll().data() serverversions = parse_serverprojects(content) updateable = list( updateable_projects(installedprojects, serverversions)) new = list(new_projects(installedprojects, serverversions)) if updateable or new: self.foundProjects.emit(updateable, new) else: msg = "Error in network request for projects: {}".format( reply.errorString()) roam.utils.warning(msg) # RoamEvents.raisemessage("Project Server Message", msg, level=RoamEvents.WARNING) def update_project(self, project, version): self.projectUpdateStatus.emit(project.name, "Pending") forupdate.put((project, version, self.server, False)) self.updatethread.start() def install_project(self, projectinfo): self.projectUpdateStatus.emit(projectinfo['name'], "Pending") is_new = True forupdate.put( (projectinfo, projectinfo['version'], self.server, is_new)) self.updatethread.start()