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 MQTTClient(QtCore.QObject): """ Wrapper class for Mosquitto MQTT client Provides inherited helper classes for SingleShot and Test requests Initial approach was to sub class QThread, but reading revealed that running the timers within a thread was the preferred approach. """ kMaxAttempts = 3 kMinKeepAlive = 5 kResetTimer = 60 mqttOnConnect = pyqtSignal(QObject, QObject, int) mqttOnDisConnect = pyqtSignal(QObject, QObject, int) mqttOnMessage = pyqtSignal(QObject, QObject, QObject) mqttOnPublish = pyqtSignal(QObject, QObject, int) mqttOnSubscribe = pyqtSignal(QObject, QObject, int, int) mqttOnLog = pyqtSignal(str, int) mqttOnTimeout = pyqtSignal(QObject) mqttConnectionError = pyqtSignal(QObject, str) # Hmmm new style signals cause problems with multiple parameters # mqttConnectionError = QtCore.pyqtSignal([str]) # Add username/password def __init__(self, creator, clientId, broker, cleanSession=True): super(MQTTClient, self).__init__() # Load settings self._creator = creator # create client id self._cleanSession = cleanSession self._resetTimer = QTimer() self._resetTimer.setSingleShot(True) self._resetTimer.timeout.connect(self._reset) self._killTimer = QTimer() self._killTimer.setSingleShot(True) self._killTimer.timeout.connect(self._kill) self._killing = False self._loopTimer = QTimer() self._loopTimer.setSingleShot(False) self._loopTimer.timeout.connect(self._loop) self._clientId = clientId self._host = broker.host() self._port = int(broker.port()) self._poll = int(broker.poll()) self.setKeepAlive(broker.keepAlive()) self._attempts = 0 self._attempted = 0 self._connected = False self._thread = QThread(self) self._thread.started.connect(lambda: self._loopTimer.start(self._poll)) self._thread.finished.connect(self._loopTimer.stop) self._thread.started.connect(lambda: Log.debug("Thread started")) self._thread.finished.connect(lambda: Log.debug("Thread stopped")) self._thread.terminated.connect(lambda: Log.debug("Thread terminated")) self._restarting = False self._subscribed = [] # self.mqttc = mqtt.Client(self._clientId, self._cleanSession) self.mqttc = mqtt.Mosquitto(self._clientId, self._cleanSession) if broker.username(): # Basic Auth! self.mqttc.username_pw_set(broker.username(), broker.password()) self.mqttc.on_connect = self.on_connect self.mqttc.on_disconnect = self.on_disconnect self.mqttc.on_message = self.onMessage self.mqttc.on_publish = self.onPublish self.mqttc.on_subscribe = self.onSubscribe # self.mqttc.on_unsubscribe = self.onSubscribe - not implemented - remove element from self._subscribed self.mqttc.on_log = self.onLog def _canRun(self): return True def run(self): Log.debug("MQTT client run") if self.isRunning() or self._killing: self.restart() return if self._restarting: self._thread.finished.disconnect(self.run) self._restarting = False self._thread.start(QThread.LowestPriority) def stop(self): self._thread.quit() # emits finished Log.debug("Thread quit") def isRunning(self): return self._thread.isRunning() def _loop(self): if self._canRun(): self.loop() def setHost(self, host): self._host = host def host(self): return self._host def setPort(self, port): self._port = int(port) def port(self): return self._port def setPoll(self, poll): self._poll = int(poll) def setKeepAlive(self, keepAlive): self._keepAlive = max( int(keepAlive) + int(self._poll), self.kMinKeepAlive) def getClientId(self): return self._clientId def on_connect(self, client, obj, flags, rc): # paho # def on_connect(self, client, obj, rc): # mosquitto Log.debug("Connected " + str(rc)) if rc != mqtt.MQTT_ERR_SUCCESS: # paho # if rc != mqtt.MOSQ_ERR_SUCCESS: # mosquitto return self._connected = True self._attempts = 0 self._subscribed = [] self.onConnect(client, obj, rc) def restart(self): Log.debug("Restarting") if self.isRunning(): self._restarting = True self._thread.finished.connect(self.run) if not self._killing: self.kill() else: self.run() def on_disconnect(self, client, obj, rc): Log.debug("disconnecting rc: " + str(rc) + " " + str(self._connected)) if self._killing: Log.debug("killing") self._kill() self.onDisConnect(client, obj, rc) self._connected = False def onConnect(self, client, obj, rc): self.mqttOnConnect.emit(self, obj, rc) # QObject.emit(self, SIGNAL('mqttOnConnect'), self, obj, rc) pass def onDisConnect(self, client, obj, rc): self.mqttOnDisConnect.emit(self, obj, rc) # QObject.emit(self, SIGNAL('mqttOnDisConnect'), self, obj, rc) pass def onMessage(self, client, obj, msg): self.mqttOnMessage.emit(self, obj, msg) # QObject.emit(self, SIGNAL('mqttOnMessage'), self, obj, msg) # Log.debug('super ' + msg.topic+" "+str(msg.qos)+" "+str(msg.payload)) def onPublish(self, client, obj, mid): self.mqttOnPublish.emit(self, obj, mid) # QObject.emit(self._creator, SIGNAL('mqttOnPublish'), self, obj, mid) Log.debug("onPublish - Message ID: " + str(mid)) def onSubscribe(self, client, obj, mid, granted_qos): self.mqttOnSubscribe.emit(self, obj, mid, granted_qos) Log.info("Subscribed: " + str(mid) + " " + str(granted_qos)) def onLog(self, client, obj, level, msg): self.mqttOnLog.emit(msg, level) #Log.debug(string,level) def isConnected(self): return self.mqttc.socket() is not None and self._connected def publish(self, topic, payload, qos=0, retain=True): self.mqttc.publish(str(topic), payload, int(qos), retain) def subscribe(self, topic, qos=0): if self.isConnected() and not str(topic) in self._subscribed: try: self.mqttc.subscribe(str(topic), int(qos)) self._subscribed.append(str(topic)) Log.debug('Subscribed to ' + topic + " " + str(qos)) except Exception as e: Log.debug("Error on subscribe " + str(e)) raise e def unsubscribe(self, topic): if self.isConnected(): self.mqttc.unsubscribe(topic) Log.debug('Un_subscribed to ' + topic) def loop(self, timeout=0.1): if not self.isConnected(): if not self._killing: self._connect() return try: connResult = self.mqttc.loop(timeout) if connResult == mqtt.MQTT_ERR_SUCCESS: # paho # if connResult == mqtt.MOSQ_ERR_SUCCESS: # mosquitto return self._connected = False self._attempts += 1 Log.warn("MQTT: An error occurred while looping") self.mqttConnectionError.emit(self, mqtt.error_string(connResult)) except ValueError as e: if e == 'Invalid timeout.': self.mqttOnTimeout.emit(self, "Connection Timeout") else: Log.debug("Paho Client ValueError" + str(e)) except Exception as e: self.mqttConnectionError.emit(self, str(e)) Log.debug("MQTT Connect: Unknown exception raised " + str(e)) exc_type, exc_value, exc_traceback = sys.exc_info() Log.debug( repr( traceback.format_exception(exc_type, exc_value, exc_traceback))) def _kill(self): self._loopTimer.stop( ) # Stopped in self.stop but lets preempt this to avoid self.loop being called by running thread self._killTimer.stop() self._killing = False self._reset() # reset timer self.stop() def kill(self): try: if self.isConnected(): self._disconnect() self._killing = True self._killTimer.start(self._keepAlive) except Exception as e: Log.warn("Error cleaning up " + str(e)) pass def _connect(self): try: if not self._connected: if not self._attempts < self.kMaxAttempts: if not self._resetTimer.isActive(): Log.progress( Settings.getMeta("name") + ": Max connection attempts reached - waiting " + str(self.kResetTimer) + " seconds before retrying") self._resetTimer.start(self.kResetTimer * 1000) # 1 minute parameterise return if self._attempts > 0 and (time.time() - pow( 2, self._attempts + 1)) < self._attemped: return Log.debug("Trying to connect") self._attemped = time.time() result = self.mqttc.connect(str(self._host), int(self._port), int(self._keepAlive), 1) self._connected = result == mqtt.MQTT_ERR_SUCCESS # paho # self._connected = result == mqtt.MOSQ_ERR_SUCCESS # mosquitto if not self._connected: self._attempts += 1 Log.progress(mqtt.connack_string(connResult)) self.mqttConnectionError.emit( self, mqtt.connack_string(connResult)) except Exception as e: msg = 'MQTT: ' + str(e) self.mqttConnectionError.emit(self, msg) #Log.progress(msg) Log.debug(msg) #exc_type, exc_value, exc_traceback = sys.exc_info() #Log.debug(repr(traceback.format_exception(exc_type, exc_value, # exc_traceback))) self._attempts += 1 self._connected = False def _disconnect(self): try: self.mqttc.disconnect() except Exception as e: Log.warn('MQTT Disconnection Error' + str(e)) def _reset(self): Log.warn("Timer reset ") self._attempts = 0 self._resetTimer.stop() # not required
class DataViewerBase(QMainWindow): """ Base GUI class for viewing data / images. This class was made in the purpose of viewing VMI images. """ # _name = DataViewerBase().__class__.__name__ def __init__(self, filepath=""): """ Initialization. """ super().__init__() self.initInnerParameters(filepath) self.initGui() self.initGetDataProcess() self.initUpdateImageProcess() self.initCheckWindowProcess() @footprint def initInnerParameters(self, filepath): """ Initialize the inner parameters. """ self._mutex = QMutex() self._windows = [] self.initData() self._isUpdatingImage = False self._font_size_button = 16 # [pixel] self._font_size_groupbox_title = 12 # [pixel] self._font_size_label = 11 # [pixel] self._font_bold_label = True self._init_window_width = 1600 # [pixel] self._init_window_height = 700 # [pixel] self._init_button_color = "#EBF5FB" self.main_bgcolor = "#FDF2E9" self._get_data_interval = 1 # [sec] self._get_data_worker_sleep_interval = self._get_data_interval - 0.1 # [sec] self._update_image_interval = 2 # [sec] self._get_update_delay = 1 # [sec] self._check_window_interval = 1 # [sec] self._currentDir = os.path.dirname(__file__) self._online = False self._closing_dialog = True if os.path.exists( os.path.join(os.path.dirname(__file__), "config.json")): self.loadConfig() if not os.path.exists( os.path.join(os.path.dirname(__file__), "config_getdata.json")): raise FileNotFoundError("config_getdata.json") self.loadConfigGetData() @footprint @pyqtSlot() def initData(self): """ Initialize inner data. """ self.dataset = { "sig_wl": None, "sig_wol": None, "bg_wl": None, "bg_wol": None } self.nbr_of_sig = 0 self.nbr_of_bg = 0 self.sig = None self.bg = None self.currentRun = -1 self.startTag = -1 self.endTag = -1 @footprint def loadConfig(self): """ Load a config file. """ with open(os.path.join(os.path.dirname(__file__), "config.json"), 'r') as ff: config = json.load(ff) if config.get("currentDir") is not None: if isinstance(config.get("currentDir"), str): if os.path.exists(config.get("currentDir")): self._currentDir = config["currentDir"] if config.get("online") is not None: if isinstance(config.get("online"), bool): self._online = config["online"] if config.get("closing_dialog") is not None: if isinstance(config.get("closing_dialog"), bool): self._closing_dialog = config["closing_dialog"] if config.get("emulate") is not None: if isinstance(config.get("emulate"), bool): self._emulate = config["emulate"] if config.get("font_size_button") is not None: if isinstance(config.get("font_size_button"), int): self._font_size_button = config["font_size_button"] if config.get("font_size_groupbox_title") is not None: if isinstance(config.get("font_size_groupbox_title"), int): self._font_size_groupbox_title = config[ "font_size_groupbox_title"] if config.get("font_size_label") is not None: if isinstance(config.get("font_size_label"), int): self._font_size_label = config["font_size_label"] if config.get("font_bold_label") is not None: if isinstance(config.get("font_bold_label"), bool): self._font_bold_label = config["font_bold_label"] self._config = config def loadConfigGetData(self): """ Load a config file of getDatawithOLPY. """ with open( os.path.join(os.path.dirname(__file__), "config_getdata.json"), 'r') as ff: config_get_data = json.load(ff) self._get_data_interval = config_get_data["interval"] # [sec] self._get_data_worker_sleep_interval = self._get_data_interval - 0.1 # [sec] self._get_data_ports = config_get_data["port"] self._get_info_port = config_get_data["port_info"] self._config_get_data = config_get_data @footprint def initGui(self): """ Initialize the GUI. """ self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.initMainWidget() self.setMenuBar() self.setWindowTitle("VMI Viewer") self.resize(self._init_window_width, self._init_window_height) ### RunInfo. group_runinfo = QGroupBox(self) group_runinfo.setTitle("RunInfo") font = group_runinfo.font() font.setPointSize(self._font_size_groupbox_title) group_runinfo.setFont(font) group_runinfo.resize(400, 100) grid_runinfo = QGridLayout(group_runinfo) # Run No. label_run = QLabel(self) label_run.setText("Run No. : ") label_run.setAlignment(Qt.AlignRight) font = label_run.font() font.setPointSize(self._font_size_label) font.setBold(self._font_bold_label) label_run.setFont(font) self.label_run_number = QLabel(self) self.label_run_number.setText("Unknown") pal = QPalette() pal.setColor(QPalette.Foreground, QColor("#0B5345")) self.label_run_number.setPalette(pal) font = self.label_run_number.font() font.setBold(True) font.setPointSize(self._font_size_label) self.label_run_number.setFont(font) # Tag No. label_tag = QLabel(self) label_tag.setText("Tag No. : ") label_tag.setAlignment(Qt.AlignRight) font = label_tag.font() font.setPointSize(self._font_size_label) font.setBold(self._font_bold_label) label_tag.setFont(font) self.label_tag_start = QLabel(self) self.label_tag_start.setText("None") font = self.label_tag_start.font() font.setBold(True) font.setPointSize(self._font_size_label) self.label_tag_start.setFont(font) label_tag_hyphen = QLabel(self) label_tag_hyphen.setText(" - ") label_tag_hyphen.setFixedWidth(30) font = label_tag_hyphen.font() font.setPointSize(self._font_size_label) font.setBold(self._font_bold_label) label_tag_hyphen.setFont(font) self.label_tag_end = QLabel(self) self.label_tag_end.setText("None") font = self.label_tag_end.font() font.setBold(True) font.setPointSize(self._font_size_label) self.label_tag_end.setFont(font) # Sig / BG. label_sig = QLabel(self) label_sig.setText("# of Sig : ") label_sig.setAlignment(Qt.AlignRight) font = label_sig.font() font.setPointSize(self._font_size_label) font.setBold(self._font_bold_label) label_sig.setFont(font) self.label_nbr_of_sig = QLabel(self) self.label_nbr_of_sig.setText("None") font = self.label_nbr_of_sig.font() font.setBold(True) font.setPointSize(self._font_size_label) self.label_nbr_of_sig.setFont(font) label_bg = QLabel(self) label_bg.setText("# of BG : ") label_bg.setAlignment(Qt.AlignRight) font = label_bg.font() font.setPointSize(self._font_size_label) font.setBold(self._font_bold_label) label_bg.setFont(font) self.label_nbr_of_bg = QLabel(self) self.label_nbr_of_bg.setText("None") font = self.label_nbr_of_bg.font() font.setBold(True) font.setPointSize(self._font_size_label) self.label_nbr_of_bg.setFont(font) # Construct the layout. grid_runinfo.addWidget(label_run, 0, 0) grid_runinfo.addWidget(self.label_run_number, 0, 1, 1, 3) grid_runinfo.addWidget(label_tag, 1, 0) grid_runinfo.addWidget(self.label_tag_start, 1, 1) grid_runinfo.addWidget(label_tag_hyphen, 1, 2) grid_runinfo.addWidget(self.label_tag_end, 1, 3) grid_runinfo.addWidget(label_sig, 2, 0) grid_runinfo.addWidget(self.label_nbr_of_sig, 2, 1) grid_runinfo.addWidget(label_bg, 2, 2) grid_runinfo.addWidget(self.label_nbr_of_bg, 2, 3) ### Settings. # group_settings = QGroupBox(self) # group_settings.setTitle("Settings") # font = group_settings.font() # font.setPointSize(self._font_size_groupbox_title) # group_settings.setFont(font) # group_settings.resize(400, 100) # grid_settings = QGridLayout(group_settings) # # Update interval. # label_upd_rate = QLabel(self) # label_upd_rate.setText("Upd. image interval: ") # font = label_upd_rate.font() # font.setPointSize(self._font_size_label) # font.setBold(self._font_bold_label) # label_upd_rate.setFont(font) # self.spinbox_upd_img_interval = QDoubleSpinBox(self) # self.spinbox_upd_img_interval.setValue(self._get_data_interval) # self.spinbox_upd_img_interval.setFixedWidth(100) # self.spinbox_upd_img_interval.setAlignment(Qt.AlignRight) # font = self.spinbox_upd_img_interval.font() # font.setBold(True) # font.setPointSize(self._font_size_label) # self.spinbox_upd_img_interval.setFont(font) # label_upd_rate_unit = QLabel(self) # label_upd_rate_unit.setText("sec") # font = label_upd_rate_unit.font() # font.setPointSize(self._font_size_label) # font.setBold(self._font_bold_label) # label_upd_rate_unit.setFont(font) # Construct the layout. # grid_settings.addWidget(label_upd_rate, 0, 0, 1, 3) # grid_settings.addWidget(self.spinbox_upd_img_interval, 0, 3) # grid_settings.addWidget(label_upd_rate_unit, 0, 4) ### Function buttons. group_func = QGroupBox(self) group_func.setTitle("Function") font = group_func.font() font.setPointSize(self._font_size_groupbox_title) group_func.setFont(font) group_func.resize(400, 100) grid_func = QGridLayout(group_func) grid_func.setSpacing(10) # Start/Stop main process button. self.brun = QPushButton(group_func) self.brun.setText("Start") font = self.brun.font() font.setPointSize(self._font_size_button) self.brun.setFont(font) self.brun.resize(400, 50) self.brun.setStyleSheet("background-color:{};".format( self._init_button_color)) self.brun.clicked.connect(self.runMainProcess) # Clear data button. bclear = QPushButton(group_func) bclear.setText("Clear") font = bclear.font() font.setPointSize(self._font_size_button) bclear.setFont(font) bclear.resize(400, 50) bclear.setStyleSheet("background-color:{};".format( self._init_button_color)) bclear.clicked.connect(self.clearData) # Save images button. bsave = QPushButton(group_func) bsave.setText("Save") font = bsave.font() font.setPointSize(self._font_size_button) bsave.setFont(font) bsave.resize(400, 50) bsave.setStyleSheet("background-color:{};".format( self._init_button_color)) bsave.clicked.connect(self.saveData) # New window button. bwindow = QPushButton(group_func) bwindow.setText("Window") font = bwindow.font() font.setPointSize(self._font_size_button) bwindow.setFont(font) bwindow.resize(400, 50) bwindow.setStyleSheet("background-color:{};".format( self._init_button_color)) bwindow.clicked.connect(self.showWindow) # Construct the layout of RunInfo groupbox. grid_func.addWidget(self.brun, 0, 0) grid_func.addWidget(bclear, 0, 1) grid_func.addWidget(bsave, 1, 0) grid_func.addWidget(bwindow, 1, 1) ### Plotting area. grp1 = QGroupBox(self) # grp1.setTitle("SIG WL") grp1.setTitle("SIG") font = grp1.font() font.setPointSize(self._font_size_groupbox_title) grp1.setFont(font) gp1 = QGridLayout(grp1) gp1.setSpacing(10) grp2 = QGroupBox(self) # grp2.setTitle("SIG WOL") grp2.setTitle("BG") font = grp2.font() font.setPointSize(self._font_size_groupbox_title) grp2.setFont(font) gp2 = QGridLayout(grp2) gp2.setSpacing(10) grp3 = QGroupBox(self) # grp3.setTitle("BG WL") grp3.setTitle("SIg - BG") font = grp3.font() font.setPointSize(self._font_size_groupbox_title) grp3.setFont(font) gp3 = QGridLayout(grp3) gp3.setSpacing(10) # grp4 = QGroupBox(self) # grp4.setTitle("BG WOL") # font = grp4.font() # font.setPointSize(self._font_size_groupbox_title) # grp4.setFont(font) # gp4 = QGridLayout(grp4) # gp4.setSpacing(10) kwargs = dict(px=False, py=False, ph=False, bp=False) self.pw1 = PlotWindow(self, **kwargs) self.pw2 = PlotWindow(self, **kwargs) self.pw3 = PlotWindow(self, **kwargs) # self.pw4 = PlotWindow(self, **kwargs) gp1.addWidget(self.pw1, 0, 0) gp2.addWidget(self.pw2, 0, 0) gp3.addWidget(self.pw3, 0, 0) # gp4.addWidget(self.pw4, 0, 0) ### Construct the layout. self.grid.addWidget(group_runinfo, 0, 0) # self.grid.addWidget(group_settings, 0, 1) self.grid.addWidget(group_func, 0, 1) self.grid.addWidget(grp1, 1, 0, 2, 1) self.grid.addWidget(grp2, 1, 1, 2, 1) self.grid.addWidget(grp3, 1, 2, 2, 1) # self.grid.addWidget(grp4, 1, 3, 2, 1) self.main_widget.setFocus() self.setCentralWidget(self.main_widget) @footprint def initMainWidget(self): """ Initialize the main widget and the grid. """ self.main_widget = QWidget(self) self.setStyleSheet("background-color:{};".format(self.main_bgcolor)) self.grid = QGridLayout(self.main_widget) self.grid.setSpacing(10) self.setWindowIcon( QIcon(os.path.join(os.path.dirname(__file__), "python.png"))) @footprint def setMenuBar(self): """ Set the contents of the menu bar """ ## File file_menu = QMenu('&File', self) # Open # file_menu.addAction('&Open', self.openFile, # QtCore.Qt.CTRL + QtCore.Qt.Key_O) # Config file_menu.addAction('&Config', self.setConfig, QtCore.Qt.CTRL + QtCore.Qt.Key_C) # Quit file_menu.addAction('&Quit', self.quitApp, QtCore.Qt.CTRL + QtCore.Qt.Key_Q) self.menuBar().addMenu(file_menu) ## Help # help_menu = QMenu('&Help', self) # help_menu.addAction('Help', self.showHelp) # help_menu.addAction('About...', self.showAbout) self.menuBar().addSeparator() # self.menuBar().addMenu(help_menu) ######################## Menu bar ######################## @footprint def openFile(self): """ Show a file dialog and select a file """ pass @footprint def setConfig(self): """ Set configuration of this application. """ pass @footprint def quitApp(self): """ Quit this application. """ self.close() @footprint def showHelp(self): """ Show a pop-up dialog showing how to use this application. """ pass @footprint def showAbout(self): """ Show a pop-up dialog describing this application. """ pass ######################## Widgets' functions ######################## @footprint @pyqtSlot() def showWindow(self): window = PlotWindow(self, "win{0:02d}".format(len(self._windows) + 1)) window.show() window.raise_() window.activateWindow() self._windows.append(window) @footprint @pyqtSlot() def runMainProcess(self): if not self._timer_getData.isActive(): self.initData() for listener in self._worker_getData.listeners.values(): listener.Connect() self._worker_getData.listener_info.Connect() # self._update_image_interval = self.spinbox_upd_img_interval.value() # self.spinbox_upd_img_interval.setEnabled(False) self._timer_getData.start() self.brun.setText("Stop") if not self._timer_updImage.isActive(): time.sleep(self._get_update_delay) self._timer_updImage.start() else: self.brun.setEnabled(False) self.stopTimer = True @footprint @pyqtSlot() def saveData(self): now_save = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") now = datetime.datetime.now().strftime("%Y%m%d%H%M%S") saveDataDir = os.path.join(os.path.dirname(__file__), "data", "Run{}".format(str(self.currentRun))) # Data. if not os.path.exists(saveDataDir): os.makedirs(saveDataDir) for _types in self._get_data_ports.keys(): np.save(os.path.join(saveDataDir, "data_{0}_{1}.npy".format(now, _types)), \ self.dataset[_types]) # Image. # saveScn = os.path.join(saveDataDir, "{}_screenshot.png".format(now)) # QPixmap.grabWindow(self.winId()).save(saveScn, 'png') # Status. status = { "save_datetime": now_save, "Run": self.label_run_number.text(), "StartTag": self.label_tag_start.text(), "CurrentTag": self.label_tag_end.text() } with open(os.path.join(saveDataDir, "{}_status.json".format(now)), "w") as ff: json.dump(status, ff) @footprint @pyqtSlot() def clearData(self): self.saveData() self.initData() ######################## GetDataProcess ######################## @footprint def initGetDataProcess(self): self._timer_getData = QTimer() self._timer_getData.setInterval(int(self._get_data_interval * 1000)) self.stopTimer = False self._thread_getData = QThread() self._worker_getData = GetDataWorker3(port=self._get_data_ports, port_info=self._get_info_port) self._worker_getData.sleepInterval = self._get_data_worker_sleep_interval # Start. self._timer_getData.timeout.connect(self.startGettingDataThread) self._thread_getData.started.connect(self._worker_getData.process) self._worker_getData.do_something.connect(self.updateData) # Finish. self._worker_getData.finished.connect(self._thread_getData.quit) self._thread_getData.finished.connect(self.checkIsTimerStopped) # Move. self._worker_getData.moveToThread(self._thread_getData) @footprint @pyqtSlot() def startGettingDataThread(self): if not self._thread_getData.isRunning(): print("start thread by timer.") self._thread_getData.start() else: print("Thread is running.") @footprint @pyqtSlot(object) def updateData(self, obj): if self._isUpdatingImage is False: # In case. if obj is not None: for key in self.dataset.keys(): if self.dataset.get(key) is None and obj.get( key) is not None: self.dataset[key] = obj.get(key).copy() elif obj.get(key) is not None: self.dataset[key] += obj.get(key).copy() currentRun = obj.get("currentRun") self.label_run_number.setText(str(currentRun)) if self.nbr_of_sig == 0: self.label_tag_start.setText(str(obj.get("startTag"))) self.label_tag_end.setText(str(obj.get("endTag"))) self.nbr_of_sig += obj.get("nbr_sig_wl") + obj.get( "nbr_sig_wol") self.nbr_of_bg += obj.get("nbr_bg_wl") + obj.get("nbr_bg_wol") self.label_nbr_of_sig.setText(str(self.nbr_of_sig)) self.label_nbr_of_bg.setText(str(self.nbr_of_bg)) if self.currentRun != -1 and currentRun != self.currentRun: self.saveData() self.initData() self.currentRun = currentRun @footprint @pyqtSlot() def checkIsTimerStopped(self): if self.stopTimer: self._timer_getData.stop() self._timer_updImage.stop() print("timer stopped.") for listener in self._worker_getData.listeners.values(): listener.Close() self._worker_getData.listener_info.Close() self.stopTimer = False self.brun.setEnabled(True) self.brun.setText("Start") # self.spinbox_upd_img_interval.setEnabled(True) ######################## updateImageProcess ######################## @footprint def initUpdateImageProcess(self): self._timer_updImage = QTimer() self._timer_updImage.setInterval( int(self._update_image_interval * 1000)) self._timer_updImage.timeout.connect(self.updateImage) @footprint def updateImage(self): self._isUpdatingImage = True try: with QMutexLocker(self._mutex): sig_wl = self.dataset.get("sig_wl", None) sig_wol = self.dataset.get("sig_wol", None) bg_wl = self.dataset.get("bg_wl", None) bg_wol = self.dataset.get("bg_wol", None) if self.sig is None or self.bg is None: self.sig = sig_wl + sig_wol self.bg = bg_wl + bg_wol else: self.sig += sig_wl + sig_wol self.bg += bg_wl + bg_wol # print(self.sig.dtype) # buff1 = self.sig / float(self.nbr_of_sig) self.pw1.data = self.sig / float(self.nbr_of_sig) # buff1 = self.bg / float(self.nbr_of_bg) self.pw2.data = self.bg / float(self.nbr_of_bg) self.pw3.data = self.pw1.data - self.pw2.data # self.pw4.data = bg_wol # if sig_wl is not None and sig_wol is not None: # self.pw3.data = sig_wl - sig_wol for window in self._windows: if not window.is_closed: window.data = sig_wl except Exception as ex: print(ex) self._isUpdatingImage = False if self.sig is not None and self.bg is not None: self.pw1.updateImage() self.pw2.updateImage() self.pw3.updateImage() # self.pw4.updateImage() ######################## CheckWindowProcess ######################## @footprint def initCheckWindowProcess(self): """ Initialize checkWindow process. """ self._timer_checkWindow = QTimer() self._timer_checkWindow.setInterval( int(self._check_window_interval * 1000)) self._timer_checkWindow.timeout.connect(self.checkWindow) self._timer_checkWindow.start() # @footprint @pyqtSlot() def checkWindow(self): """ Check whether windows are active. """ # print(len(self._windows)) N = len(self._windows) * 1 for ii in range(N): if self._windows[N - ii - 1].is_closed: del self._windows[N - ii - 1] # @footprint # @pyqtSlot() # def finishWorker(self): # pass ######################## Closing processes ######################## @footprint def closeEvent(self, event): if self._thread_getData.isRunning(): string = "Some threads are still running.\n" string += "Please wait for their finishing." confirmObject = QMessageBox.warning(self, "Closing is ignored.", string, QMessageBox.Ok) event.ignore() return if self._closing_dialog: confirmObject = QMessageBox.question( self, "Closing...", "Are you sure to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if confirmObject == QMessageBox.Yes: self.makeConfig() with open( os.path.join(os.path.dirname(__file__), "config.json"), "w") as ff: json.dump(self.config, ff) self.stopAllTimers() self.saveData() event.accept() else: event.ignore() else: self.makeConfig() with open(os.path.join(os.path.dirname(__file__), "config.json"), "w") as ff: json.dump(self.config, ff, indent=4) self.saveData() self.stopAllTimers() @footprint def stopAllTimers(self): if self._timer_getData.isActive(): self._timer_getData.stop() if self._timer_updImage.isActive(): self._timer_updImage.stop() if self._timer_checkWindow.isActive(): self._timer_checkWindow.stop() @footprint def makeConfig(self): """ Make a config dict object to save the latest configration in. """ self.config = OrderedDict([ ("online", self._online), ("closing_dialog", self._closing_dialog), ("currentDir", self._currentDir), ("emulate", self._emulate), ("font_size_button", self._font_size_button), ("font_size_label", self._font_size_label), ("font_size_groupbox_title", self._font_size_groupbox_title) ])
class MainWindow(QMainWindow, Ui_MainWindow): """ Class for main application window. """ def __init__(self, parent=None): """ Main application window. """ QMainWindow.__init__(self, parent) self.win7_taskbar = Win7Taskbar(self) self.app = QApplication.instance() self.setupUi(self) self.setWindowTitle('ABBYY Automator v{0:s}'.format(self.app.applicationVersion())) self.settings = Settings() self.get_app_paths() self.button_save_errors.setVisible(False) self.progress_bar.setVisible(0) self.on_tb_refresh_profiles_released() self.last_logged_text = '' self.file_watcher_threads = [] self.file_watcher = None self.processed_count = 0 self.skipped_count = 0 self.file_queue = deque() self.current_pathname = None self.current_watch_path = None self.error_paths = [] self.acrobat_proxy_listener = AcrobatProxyListener() self.acrobat_proxy_listener.new_path.connect(self.path_received) self.abbyy_ocr = AbbyyOcr(self.abby_path) self.abbyy_ocr.error.connect(self.error_received) self.update_processed_status() self.statusbar.update_left('Ready to begin') self.output_folder = '' self.load_settings() def save_settings(self): self.settings.last_watch_folder = self.le_watch_folder.text() self.settings.last_output_folder = self.le_output_folder.text() self.settings.last_profile = self.cb_profile.currentText() def load_settings(self): self.le_watch_folder.setText(self.settings.last_watch_folder) self.le_output_folder.setText(self.settings.last_output_folder) index = self.cb_profile.findText(self.settings.last_profile) if index == -1: self.cb_profile.setCurrentIndex(0) else: self.cb_profile.setCurrentIndex(index) def get_app_paths(self): # Get ABBYY path. self.abby_path = find_app_path('FineReader') if not self.abby_path or not os.path.isfile(self.abby_path): message_box_error('ABBYY FineReader not found', 'Could not find an ABBYY FineReader install on this machine.') sys.exit() # Check for version 10 of ABBYY. abbyy_dir = os.path.dirname(self.abby_path) if not abbyy_dir[-2:] == '10': ten_path = '{0:s}10\\FineReader.exe'.format(self.abby_path[-2:]) if os.path.isfile(ten_path): self.abby_path = ten_path else: message_box_error('ABBYY FineReader 10 not found', 'ABBYY FineReader was found on this machine, but a version other than 10.') sys.exit() # Get Acrobat path. self.acrobat_path = find_app_path('Acrobat') if not self.acrobat_path or not os.path.isfile(self.acrobat_path): message_box_error('Adobe Acrobat not found', 'Could not find an Adobe Acrobat install on this machine.') sys.exit() def install_acrobat_proxy(self): acrobat_backup_path = '{0:s}_.exe'.format(self.acrobat_path[:-4]) acrobat_version = get_exe_version(self.acrobat_path) if acrobat_version[0] < 5 and os.path.isfile(acrobat_backup_path): # Proxy already installed. self.log('Acrobat proxy already installed.', bold=True) return True self.log('Installing Acrobat proxy over Acrobat v{0:d}.{1:d}...'.format(*acrobat_version), bold=True) try: # Backup existing Acrobat.exe. shutil.move(self.acrobat_path, acrobat_backup_path) except(OSError, IOError): self.log('Error moving Acrobat.exe - is Acrobat running?', bold=True, colour='red') return False try: # Copy Proxy in place of Acrobat shutil.copy('Acrobat Proxy.exe', self.acrobat_path) except(OSError, IOError): self.log('Error installing proxy - please check folder permissions.', bold=True, colour='red') return False return True def restore_acrobat(self): acrobat_backup_path = '{0:s}_.exe'.format(self.acrobat_path[:-4]) acrobat_version = get_exe_version(acrobat_backup_path) proxy_version = get_exe_version(self.acrobat_path) if not acrobat_version and proxy_version: return False if proxy_version[0] > 5 and os.path.isfile(acrobat_backup_path): # Proxy not installed. self.log('Acrobat proxy not installed.', bold=True, colour='red') return True self.log('Restoring Acrobat v{0:d}.{1:d}...'.format(*acrobat_version), bold=True) try: # Backup existing Acrobat.exe. shutil.move(acrobat_backup_path, self.acrobat_path) except(OSError, IOError): self.log('Error moving Acrobat.exe - is Acrobat running?', bold=True, colour='red') return False return True def increment_processed(self): self.processed_count += 1 self.update_processed_status() def update_processed_status(self): if not self.skipped_count: skipped_text = '' else: if self.skipped_count == 1: skipped_text = ' (1 file skipped)' else: skipped_text = ' ({0:,} files skipped)'.format(self.skipped_count) if self.processed_count == 1: self.statusbar.update_middle('1 file processed{0:s}'.format(skipped_text)) else: self.statusbar.update_middle('{0:,} files processed{1:s}'.format(self.processed_count, skipped_text)) queue_size = len(self.file_queue) if queue_size == 1: self.statusbar.update_right('1 file remaining') else: self.statusbar.update_right('{0:,} files remaining'.format(queue_size)) self.app.processEvents() def log(self, text='', indent=False, update_existing=False, colour=None, bold=False, status_bar=False): """ Update the QPlainTextEdit based log. """ if colour: if colour == 'green': text = '<font color="#347235">%s</font>' % text elif colour == 'red': text = '<font color="#7E2217">%s</font>' % text if bold: text = '<b>%s</b>' % text if indent or update_existing: text = ' %s' % text if update_existing: self.pte_log.undo() text = '%s%s' % (self.last_logged_text, text) self.pte_log.appendHtml(text) self.last_logged_text = text self.app.processEvents() v_scrollbar = self.pte_log.verticalScrollBar() v_scrollbar.setValue(v_scrollbar.maximum()) if status_bar: # Remove HTML tags from the text and add to the status bar. self.statusbar.showMessage(regex_html_tags.sub('', text)) def queue_size_changed(self, count=None): # If we've items in the queue, we're in process mode and currently are not processing any files then start # on the next item in the queue. if not count: count = len(self.file_queue) if count and self.button_start.isChecked() and not self.current_pathname: print 'Processing restarted!' self.process_next() def queue_received(self, queue_list): print 'Received new queue' length = len(queue_list) self.file_queue = deque(queue_list) self.log('Found <b>{0:,} files'.format(length)) self.progress_bar.setRange(0, length) self.update_processed_status() if self.button_start.isChecked(): self.log() self.log('Processing queue', bold=True) self.log() self.process_next() def adjust_progress_bars(self): self.progress_bar.setMaximum(len(self.file_queue)) def watch_folder_changed(self, event): print event action, filename = event if action == 3: # Updated. return elif action in (1, 4): # Created / Renamed from something. self.file_queue.append(filename) elif action in (2, 5): # Deleted / Renamed to something. try: self.file_queue.remove(filename) except ValueError: print 'Error removing from queue:', filename self.update_processed_status() self.queue_size_changed() self.adjust_progress_bars() def process_next(self): """ Get next item in the queue and send it to ABBYY. """ try: while True: # Keep pulling from the queue until the output file doesn't already exist. path = self.file_queue.popleft() out_path = os.path.join(self.output_folder, '{0:s}.pdf'.format(path[len(self.current_watch_path) + 1:-4])) print 'CHECKING', out_path if not os.path.isfile(out_path): break self.skipped_count += 1 self.update_processed_status() except IndexError: # Queue is empty. print 'QUEUE IS NOW EMPTY!' self.current_pathname = None self.progress_bar.setMaximum(0) self.progress_bar.setValue(0) self.statusbar.update_left('Waiting for files...') return self.current_pathname = path print 'QUEUE PROCESSING:', path self.statusbar.update_left(path[len(self.current_watch_path):]) self.abbyy_ocr.ocr(path) def closeEvent(self, event): self.save_settings() self.file_queue.clear() # Ensure the queue is clear for a clean exit. self.restore_acrobat() self.abbyy_ocr.kill() super(MainWindow, self).closeEvent(event) def path_received(self, path): self.abbyy_ocr.kill() path = str(path) print 'Path received:', path # Calculate output path and store it for when the file is done. out_path = os.path.join(self.output_folder, '{0:s}.pdf'.format(self.current_pathname[len(self.current_watch_path) + 1:-4])) out_folder = os.path.dirname(out_path) if not os.path.isdir(out_folder): os.makedirs(out_folder) try: move(path, out_path) except(IOError, OSError): print 'Error moving', path, 'to', out_path self.progress_bar.setValue(self.progress_bar.value() + 1) self.increment_processed() self.update_processed_status() if self.button_start.isChecked(): self.process_next() else: self.acrobat_proxy_listener.stop() def error_received(self, error_message): self.button_save_errors.setVisible(True) self.error_paths.append(self.current_pathname) self.log('Error processing {0:s}:'.format(self.current_pathname), bold=True, colour='red') self.log('Error phrase matched: <b>{0:s}</b>'.format(error_message), indent=True) self.progress_bar.setValue(self.progress_bar.value() + 1) self.increment_processed() self.app.processEvents() # Keep the GUI responsive during processing. self.process_next() def file_watcher_error(self, error_message): self.log('WATCH FOLDER ERROR:', colour='red', bold=True) self.log(error_message, indent=True, bold=True) message_box_error('Watch folder error', error_message) # Lock down all application controls other than the error log saving button. self.button_start.setChecked(False) for widget in (self.button_start, self.button_output_browse, self.button_watch_browse, self.cb_profile, self.tb_refresh_profiles, self.rb_jpeg, self.rb_pdf, self.rb_tiff): widget.setEnabled(False) return def set_inputs_enabled(self, enabled=True): for item in (self.inputs_layout.itemAt(i).widget() for i in xrange(self.inputs_layout.count())): if isinstance(item, QWidget): item.setEnabled(enabled) def reset(self): self.acrobat_proxy_listener.stop() self.progress_bar.setVisible(False) self.button_start.setChecked(False) self.processed_count = 0 self.skipped_count = 0 self.file_queue = deque() self.update_processed_status() @pyqtSignature('') def on_button_reset_released(self): self.button_start.setChecked(False) print 'Stopping file watcher...' try: self.file_watcher.stopping = True self.file_watcher_thread.quit() # self.file_watcher_thread.terminate() del self.file_watcher # del self.file_watcher_thread except AttributeError: print 'Could not stop file watcher. Was it running?' print 'File watcher stopped.' self.reset() self.set_inputs_enabled(True) self.button_reset.setEnabled(False) @pyqtSignature('bool') def on_button_start_toggled(self, pressed): if not pressed: print 'Released!', pressed self.progress_bar.setVisible(False) self.statusbar.update_left('Ready to begin') if self.current_pathname is None: self.acrobat_proxy_listener.stop() return self.set_inputs_enabled(False) self.button_reset.setEnabled(True) self.output_folder = str(self.le_output_folder.text()) print "Output folder:", self.output_folder watch_folder = str(self.le_watch_folder.text()) print watch_folder if not os.path.isdir(watch_folder): message_box_error( 'Invalid watch folder', 'Please set the watch folder to a valid path and try again.' ) self.button_start.setChecked(False) return # Set profile. if self.cb_profile.currentIndex(): self.abbyy_ocr.current_profile = str(self.cb_profile.itemData(self.cb_profile.currentIndex()).toString()) else: self.abbyy_ocr.current_profile = None print 'Current profile:', self.abbyy_ocr.current_profile print self.install_acrobat_proxy() # return self.current_watch_path = watch_folder self.log('Searching for files in <b>{0:s}</b>...'.format(watch_folder), status_bar=True) self.progress_bar.setVisible(True) self.progress_bar.setRange(0, 0) if not self.acrobat_proxy_listener.start(): message_box_error('Error starting Acrobat Proxy Listener', self.acrobat_proxy_listener.errorString()) self.log('Error starting Acrobat Proxy Listener', colour='red') self.log(str(self.acrobat_proxy_listener.errorString()), indent=True, bold=True) self.reset() return # if not hasattr(self, 'file_watcher_thread'): # Get extension from radio buttons. if self.rb_tiff.isChecked(): extension = ('.tiff', '.tif') elif self.rb_pdf.isChecked(): extension = '.pdf' elif self.rb_jpeg.isChecked(): extension = '.jpg' else: raise Exception('No extension selected') self.file_watcher_thread = QThread() self.file_watcher_threads.append(self.file_watcher_thread) self.file_watcher = FileWatcher(watch_folder, extension) self.file_watcher.moveToThread(self.file_watcher_thread) self.file_watcher_thread.started.connect(self.file_watcher.start) self.file_watcher_thread.finished.connect(self.file_watcher_thread.deleteLater) self.file_watcher.finished.connect(self.file_watcher_thread.quit) self.file_watcher.count_change.connect(self.queue_size_changed) self.file_watcher.first_queue.connect(self.queue_received) self.file_watcher.queue_change.connect(self.watch_folder_changed) self.file_watcher.error.connect(self.file_watcher_error) if not self.file_watcher_thread.isRunning(): self.file_watcher_thread.start() else: if self.file_queue: self.adjust_progress_bars() self.progress_bar.setValue(0) self.update_processed_status() self.process_next() @pyqtSignature('') def on_button_watch_browse_released(self): output_path = unicode(self.le_output_folder.text()) if output_path and os.path.isdir(output_path): start_path = output_path else: start_path = '' watch_path = QFileDialog.getExistingDirectory(self, 'Select an input folder', start_path) if not watch_path: return self.le_watch_folder.setText(watch_path) @pyqtSignature('') def on_button_output_browse_released(self): watch_path = unicode(self.le_watch_folder.text()) if watch_path and os.path.isdir(watch_path): start_path = watch_path else: start_path = '' output_path = QFileDialog.getExistingDirectory(self, 'Select an output folder', start_path) if not output_path: return self.le_output_folder.setText(output_path) @pyqtSignature('') def on_tb_refresh_profiles_released(self): if hasattr(sys, 'frozen'): profiles_dir = os.path.join(os.path.dirname(sys.executable), 'profiles') else: profiles_dir = os.path.join(os.getcwd(), 'profiles') profiles_list = sorted(iglob('{0:s}/*.fbt'.format(profiles_dir))) self.cb_profile.clear() self.cb_profile.addItem('Last used ABBYY settings') for path in profiles_list: title = os.path.basename(path)[:-4] self.cb_profile.addItem(title, path) @pyqtSignature('') def on_button_save_errors_released(self): error_log_path = QFileDialog.getSaveFileName(self, 'Save error log', filter='Text Files (*.txt)') if not error_log_path: return with open(error_log_path, 'w') as log_file: for item in self.error_paths: log_file.write('{0:s}\n'.format(item))
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, _extruder, 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 SensitiveLegendRaster(QObject): def __init__(self): super(SensitiveLegendRaster, self).__init__() # QThread self.layer = self.worker = self.thread = None self.canvas = iface.mapCanvas() self.msgBar = iface.messageBar() self.legend = iface.legendInterface() self.nameModulus = "Script_Sensitive_Legend" # self.initThread() self._connect() # self.selectLayer(iface.activeLayer()) def __del__(self): self.finishThread() self._connect(False) def printMsgBar(self, msg, typeMsg=QgsMessageBar.INFO): self.msgBar.popWidget() if typeMsg == QgsMessageBar.INFO: self.msgBar.pushMessage("SensitiveLegendRaster Script", msg, typeMsg) else: self.msgBar.pushMessage("SensitiveLegendRaster Script", msg, typeMsg, 5) def initThread(self): self.thread = QThread(self) self.thread.setObjectName(self.nameModulus) self.worker = WorkerSensitiveLegendRaster() self.worker.moveToThread(self.thread) self._connectWorker() def finishThread(self): self._connectWorker(False) self.worker.deleteLater() self.thread.wait() self.thread.deleteLater() self.thread = self.worker = None def _connectWorker(self, isConnect=True): ss = [ {"signal": self.thread.started, "slot": self.worker.run}, {"signal": self.worker.finished, "slot": self.finishedWorker}, {"signal": self.worker.messageResult, "slot": self.messageResultWorker}, {"signal": self.worker.messageStatus, "slot": self.messageStatusWorker}, ] if isConnect: for item in ss: item["signal"].connect(item["slot"]) else: for item in ss: item["signal"].disconnect(item["slot"]) def _connect(self, isConnect=True): ss = [ {"signal": self.legend.currentLayerChanged, "slot": self.selectLayer}, {"signal": QgsMapLayerRegistry.instance().layerWillBeRemoved, "slot": self.unselectLayer}, {"signal": self.canvas.extentsChanged, "slot": self.changeSensitiveLegend}, ] if isConnect: for item in ss: item["signal"].connect(item["slot"]) else: for item in ss: item["signal"].disconnect(item["slot"]) @pyqtSlot() def finishedWorker(self): self.thread.quit() if self.worker.isKilled: # When PAN/ZOOM/... self.thread.wait() self.changeSensitiveLegend() @pyqtSlot(str) def messageResultWorker(self, msg): self.printMsgBar(msg) @pyqtSlot(str) def messageStatusWorker(self, msg): self.printMsgBar(msg) @pyqtSlot("QgsMapLayer") def selectLayer(self, layer): if self.thread.isRunning(): return isOk = True msg = "" typeMsg = QgsMessageBar.WARNING if not layer is None and layer.type() == QgsMapLayer.RasterLayer: legendColorAll = layer.legendSymbologyItems() if len(legendColorAll) > 0: # Had a classification self.layer = layer self.worker.setLegendReadBlock(layer) msg = "Raster Layer '%s' actived" % layer.name() typeMsg = QgsMessageBar.INFO else: msg = "Raster Layer '%s' need be a classification" % layer.name() isOk = False else: if layer is None: msg = "Active a Raster layer" else: msg = "Layer '%s' need be a Raster" % layer.name() isOk = False self.printMsgBar(msg, typeMsg) return isOk @pyqtSlot(str) def unselectLayer(self, idLayer): if idLayer == self.layer.id(): if self.thread.isRunning(): self.worker.isKilled = True msg = "Raster Layer '%s' was removed" % self.layer.name() self.printMsgBar(msg, QgsMessageBar.WARNING) self.layer = None @pyqtSlot() def changeSensitiveLegend(self): if self.layer is None: return if self.thread.isRunning(): self.worker.isKilled = True return mapSettings = self.canvas.mapSettings() crsCanvas = mapSettings.destinationCrs() extentCanvas = self.canvas.extent() extentLayer = self.layer.extent() resX = self.layer.rasterUnitsPerPixelX() resY = self.layer.rasterUnitsPerPixelY() if self.layer.crs() != crsCanvas: extentCanvas = mapSettings.mapToLayerCoordinates(self.layer, extentCanvas) if not extentCanvas.intersects(extentLayer): self.printMsgBar("View not intersects Raster '%s'" % self.layer.name, QgsMessageBar.WARNING) return keysLegend = range(len(self.worker.legendAll)) # [ idClass1, idClass2, ... ] [ 0..N-1] if extentCanvas == extentLayer or extentCanvas.contains(extentLayer): legendsView = map(lambda x: "(%s)%s" % (x, self.worker.legendAll[x]), keysLegend) msg = "[%d] = %s" % (len(legendsView), " ".join(legendsView)) self.printMsgBar(msg) return extent = extentCanvas.intersect(extentLayer) widthRead = int(extent.width() / resX) + 1 heightRead = int(extent.height() / resY) + 1 self.worker.setProcessImage(extent, widthRead, heightRead) self.thread.start()
class MQTTClient(QtCore.QObject): """ Wrapper class for Mosquitto MQTT client Provides inherited helper classes for SingleShot and Test requests Initial approach was to sub class QThread, but reading revealed that running the timers within a thread was the preferred approach. """ kMaxAttempts = 3 kMinKeepAlive = 5 kResetTimer = 60 mqttOnConnect = pyqtSignal(QObject,QObject,int) mqttOnDisConnect = pyqtSignal(QObject,QObject,int) mqttOnMessage = pyqtSignal(QObject,QObject,QObject) mqttOnPublish = pyqtSignal(QObject,QObject,int) mqttOnSubscribe = pyqtSignal(QObject,QObject,int,int) mqttOnLog = pyqtSignal(str,int) mqttOnTimeout = pyqtSignal(QObject) mqttConnectionError = pyqtSignal(QObject,str) # Hmmm new style signals cause problems with multiple parameters # mqttConnectionError = QtCore.pyqtSignal([str]) # Add username/password def __init__(self, creator, clientId, broker, cleanSession=True): super(MQTTClient, self).__init__() # Load settings self._creator = creator # create client id self._cleanSession = cleanSession self._resetTimer = QTimer() self._resetTimer.setSingleShot(True) self._resetTimer.timeout.connect(self._reset) self._killTimer = QTimer() self._killTimer.setSingleShot(True) self._killTimer.timeout.connect(self._kill) self._killing = False self._loopTimer = QTimer() self._loopTimer.setSingleShot(False) self._loopTimer.timeout.connect(self._loop) self._clientId = clientId self._host = broker.host() self._port = int(broker.port()) self._poll = int(broker.poll()) self.setKeepAlive(broker.keepAlive()) self._attempts = 0 self._attempted = 0 self._connected = False self._thread = QThread(self) self._thread.started.connect(lambda: self._loopTimer.start(self._poll)) self._thread.finished.connect(self._loopTimer.stop) self._thread.started.connect(lambda:Log.debug("Thread started")) self._thread.finished.connect(lambda:Log.debug("Thread stopped")) self._thread.terminated.connect(lambda:Log.debug("Thread terminated")) self._restarting = False self._subscribed =[] # self.mqttc = mqtt.Client(self._clientId, self._cleanSession) self.mqttc = mqtt.Mosquitto(self._clientId, self._cleanSession) if broker.username(): # Basic Auth! self.mqttc.username_pw_set(broker.username(), broker.password()) self.mqttc.on_connect = self.on_connect self.mqttc.on_disconnect = self.on_disconnect self.mqttc.on_message = self.onMessage self.mqttc.on_publish = self.onPublish self.mqttc.on_subscribe = self.onSubscribe # self.mqttc.on_unsubscribe = self.onSubscribe - not implemented - remove element from self._subscribed self.mqttc.on_log = self.onLog def _canRun(self): return True def run(self): Log.debug("MQTT client run") if self.isRunning() or self._killing: self.restart() return if self._restarting: self._thread.finished.disconnect(self.run) self._restarting = False self._thread.start(QThread.LowestPriority) def stop(self): self._thread.quit() # emits finished Log.debug("Thread quit") def isRunning(self): return self._thread.isRunning() def _loop(self): if self._canRun(): self.loop() def setHost(self, host): self._host = host def host(self): return self._host def setPort(self, port): self._port = int(port) def port(self): return self._port def setPoll(self, poll): self._poll = int(poll) def setKeepAlive(self, keepAlive): self._keepAlive = max(int(keepAlive) + int(self._poll), self.kMinKeepAlive) def getClientId(self): return self._clientId def on_connect(self, client, obj,flags, rc): # paho # def on_connect(self, client, obj, rc): # mosquitto Log.debug("Connected " + str(rc)) if rc != mqtt.MQTT_ERR_SUCCESS: # paho # if rc != mqtt.MOSQ_ERR_SUCCESS: # mosquitto return self._connected = True self._attempts = 0 self._subscribed =[] self.onConnect(client, obj, rc) def restart(self): Log.debug("Restarting") if self.isRunning(): self._restarting = True self._thread.finished.connect(self.run) if not self._killing: self.kill() else: self.run() def on_disconnect(self, client, obj, rc): Log.debug("disconnecting rc: " + str(rc) + " " + str(self._connected)) if self._killing: Log.debug("killing") self._kill() self.onDisConnect(client, obj, rc) self._connected = False def onConnect(self, client, obj, rc): self.mqttOnConnect.emit(self, obj, rc) # QObject.emit(self, SIGNAL('mqttOnConnect'), self, obj, rc) pass def onDisConnect(self, client, obj, rc): self.mqttOnDisConnect.emit(self,obj,rc) # QObject.emit(self, SIGNAL('mqttOnDisConnect'), self, obj, rc) pass def onMessage(self, client, obj, msg): self.mqttOnMessage.emit(self,obj,msg) # QObject.emit(self, SIGNAL('mqttOnMessage'), self, obj, msg) # Log.debug('super ' + msg.topic+" "+str(msg.qos)+" "+str(msg.payload)) def onPublish(self, client, obj, mid): self.mqttOnPublish.emit(self,obj,mid) # QObject.emit(self._creator, SIGNAL('mqttOnPublish'), self, obj, mid) Log.debug("onPublish - Message ID: " + str(mid)) def onSubscribe(self, client, obj, mid, granted_qos): self.mqttOnSubscribe.emit(self, obj, mid, granted_qos) Log.info("Subscribed: " + str(mid) + " " + str(granted_qos)) def onLog(self, client, obj, level, msg): self.mqttOnLog.emit(msg, level) #Log.debug(string,level) def isConnected(self): return self.mqttc.socket() is not None and self._connected def publish(self, topic, payload, qos=0, retain=True): self.mqttc.publish(str(topic), payload, int(qos), retain) def subscribe(self, topic, qos =0): if self.isConnected() and not str(topic) in self._subscribed: try: self.mqttc.subscribe(str(topic), int(qos)) self._subscribed.append(str(topic)) Log.debug('Subscribed to ' + topic + " " + str(qos)) except Exception as e: Log.debug("Error on subscribe " + str(e)) raise e def unsubscribe(self, topic): if self.isConnected(): self.mqttc.unsubscribe(topic) Log.debug('Un_subscribed to ' + topic) def loop(self, timeout=0.1): if not self.isConnected(): if not self._killing: self._connect() return try: connResult = self.mqttc.loop(timeout) if connResult == mqtt.MQTT_ERR_SUCCESS: # paho # if connResult == mqtt.MOSQ_ERR_SUCCESS: # mosquitto return self._connected = False self._attempts += 1 Log.warn("MQTT: An error occurred while looping") self.mqttConnectionError.emit(self, mqtt.error_string(connResult)) except ValueError as e: if e == 'Invalid timeout.': self.mqttOnTimeout.emit(self, "Connection Timeout") else: Log.debug("Paho Client ValueError" + str(e)) except Exception as e: self.mqttConnectionError.emit(self, str(e)) Log.debug("MQTT Connect: Unknown exception raised " + str(e)) exc_type, exc_value, exc_traceback = sys.exc_info() Log.debug(repr(traceback.format_exception(exc_type, exc_value, exc_traceback))) def _kill(self): self._loopTimer.stop() # Stopped in self.stop but lets preempt this to avoid self.loop being called by running thread self._killTimer.stop() self._killing = False self._reset() # reset timer self.stop() def kill(self): try: if self.isConnected(): self._disconnect() self._killing = True self._killTimer.start(self._keepAlive) except Exception as e: Log.warn("Error cleaning up " + str(e)) pass def _connect(self): try: if not self._connected: if not self._attempts < self.kMaxAttempts: if not self._resetTimer.isActive(): Log.progress(Settings.getMeta("name") + ": Max connection attempts reached - waiting " + str(self.kResetTimer) + " seconds before retrying" ) self._resetTimer.start(self.kResetTimer*1000) # 1 minute parameterise return if self._attempts > 0 and (time.time() - pow(2,self._attempts +1)) < self._attemped: return Log.debug("Trying to connect") self._attemped = time.time() result = self.mqttc.connect(str(self._host), int(self._port),int( self._keepAlive), 1) self._connected = result == mqtt.MQTT_ERR_SUCCESS # paho # self._connected = result == mqtt.MOSQ_ERR_SUCCESS # mosquitto if not self._connected: self._attempts += 1 Log.progress(mqtt.connack_string(connResult)) self.mqttConnectionError.emit(self, mqtt.connack_string(connResult)) except Exception as e: msg = 'MQTT: ' + str(e) self.mqttConnectionError.emit(self, msg) #Log.progress(msg) Log.debug(msg) #exc_type, exc_value, exc_traceback = sys.exc_info() #Log.debug(repr(traceback.format_exception(exc_type, exc_value, # exc_traceback))) self._attempts += 1 self._connected = False def _disconnect(self): try: self.mqttc.disconnect() except Exception as e: Log.warn('MQTT Disconnection Error' + str(e)) def _reset(self): Log.warn("Timer reset ") self._attempts = 0 self._resetTimer.stop() # not required
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)
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 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 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()