def take_screenshot_pos(flags): loop = QEventLoop() screen_shot = Screenshot(flags) screen_shot.show() screen_shot.widget_closed.connect(loop.quit) loop.exec_() pos = screen_shot.target_img_pos return pos
def data_request(self, request, bparams): connection_loop = QEventLoop() QObject.connect(self.managerAccess, SIGNAL("finished( QNetworkReply* )"), connection_loop, SLOT("quit()")) reply = self.managerAccess.post(request, bparams) connection_loop.exec_() #sleep #reply->bytesAvailable(); #reply.deleteLater() return reply
def run_leave_animation(self): """ Runs the animation that represents execution leaving this item. Blocks until the animation is finished. """ loop = QEventLoop() animation = self.make_execution_leave_animation() animation.finished.connect(loop.quit) animation.start() if animation.state() == QParallelAnimationGroup.Running: loop.exec_()
def event_loop(msec): """Event loop to show the GUI during a unit test. https://www.qtcentre.org/threads/23541-Displaying-GUI-events-with-QtTest """ loop = QEventLoop() timer = QTimer() timer.timeout.connect(loop.quit) timer.setSingleShot(True) timer.start(msec) loop.exec_()
def _run_leave_animation(self, item_name, direction, engine_state): """ Runs the animation that represents execution leaving this item. Blocks until the animation is finished. """ if direction == ExecutionDirection.BACKWARD or engine_state != SpineEngineState.RUNNING: return loop = QEventLoop() animation = self._make_execution_leave_animation(item_name) animation.finished.connect(loop.quit) animation.start() if animation.state() == QParallelAnimationGroup.Running: loop.exec_()
def fix_pyside_exec(namespace): if namespace.get("__name__") == "AnyQt.QtWidgets": from PySide2.QtWidgets import QApplication, QDialog, QMenu if "exec" not in QApplication.__dict__: QApplication.exec = lambda self: QApplication.exec_() if not hasattr(QDialog, "exec"): QDialog.exec = lambda self: QDialog.exec_(self) if not hasattr(QMenu, "exec"): QMenu.exec = lambda self: QMenu.exec_(self) if namespace.get("__name__") == "AnyQt.QtGui": from PySide2.QtGui import QGuiApplication, QDrag if "exec" not in QGuiApplication.__dict__: QGuiApplication.exec = lambda self: QGuiApplication.exec_() if not hasattr(QDrag, "exec"): QDrag.exec = (lambda self, *args, **kwargs: QDrag.exec_( self, *args, **kwargs)) elif namespace.get("__name__") == "AnyQt.QtCore": from PySide2.QtCore import QCoreApplication, QEventLoop, QThread if not hasattr(QCoreApplication, "exec"): QCoreApplication.exec = lambda self: QCoreApplication.exec_() if not hasattr(QEventLoop, "exec"): QEventLoop.exec = (lambda self, *args, **kwargs: QEventLoop.exec_( self, *args, **kwargs)) if not hasattr(QThread, "exec"): QThread.exec = lambda self: QThread.exec_(self) elif namespace.get("__name__") == "AnyQt.QtPrintSupport": from PySide2.QtPrintSupport import QPageSetupDialog, QPrintDialog if "exec" not in QPageSetupDialog.__dict__: QPageSetupDialog.exec = lambda self: QPageSetupDialog.exec_(self) if "exec" not in QPrintDialog.__dict__: QPrintDialog.exec = lambda self: QPrintDialog.exec_(self)
class PlayerMoveSignal(QObject): def __init__(self, parent): super(PlayerMoveSignal, self).__init__() self.game = parent self.sig = Signal(str) self.eventLoop = QEventLoop(self) self.data = None @Slot(str) def stop_waiting(self, data): self.data = data self.eventLoop.exit() return def wait_for_move(self): self.eventLoop.exec_() return self.data
def open(self, url, timeout=60): """Wait for download to complete and return result""" loop = QEventLoop() timer = QTimer() timer.setSingleShot(True) timer.timeout.connect(loop.quit) self.loadFinished.connect(loop.quit) self.load(QUrl(url)) timer.start(timeout * 1000) loop.exec_() # delay here until download finished if timer.isActive(): # downloaded successfully timer.stop() return self.html() else: # timed out print('Request timed out:', url)
class EventLoop: def __init__(self, timeout_in_second: float = None): self.event = QEventLoop(None) self.timeout_in_second = timeout_in_second def quit(self): self.event.quit() def __enter__(self): return self def __exit__(self, *_): if self.timeout_in_second is not None: t = Timer() t.timeout.connect(self.quit) t.start(seconds=self.timeout_in_second) self.event.exec_()
def wait_signal(signal, timeout=1): """Block loop until signal emitted, or timeout (s) elapses.""" loop = QEventLoop() signal.connect(loop.quit) yield timed_out = [] if timeout is not None: def quit_with_error(): timed_out.append(1) loop.quit() QTimer.singleShot(timeout * 1000, quit_with_error) loop.exec_() if timed_out: assert False, "Timeout while waiting for %s" % signal
def open(self, url: str, timeout: int = 10): """Wait for download to complete and return result""" loop = QEventLoop() timer = QTimer() timer.setSingleShot(True) # noinspection PyUnresolvedReferences timer.timeout.connect(loop.quit) # noinspection PyUnresolvedReferences self.loadFinished.connect(loop.quit) self.load(QUrl(url)) # noinspection PyArgumentList timer.start(timeout * 1000) loop.exec_() # delay here until download finished if timer.isActive(): # downloaded successfully timer.stop() else: logger.info('Request timed out: %s' % url)
def _execute_forward(self, resources): """See base class.""" if not self._settings: return True absolute_paths = _files_from_resources(resources) absolute_path_settings = dict() for label in self._settings: absolute_path = absolute_paths.get(label) if absolute_path is not None: absolute_path_settings[absolute_path] = self._settings[label] source_settings = { "GdxConnector": { "gams_directory": self._gams_system_directory() } } # Collect arguments for the importer_program import_args = [ list(absolute_paths.values()), absolute_path_settings, source_settings, [ r.url for r in self._resources_from_downstream if r.type_ == "database" ], self._logs_dir, self._cancel_on_error, ] if not self._prepare_importer_program(import_args): self._logger.msg_error.emit( f"Executing Importer {self.name} failed.") return False self._importer_process.start_execution() loop = QEventLoop() self.importing_finished.connect(loop.quit) # Wait for finished right here loop.exec_() # This should be executed after the import process has finished if not self._importer_process_successful: self._logger.msg_error.emit( f"Executing Importer {self.name} failed.") else: self._logger.msg_success.emit( f"Executing Importer {self.name} finished") return self._importer_process_successful
def wait_signal(signal, timeout=5000): """Block loop until signal emitted, or timeout (ms) elapses.""" loop = QEventLoop() signal.connect(loop.quit) yield if timeout: timer = QTimer() timer.setInterval(timeout) timer.setSingleShot(True) timer.timeout.connect(loop.quit) timer.start() else: timer = None loop.exec_() signal.disconnect(loop.quit) if timer and timer.isActive(): timer.stop()
class ReadSerialPortsThread(QThread): """ Find connected serial ports and send it to GUI """ add_serial_port = Signal(str) remove_serial_port = Signal(str) def __init__(self, parent=None): QThread.__init__(self, parent) self.current_port = '' self.result = [] self.loop = "" if _platform.startswith('linux'): self.ports = ['/dev/ttyS%s' % (i + 1) for i in range(256)] elif _platform.startswith('win32'): self.ports = ['COM%s' % (i + 1) for i in range(256)] elif _platform.startswith('darwin'): self.ports = ['/dev/cu.serial%s' % (i + 1) for i in range(256)] def run(self): """ DOCSTRING """ while True: for port in self.ports: if port in self.result: try: serial_conn = serial.Serial(port) serial_conn.close() except (OSError, serial.SerialException): if port != self.current_port: self.result.remove(port) self.remove_serial_port.emit(port) else: try: serial_conn = serial.Serial(port) serial_conn.close() if port not in self.result: self.result.append(port) self.add_serial_port.emit(port) except (OSError, serial.SerialException): pass self.loop = QEventLoop() QTimer.singleShot(50, self.loop.quit) self.loop.exec_()
def testBlockingSignal(self): app = QCoreApplication.instance() or QCoreApplication([]) eventloop = QEventLoop() emitter = Emitter() receiver = Receiver(eventloop) emitter.connect(emitter, SIGNAL("signal(int)"), receiver.receive, Qt.BlockingQueuedConnection) emitter.start() retval = eventloop.exec_() emitter.wait(2000) self.assertEqual(retval, 0)
def testBlockingSignal(self): app = QCoreApplication.instance() or QCoreApplication([]) eventloop = QEventLoop() emitter = Emitter() receiver = Receiver(eventloop) emitter.connect(emitter, SIGNAL("signal(int)"), receiver.receive, Qt.BlockingQueuedConnection) emitter.start() retval = eventloop.exec_() emitter.wait() self.assertEqual(retval, 0)
def main(): app = QApplication([]) webview = QWebEngineView() loop = QEventLoop() webview.loadFinished.connect(loop.quit) webview.load(QUrl('http://example.webscraping.com/places/default/search')) loop.exec_() webview.show() frame = webview.page().mainFrame() frame.findFirstElement('#search_term').setAttribute('value', '.') frame.findFirstElement('#page_size option:checked').setPlainText('1000') frame.findFirstElement('#search').evaluateJavaScript('this.click()') elements = None while not elements: app.processEvents() elements = frame.findAllElements('#results a') countries = [e.toPlainText().strip() for e in elements] print(countries)
def _run_importer_program(self, args): """Starts and runs the importer program in a separate process. Args: args (list): List of arguments for the importer program """ self.importer_process = QProcess() self.importer_process.readyReadStandardOutput.connect(self._log_importer_process_stdout) self.importer_process.readyReadStandardError.connect(self._log_importer_process_stderr) self.importer_process.finished.connect(self.importer_process.deleteLater) program_path = os.path.abspath(importer_program.__file__) self.importer_process.start(sys.executable, [program_path]) self.importer_process.waitForStarted() self.importer_process.write(json.dumps(args).encode("utf-8")) self.importer_process.write(b'\n') self.importer_process.closeWriteChannel() if self.importer_process.state() == QProcess.Running: loop = QEventLoop() self.importer_process.finished.connect(loop.quit) loop.exec_() return self.importer_process.exitCode()
def _execute_forward(self, resources): """See base class.""" from_urls = self._urls_from_resources(resources) to_urls = self._urls_from_resources(self._resources_from_downstream) if not from_urls or not to_urls: # Moving on... return True combiner_args = [from_urls, to_urls, self._logs_dir, self._cancel_on_error] if not self._prepare_combiner_program(combiner_args): self._logger.msg_error.emit(f"Executing Combiner {self.name} failed.") return False self._combiner_process.start_execution() loop = QEventLoop() self.recombination_finished.connect(loop.quit) # Wait for finished right here loop.exec_() # This should be executed after the import process has finished if not self._combiner_process_successful: self._logger.msg_error.emit(f"Executing Combiner {self.name} failed.") else: self._logger.msg_success.emit(f"Executing Combiner {self.name} finished") return self._combiner_process_successful
def wait(): # loop = QEventLoop() # QTimer.singleShot(delay * 1000, loop.quit) # loop.exec_() loop = QEventLoop() timer = QTimer() timer.setSingleShot(True) timer.timeout.connect(loop.quit) if append: TIMER_RUNNING.append(timer) LOOP_RUNNING.append(loop) timer.start(delay * 1000) loop.exec_() TIMER_RUNNING.remove(timer) LOOP_RUNNING.remove(loop) else: timer.start(delay * 1000) loop.exec_()
def initCamera(self, resolution=(640, 480), monochrome=False, framerate=24, effect='none', use_video_port=True): # multiple streams are only possible eith the video port! self.msg(self.name + "Init: resolution = " + str(resolution)) self.camera.resolution = resolution self.camera.image_effect = effect self.camera.image_effect_params = (2, ) self.camera.iso = 100 # should force unity analog gain self.monochrome = monochrome # spoils edges self.camera.framerate = framerate if self.monochrome: self.rawCapture = PiYArray(self.camera, size=self.camera.resolution) # self.PreviewArray = PiYArray(self.camera, size=(640,480)) self.stream = self.camera.capture_continuous(self.rawCapture, format='yuv', use_video_port=True, splitter_port=0) # self.previewStream = self.camera.capture_continuous(output=self.PreviewArray, format='yuv', use_video_port=True, splitter_port=1, resize=(640,480)) else: self.rawCapture = PiRGBArray(self.camera, size=self.camera.resolution) # self.PreviewArray = PiRGBArray(self.camera, size=(640,480)) self.stream = self.camera.capture_continuous(self.rawCapture, format='bgr', use_video_port=True, splitter_port=0) # self.previewStream = self.camera.capture_continuous(output=self.PreviewArray, format='bgr', use_video_port=True, splitter_port=1, resize=(640,480)) GeneralEventLoop = QEventLoop(self) QTimer.singleShot(2, GeneralEventLoop.exit) GeneralEventLoop.exec_()
def test_duplicate_object_in_object_tree_model(self): data = dict() data["object_classes"] = ["fish", "dog"] data["relationship_classes"] = [("fish__dog", ("fish", "dog"))] data["objects"] = [("fish", "nemo"), ("dog", "pluto")] data["relationships"] = [("fish__dog", ("nemo", "pluto"))] data["object_parameters"] = [("fish", "color")] data["object_parameter_values"] = [("fish", "nemo", "color", "orange")] with mock.patch( "spinetoolbox.spine_db_manager.SpineDBManager.entity_class_icon" ) as mock_icon: mock_icon.return_value = None loop = QEventLoop() self.db_mngr.data_imported.connect(loop.quit) self.db_mngr.import_data({self.db_map: data}) loop.exec_() loop.deleteLater() mock_icon.assert_called() self.fetch_object_tree_model() root_item = self.spine_db_editor.object_tree_model.root_item fish_item = next( iter(item for item in root_item.children if item.display_data == "fish")) nemo_item = fish_item.child(0) with mock.patch( "spinetoolbox.spine_db_editor.widgets.tree_view_mixin.QInputDialog" ) as mock_input_dialog: mock_input_dialog.getText.side_effect = lambda *args, **kwargs: ( "nemo_copy", True) loop = QEventLoop() self.db_mngr.data_imported.connect(loop.quit) self.spine_db_editor.duplicate_object(nemo_item.index()) loop.exec_() loop.deleteLater() nemo_dupe = fish_item.child(1) self.assertEqual(nemo_dupe.display_data, "nemo_copy")
class Page(QWebEnginePage): def __init__(self, view): super(Page, self).__init__() self.parent = view.parent self.view = view self.result = None self.fullView = QWebEngineView() self.exitFSAction = QAction(self.fullView) self.loop = None def javaScriptConsoleMessage(self, level, msg, line, sourceid): """Override javaScriptConsoleMessage to use debug log.""" if level == QWebEnginePage.InfoMessageLevel: print("JS - INFO - Ligne {} : {}".format(line, msg)) elif level == QWebEnginePage.WarningMessageLevel: print("JS - WARNING - Ligne {} : {}".format(line, msg)) else: print("JS - ERROR - Ligne {} : {}".format(line, msg)) def hittestcontent(self, pos): return WebHitTestResult(self, pos) def maptoviewport(self, pos): return QPointF(pos.x(), pos.y()) def executejavascript(self, scriptsrc): self.loop = QEventLoop() self.result = None QTimer.singleShot(250, self.loop.quit) self.runJavaScript(scriptsrc, self.callbackjs) self.loop.exec_() self.loop = None return self.result def callbackjs(self, res): if self.loop is not None and self.loop.isRunning(): self.result = res self.loop.quit() def vsource(self): if "view-source:http" in self.url().toString(): self.load(QUrl(self.url().toString().split("view-source:")[1])) else: self.triggerAction(self.ViewSource) def makefullscreen(self, request): if request.toggleOn(): self.fullView = QWebEngineView() self.exitFSAction = QAction(self.fullView) self.exitFSAction.setShortcut(Qt.Key_Escape) self.exitFSAction.triggered.connect( lambda: self.triggerAction(self.ExitFullScreen)) self.fullView.addAction(self.exitFSAction) self.setView(self.fullView) self.fullView.showFullScreen() self.fullView.raise_() else: del self.fullView self.setView(self.view) request.accept()
class BatchProcessor(): signals = signal.signalClass() GeneralEventLoop = None SnapshotEventLoop = None SnapshotTaken = False is_active = False well_positioner = None Well_Map = None Well_Targets = None ID = str info = str duration = 0 interleave = 0 start_time = 0 end_time = 0 logging = True ## @brief BatchProcessor()::__init__ sets the batch settings ## @param well_controller is the well positioning class instance used to position the wells under the camera. ## @param well_map is the list of target wells with their positions in mm of both row and column. ## @param well_targets is the list of the target wells with their column and row index positions and ID. ## @param ID is the batch ID. ## @param info is the batch information. ## @param dur is the batch duration. ## @param interl is the time between the photographing of each well. def __init__(self, well_controller, well_map, well_targets, ID, info, path, dur, interl): #super().__init__() self.is_active = False self.well_positioner = well_controller self.Well_Map = well_map self.Well_Targets = well_targets self.batch_id = ID self.batch_info = info self.duration = dur self.interleave = interl self.path = os.path.sep.join([path, ID]) if not os.path.exists(self.path): os.makedirs(self.path) ## @brief BatchProcessor()::msg emits the message signal. This emit will be catched by the logging slot function in main.py. ## @param message is the string message to be emitted. @Slot(str) def msg(self, message): if message: self.signals.mes.emit(self.__class__.__name__ + ": " + str(message)) return ## @brief BatchProcessor()::updateBatchSettings(self, well_data, ID, info, dur, interl) can be called to update the batch settings during runtime. ## @todo well_data update in MainWindow. ## @depricated, BatchProcessor::updateBatchSettings not in use yet. Function might be usefull when updating the batch.ini via the GUI. ## @param well_data is the list of target wells with their positions/indexes. ## @param ID is the batch ID. ## @param info is the batch information. ## @param dur is the batch duration. ## @param interl is the time between the photographing of each well. def updateBatchSettings(self, well_map, well_targets, ID, info, dur, interl): self.is_active = False self.Well_Map = well_map self.Well_Targets = well_targets self.batch_id = ID self.batch_info = info self.duration = dur self.interleave = interl return ## @brief BatchProcessor()::startBatch(self) starts the batch process by setting the current (start)time and the endtime. ## It disables the manual motor control and emits the batch_active signal ## @todo call updateBatchSettings function @Slot() def startBatch(self): self.start_time = current_milli_time() self.end_time = current_milli_time() + (self.duration * 1000) self.well_positioner.reset_current_well() self.signals.batch_active.emit() self.is_active = True self.msg("Batch process initialized and started with:\n\tDuration: " + str(self.duration) + "\n\tInterleave: " + str(self.interleave) + "\n\tStart_time: " + str(self.start_time) + "\n\tEnd_time: " + str(self.end_time)) print("Batch process initialized and started with:\n\tDuration: " + str(self.duration) + "\n\tInterleave: " + str(self.interleave) + "\n\tStart_time: " + str(self.start_time) + "\n\tEnd_time: " + str(self.end_time)) self.runBatch() return ## @brief BatchProcessor()::runBatch(self) runs the batch after it is started. ## @note the interleave is actually the interleave + processingtime of the "for target in self.Well_Targets:" loop def runBatch(self): if self.logging: recording_file_name = os.path.sep.join( [self.path, 'batch_positioning_results.csv']) recording_file = open(recording_file_name, "w") # build csv file heading record_str = "run_start_time, run_time," for target in self.Well_Targets: record_str += ',' + str( self.Well_Map[target[0][1]][1][1]) + ',' + str( self.Well_Map[1][target[0][0]][0]) recording_file.write(record_str + "\n") else: recording_file = None first_run = True while True: print("BatchProcessor thread check: " + str(QThread.currentThread())) ## Run start time run_start_time = current_milli_time() actual_postions = [] # Home first on avery run self.well_positioner.stepper_control.homeXY() for target in self.Well_Targets: if not self.is_active: self.signals.batch_inactive.emit() self.msg("Batch stopped.") print("Batch stopped.") return else: row = target[0][0] column = target[0][1] self.msg("Target: " + str(target[0][2])) ## print("Target: at (" + str(self.Well_Map[column][1][1]) + ", " + str(self.Well_Map[1][row][0]) +")" + ", first run: " + str(first_run)) if (self.well_positioner.goto_well( self.Well_Map[column][1][1], self.Well_Map[1][row][0], first_run)): ## if found well self.snapshot_request( str(self.batch_id) + "/" + str(target[0][2])) (self.Well_Map[1][row][0], self.Well_Map[column][1][1] ) = self.well_positioner.get_current_well() print(" Target adapted to (" + str(self.Well_Map[column][1][1]) + ", " + str(self.Well_Map[1][row][0]) + ")") actual_postions.append( self.well_positioner.get_current_well()) while self.SnapshotTaken is False: if not self.is_active: self.signals.batch_inactive.emit() #self.msg("Batch stopped, breaking out of SnapshotTaken is False while loop.") print( "Batch stopped, breaking out of SnapshotTaken is False while loop." ) return print("Waiting in snapshot loop") self.wait_ms(50) self.msg(str(target) + " finished.") print(str(target) + " finished.") if self.end_time < current_milli_time(): self.msg("batch completed") print("batch completed") self.signals.batch_inactive.emit() self.stopBatch() return else: self.msg("Remaining time " + str(self.end_time - current_milli_time())) print("Remaining time " + str(self.end_time - current_milli_time()) + " ms") # first run is completed, remove this statemant to adapt to well-position in stead of feedforward if actual_postions: first_run = False run_time = current_milli_time() - run_start_time self.msg("Run time: " + str(run_time)) print("Run time: " + str(run_time) + "\nWaiting for " + str(self.interleave * 1000 - run_time) + " ms") if self.interleave * 1000 - run_time < 0: self.msg( "To short interleave, please increase the interleave to at least: " + str(run_time)) print( "To short interleave, please increase the interleave to at least: " + str(run_time)) else: ## Wait for the specified interleave minus the run_time of one run self.msg("Waiting for: " + str(self.interleave * 1000 - run_time) + " ms") print("Waiting for: " + str(self.interleave * 1000 - run_time) + " ms") self.wait_ms(self.interleave * 1000 - run_time) if recording_file: record_str = str(run_start_time) + ',' + str(run_time) for actual_position in actual_postions: record_str += ',' + str(actual_position[0]) + ',' + str( actual_position[1]) print(record_str) recording_file.write(record_str + "\n") if recording_file: recording_file.close() #self.msg("Breaking out batch process") print("Finishing batch process") return ## @brief BatchProcessor()::wait_ms(self, milliseconds) is a delay function. ## @param milliseconds is the number of milliseconds to wait. def wait_ms(self, milliseconds): self.GeneralEventLoop = QEventLoop() QTimer.singleShot(milliseconds, self.GeneralEventLoop.exit) self.GeneralEventLoop.exec_() return ## @brief BatchProcessor()::snapshot_request(self, message) emits the self.signals.snapshot_requested signal. ## @param message is the pathname of the desired snapshot def snapshot_request(self, message): self.msg("Requesting snapshot") self.signals.snapshot_requested.emit(message) self.snapshot_await() return ## @brief StepperWellPositioning()::snapshot_await(self) runs an eventloop while the new snapshot is not stored in self.image yet. def snapshot_await(self): self.SnapshotTaken = False while self.is_active and not self.SnapshotTaken: #self.msg("Waiting for snapshot") self.SnapshotEventLoop = QEventLoop() QTimer.singleShot(10, self.SnapshotEventLoop.exit) self.SnapshotEventLoop.exec_() return @Slot() def snapshot_confirmed(self): self.SnapshotTaken = True if not (self.SnapshotEventLoop is None): self.SnapshotEventLoop.exit() return ## @brief BatchProcessor()::stopBatch is a slot function which is called when the button stopBatch is pressed on the MainWindow GUI. ## It stops the batch process and resets the wait eventloop if running. @Slot() def stopBatch(self): #self.msg("Stopping Batch process") print("Stopping Batch process") self.signals.batch_inactive.emit() self.signals.process_inactive.emit( ) ## Used to stop positioning process of function StepperWellPositioning::goto_target self.is_active = False self.snapshot_confirmed() if not (self.GeneralEventLoop is None): self.GeneralEventLoop.exit() print("Exit BatchProcessor::GeneralEventloop") if not (self.SnapshotEventLoop is None): self.SnapshotEventLoop.exit() print("Exit BatchProcessor::SnapshotEventLoop") return @Slot() def close(self): self.stopBatch() return
def wait_ms(self, milliseconds): GeneralEventLoop = QEventLoop() QTimer.singleShot(milliseconds, GeneralEventLoop.exit) GeneralEventLoop.exec_() return
class StepperWellPositioning(): signals = signal.signalClass() process_activity = False stepper_control = None ## object StepperControl instance current_well_row = None current_well_column = None Stopped = True GeneralEventLoop = None SnapshotEventLoop = None SnapshotTaken = False image = np.ndarray image_area = None WPE = None WPE_target = None WPE_targetRadius = None Well_Map = None diaphragm_diameter = 12.0 ## mm ## @brief StepperWellPositioning()::__init__ initialises the stepper objects for X and Y axis and initialises the gcodeSerial to the class member variable. ## @param steppers is the StepperControl object representing the X- and Y-axis ## @param Well_data contains the target well specified by the user in the batch.ini file def __init__(self, steppers, Well_data, record_path): self.stepper_control = steppers self.Well_Map = Well_data log_fine_tuning = False if record_path is None else True self.path = record_path return ## @brief StepperWellPositioning()::msg emits the message signal. This emit will be catched by the logging slot function in main.py. ## @param message is the string message to be emitted. @Slot(str) def msg(self, message): if message is not None: self.signals.mes.emit(self.__class__.__name__ + ": " + str(message)) return ## @brief StepperWellPositioning()::reset_current_well(self) sets the current well position (XY) to None def reset_current_well(self): self.set_current_well(None, None) return ## @brief StepperWellPositioning()::set_current_well(self) sets the current well position. ## @param column is the column or x well coordinate ## @param row is the row or y well coordinate def set_current_well(self, column, row): self.current_well_column = column self.current_well_row = row return ## @brief StepperWellPositioning()::get_current_well(self) is the well position getter. ## @return current_well_column (x position in mm from home) and current_well_row (y position in mm from home). def get_current_well(self): if self.current_well_column == None or self.current_well_row == None: self.current_well_column = None self.current_well_row = None return self.current_well_column, self.current_well_row ## @brief StepperWellPositioning()::wait_ms(self, milliseconds) is a delay function. ## @param milliseconds is the number of milliseconds to wait. def wait_ms(self, milliseconds): GeneralEventLoop = QEventLoop() QTimer.singleShot(milliseconds, GeneralEventLoop.exit) GeneralEventLoop.exec_() return ## @brief StepperWellPositioning()::goto_well(self, row, column): ## @author Robin Meekers ## @author Gert van Lagen (ported to new prototype which makes use of the Wrecklab PrintHAT) @Slot() def goto_well(self, row, column, adapt_to_well=False): print("In goto_well function") print(" adapt to well is " + str(adapt_to_well)) ## self.stepper_control.enableMotors() self.signals.process_active.emit() self.Stopped = False self.stepper_control.setLightPWM(1.0) ## If the Well Position Evaluator is not initialized. if self.WPE is None: ## define the resolution of the received images to be entered into the evaluator. self.snapshot_request() self.snapshot_await() self.WPE_target = (int(self.image.shape[1] / 2), int(self.image.shape[0] / 2)) self.WPE = imageProcessor.WellPositionEvaluator( (self.image.shape[0], self.image.shape[1])) self.msg("Current well: " + str(self.get_current_well())) current_row, current_column = self.get_current_well() ## Position unknown if current_row is None or current_column is None: ## Need to go to home position first self.msg("Current position unknown. Moving to home...") self.stepper_control.homeXY() ## If homing is succeeded and confirmed by the STM of the Wrecklab PrintHAT if self.stepper_control.homing_confirmed: self.image = None self.snapshot_request() self.snapshot_await() self.wait_ms( 1000) ## Wait for the snapshot to be stored in self.image ## Arrived at home, move to first well. ## The exact centre of the image can more easily be determined at the home position, ## as there is no distortion in the image by light refracting. self.msg("Looking for light source...") WPE_Error = self.WPE.evaluate(self.image, (int( self.image.shape[1] / 2), int(self.image.shape[0] / 2))) self.msg("Found light-source at: (" + str(WPE_Error[0][0]) + " | " + str(WPE_Error[0][1]) + "). Radius: " + str(WPE_Error[2])) self.WPE_targetRadius = WPE_Error[2] self.WPE_target = (int(self.image.shape[1] / 2) + WPE_Error[0][0], int(self.image.shape[0] / 2) + WPE_Error[0][1]) self.msg("WPE_target: " + str(self.WPE_target[0]) + " | " + str(self.WPE_target[1])) self.signals.target_located.emit( (self.WPE_target[0], self.WPE_target[1], self.WPE_targetRadius)) self.WPE.circle_minRadius = int( self.WPE_targetRadius * 0.8 ) ## Roughly the amount remaining if the target is on the edge between wells. self.WPE.circle_maxRadius = int( self.WPE_targetRadius ) ## The well can never be larger than the diaphragm of the light source. ## ## Homing succeeded, light source found, lets move to the first target of Well_map ## self.set_current_well(column, row) ## self.stepper_control.moveToWell(column, row) self.signals.first_move.emit() else: return False ## position known if self.process_activity: # JV: column and row is probably X and Y in Robin's functions, also in Gert's functions? self.stepper_control.moveToWell(column, row) ## Wait for ms depending on moving distance, JV:why? if self.current_well_column is None or self.current_well_row is None: dist = 60 else: dist = math.sqrt( float( abs(float(column) - float(self.current_well_column)) * abs(float(column) - float(self.current_well_column))) + float( abs(float(row) - float(self.current_well_row)) * abs(float(row) - float(self.current_well_row)))) self.msg("Moving distance: " + str(dist)) if dist < 20: self.msg("Delay: 1999ms | dist: " + str(dist) + "mm") self.wait_ms(1999) elif dist < 50: self.msg("Delay: 3999ms | dist: " + str(dist) + "mm") self.wait_ms(3999) elif dist < 70: self.msg("Delay: 5999ms | dist: " + str(dist) + "mm") self.wait_ms(5999) elif dist < 85: self.msg("Delay: 8999ms | dist: " + str(dist) + "mm") self.wait_ms(8999) elif dist > 85: self.msg("Delay: 11999ms | dist: " + str(dist) + "mm") self.wait_ms(11999) else: self.msg("Delay: 4999ms | Unknown dist: " + str(dist) + "mm") self.wait_ms(4999) self.set_current_well(column, row) print( str(self.WPE_target[0]) + " | " + str(self.WPE_target[1]) + " first run " + str(adapt_to_well)) if adapt_to_well: self.goto_target() else: self.wait_ms( 2000) ## Wait for the snapshot to be stored in self.image self.msg("Well positioning succeeded.") ## Manual confirmation needed. STM is too fast for the software to catch the last confirmation. self.stepper_control.move_confirmed = True ## if not self.goto_target(): ## self.msg("Well positioning not succeeded.") ## self.set_current_well(None, None) ## Never reached reference point ## self.Stopped = True ## self.signals.process_inactive.emit() ## ## ## Manual confirmation needed. STM is too fast for the software to catch the last confirmation. ## self.stepper_control.move_confirmed = True ## return False ## else: ## self.msg("Well positioning succeeded.") ## ## Manual confirmation needed. STM is too fast for the software to catch the last confirmation. ## self.stepper_control.move_confirmed = True else: print( "Positioning process inactive, cannot call goto_target positioning controller function." ) self.msg( "Positioning process inactive, cannot call goto_target positioning controller function." ) ## Different row? if row != self.current_well_row or column != self.current_well_column: self.msg("moving from: (" + str(self.current_well_row) + " | " + str(self.current_well_column) + ") to: (" + str(row) + " | " + str(column) + ")") ## why can we not re-enable motors during steps? Because M17 is not implemented by Klipper ## self.stepper_control.disableMotors() self.stepper_control.setLightPWM(0.0) return True ## @brief StepperWellPositioning()::goto_target(self) evaluates the target circle using class Well_Position_Evaluator, updates the snapshot, ## and corrects the position of the well while not aligned with the light source. def goto_target(self): loops_ = 0 error_count = 0 WPE_Error = None new_column = 0 new_row = 0 error_threshold = 100 #50#120 # higher number means lower error... resolution = self.diaphragm_diameter / self.WPE_targetRadius # [mm/px] run_start_time = current_milli_time() if self.path is not None: column, row = self.get_current_well() log_file_name = '_'.join([ str(run_start_time), 'goto_target', str(round(column)), str(round(row)) ]) recording_file_name = os.path.sep.join([self.path, log_file_name]) recording_file = open(recording_file_name, "w") record_str = "run_time, WPE_target[0], WPE_target[1], WPE_Error[0][0], WPE_Error[0][1]" recording_file.write(record_str + "\n") else: recording_file = None ## Do while the well is not aligned with the light source. while True: if (self.stepper_control.move_confirmed): if not self.process_activity: #self.msg("!Returning from alignment controller loop in StepperWellPositioning::goto_target") print( "!Returning from alignment controller loop in StepperWellPositioning::goto_target" ) return False ## Wait for image to stabilize and request new snapshot self.wait_ms(2000) self.snapshot_request() ## Wait for snapshot to be stored in self.image self.wait_ms(1000) ## Evaluate current wellposition relative to the light source. WPE_Error = self.WPE.evaluate(self.image, self.WPE_target) self.msg("WPE target at (" + str(self.WPE_target[0]) + " | " + str(self.WPE_target[1]) + ")") ## Area error, surface smaller or equal to 0 if WPE_Error[1] <= 0: self.msg( "Possibly something wrong with captured frame, retry with another snapshot" ) error_count = error_count + 1 if (error_count >= 3): self.msg("Error_count = " + str(error_count)) return False continue else: self.msg("Well found at offset (" + str(WPE_Error[0][0]) + " | " + str(WPE_Error[0][1]) + ")") self.signals.well_located.emit( (self.WPE_target[0] + WPE_Error[0][0], self.WPE_target[1] + WPE_Error[0][1], WPE_Error[2])) # ## Some # error_count = 0 # if (abs(WPE_Error[0][0]-30) < (self.image.shape[0] / error_threshold) and abs(WPE_Error[0][1]+30) < (self.image.shape[0] / error_threshold)) or (loops_ >20): ## No more adjustments to make or system is oscilating # if loops_ > 20:loops_ # self.msg("More than 20 correction loops, return False") # return False # self.msg("Returning from positioner controller, loops: " + str(loops_)) # return True ## Define controller variables for column and row | x and y. ## new_column = float(WPE_Error[0][0]) / ((loops_+1)*20.0) ## new_row = float(WPE_Error[0][1]) / ((loops_+1)*15.0) ## column, row = self.get_current_well() run_time = current_milli_time() - run_start_time if recording_file is not None: record_str = str(run_time) + ',' + str( self.WPE_target[0]) + ',' + str( self.WPE_target[1]) + ',' + str( WPE_Error[0][0]) + ',' + str(WPE_Error[0][1]) recording_file.write(record_str + "\n") error = np.sqrt(WPE_Error[0][0]**2 + WPE_Error[0][1]**2) threshold = min(self.image.shape[0:1]) / error_threshold print(" well placement error: {}, while acceptable error:{}". format(error, threshold)) if error > threshold: # Decrease stepsize on each iteration for smooth convergence new_column = float( WPE_Error[0][0]) * resolution / (loops_ + 1) new_row = float( WPE_Error[0][1]) * resolution / (loops_ + 1) column, row = self.get_current_well() new_column += column new_row += row new_column = round(new_column, 1) new_row = round(new_row, 1) self.stepper_control.moveToWell(new_column, new_row) self.set_current_well(new_column, new_row) else: break loops_ += 1 if loops_ > 5: self.msg("Too many correction loops, giving up") break if not self.process_activity: #self.msg("Returning from alignment controller loop in StepperWellPositioning::goto_target") print( "Returning from alignment controller loop in StepperWellPositioning::goto_target" ) return False if recording_file: recording_file.close() return True ## @brief StepperWellPositioning()::snapshot_request(self) emits the snapshot_request signal def snapshot_request(self): self.signals.snapshot_requested.emit(str((1, 1))) ## @brief StepperWellPositioning()::snapshot_awiat(self) runs an eventloop while the new snapshot is not stored in self.image yet. def snapshot_await(self): self.SnapshotTaken = False while not self.Stopped and not self.SnapshotTaken: self.SnapshotEventLoop = QEventLoop() QTimer.singleShot(10, self.SnapshotEventLoop.exit) self.SnapshotEventLoop.exec_() ## @brief StepperWellPositioning()::snapshot_confirmed(self, snapshot) exits the event loop of snapshot_await when the new image is arived. ## It updates the self.image variable with the new variable and defines the image area and WPE_target. @Slot(np.ndarray) def snapshot_confirmed(self, snapshot): self.image = snapshot # self.WPE_target = (int(self.image.shape[1] / 2), int(self.image.shape[0] / 2)) self.image_area = int((self.image.shape[0] * self.image.shape[1])) self.SnapshotTaken = True if not (self.SnapshotEventLoop is None): self.SnapshotEventLoop.exit() return ## @brief StepperWellPositioning()::setProcessInactive(self) disables the positionings process if the signal is emitted. @Slot() def setProcessInactive(self): #self.msg("Disabled positioning process activity") print("Disabled positioning process activity") self.process_activity = False self.Stopped = True if not (self.GeneralEventLoop is None): self.GeneralEventLoop.exit() #self.msg("Exit StepperWellPositioning::GeneralEventloop") print("Exit StepperWellPositioning::GeneralEventloop") if not (self.SnapshotEventLoop is None): self.SnapshotEventLoop.exit() #self.msg("Exit StepperWellPositioning::SnapshotEventLoop") print("Exit StepperWellPositioning::SnapshotEventLoop") return ## @brief StepperWellPositioning()::setProcessActive(self) enables the positionings process if the signal is emitted. @Slot() def setProcessActive(self): self.process_activity = True self.Stopped = False return @Slot() def close(self): self.stepper_control.setLightPWM(0.0) self.stepper_control.setFanPWM(0.0) self.process_activity = False self.Stopped = True return
class MakeSerialConnection(QThread): """ Connect to selected serial port, receive data and send it to proper tabs """ start_btn_state = Signal(bool) stop_btn_state = Signal(bool) baud_box_state = Signal(bool) port_box_state = Signal(bool) board_box_state = Signal(bool) time_edit_state = Signal(bool) wifi_check_state = Signal(bool) blue_check_state = Signal(bool) serial_port_placeholder = Signal(str) serial_port_clear = Signal() send_text_signal = Signal(str) conn_stat_text = Signal(str) conn_stat_style = Signal(str) clear_wifi_list = Signal() clear_blue_list = Signal() append_wifi_list_item = Signal(QListWidgetItem) append_blue_list_item = Signal(QListWidgetItem) append_wifi_timeline_data = Signal(list) save_wifi_timeline_data = Signal(tuple) clear_wifi_series = Signal() add_wifi_series = Signal(QtCharts.QLineSeries) set_axis_y_series = Signal(QtCharts.QLineSeries) set_axis_x_series = Signal(QtCharts.QLineSeries) wifi_data_to_func = Signal(dict) wifi_data_to_func_graph = Signal(dict) wifi_data_to_func_time = Signal(dict) blue_data_to_func = Signal(dict) def __init__(self, parent=None): super(MakeSerialConnection, self).__init__(parent) self.current_port = '' self.current_baud = 9600 self.current_board = 'ESP32' self.current_time = 10000 self.wifi_scan_status = True self.blue_scan_status = False self.first_conn = True self.arduino = '' self.loop = '' self.reading = True self.data_collection = False self.blue_data_collection = False self.collected_data = {} self.collected_blue_data = {} self.default_array = [] self.default_tupple = () self.final_tuple = () self.save_data_to_db = False self.channels_occ = { 1: [1, 2], 2: [1, 2, 3], 3: [2, 3, 4], 4: [3, 4, 5], 5: [4, 5, 6], 6: [5, 6, 7], 7: [6, 7, 8], 8: [7, 8, 9], 10: [9, 10, 11], 11: [10, 11, 12], 12: [11, 12, 13], 13: [12, 13] } self.current_lang = 'English' def run(self): """ DOCSTRING """ if self.first_conn is True: self.arduino = serial.Serial(self.current_port, self.current_baud, write_timeout=1) self.disable_settings_options(False) self.send_config_to_esp() else: self.show_status_reconnect() self.disable_settings_options(False) self.loop = QEventLoop() QTimer.singleShot(5000, self.loop.quit) self.loop.exec_() self.arduino = serial.Serial(self.current_port, self.current_baud, write_timeout=1) self.send_config_to_esp() self.reading = True while self.reading is True: self.show_status_connected() try: data = self.arduino.readline()[:-1].decode(encoding='utf-8', errors="replace") if data.split() == ['[CFG]BOARD_ERROR']: self.show_status_wrong_board() self.send_text_signal.emit( 'Selected wrong board! Disconnected') self.stop_serial_communication() if data.split() == ['[SCN]WIFI_START_SCAN' ] or self.data_collection is True: if self.data_collection is True and data.split() != [ '[SCN]WIFI_STOP_SCAN' ]: if len(data) > 1: self.collected_data[str(data.split(" | ")[1])] = [ int(data.split(" | ")[2]), int(data.split(" | ")[3]), str(data.split(" | ")[4]), str(data.split(" | ")[5]), str(data.split(" | ")[6]) ] elif data.split() == ['[SCN]WIFI_STOP_SCAN']: self.data_collection = False self.wifi_data_to_func.emit(self.collected_data) else: self.collected_data = {} self.data_collection = True if data.split() == ['[SCN]BLUE_START_SCAN' ] or self.blue_data_collection is True: if (self.blue_data_collection is True and data.split() != ['[SCN]BLUE_STOP_SCAN']): if data.split()[0] == 'DEV': if len(data) > 1: # Data template: DEV | Name | RSSI | MAC | MSG self.collected_blue_data[str( data.split(" | ")[1])] = [ str(data.split(" | ")[2]), int(data.split(" | ")[3]), str(data.split(" | ")[4]) ] elif data.split() == ['[SCN]BLUE_STOP_SCAN']: self.blue_data_collection = False self.blue_data_to_func.emit(self.collected_blue_data) else: self.blue_data_collection = True self.send_text_signal.emit(data) except serial.SerialException: self.show_status_uart_error() self.send_text_signal.emit( 'Disconnect of USB->UART occured.\nRestart needed!') self.stop_serial_communication() self.arduino.close() def send_config_to_esp(self): """send config to ESP""" self.arduino.write("[CFG]START".encode()) self.arduino.write("\n".encode()) self.arduino.write( str("[CFG]BOARD=" + self.current_board + "").encode()) self.arduino.write("\n".encode()) self.arduino.write( str("[CFG]TIME=" + str(self.current_time) + "").encode()) self.arduino.write("\n".encode()) self.arduino.write( str("[CFG]WIFI_SCAN=" + str(self.wifi_scan_status).upper() + "").encode()) self.arduino.write("\n".encode()) self.arduino.write( str("[CFG]BLUE_SCAN=" + str(self.blue_scan_status).upper() + "").encode()) self.arduino.write("\n".encode()) self.arduino.write("[CFG]STOP\n".encode()) self.arduino.write("\n".encode()) self.arduino.write("[SCN]START".encode()) self.arduino.write("\n".encode()) def stop_serial_communication(self): """ Stop reading from serial port """ self.reading = False self.first_conn = False self.arduino.close() self.disable_settings_options(True) self.show_status_disconnected() def get_curr_port(self, port): """ Get selected serial port """ self.current_port = str(port) def get_curr_baud(self, baud): """ DOCSTRING """ self.current_baud = int(baud) def get_curr_board(self, board): """ DOCSTRING """ self.current_board = str(board) def get_curr_time(self, user_time): """ DOCSTRING """ self.current_time = ( int(user_time.toString("m:ss").split(":")[0]) * 60 + int(user_time.toString("m:ss").split(":")[1])) * 1000 def get_wifi_check(self, state): """ DOCSTRING """ self.wifi_scan_status = state def get_blue_check(self, state): """ DOCSTRING """ self.blue_scan_status = state def set_style_sheet(self, color): """ Set stylesheet for connection status label """ return "background: " + color + "; color: white; font-size: 14px; border-width: 1px; \ border-style: solid; border-radius: 2px; border-color: " + color + ";" def show_status_connected(self): """ Show connected status in MainWindow class """ self.start_btn_state.emit(False) self.stop_btn_state.emit(True) if self.current_lang == "Polski": self.conn_stat_text.emit('Połączono') else: self.conn_stat_text.emit('Connected') self.conn_stat_style.emit(self.set_style_sheet('green')) def show_status_disconnected(self): """ Show disconnected status in MainWindow class """ self.start_btn_state.emit(True) self.stop_btn_state.emit(False) if self.current_lang == "Polski": self.conn_stat_text.emit('Rozłączono') else: self.conn_stat_text.emit('Disconnected') self.conn_stat_style.emit(self.set_style_sheet('red')) def show_status_uart_error(self): """ Show UART error status in MainWindow """ self.start_btn_state.emit(False) self.stop_btn_state.emit(True) if self.current_lang == "Polski": self.conn_stat_text.emit('Rozłączono. Wymagany restart') else: self.conn_stat_text.emit( 'Disconnect of USB->UART occured. Need restart') self.conn_stat_style.emit(self.set_style_sheet('orange')) def show_status_wrong_board(self): """ Show selected wrong board status in MainWindow """ self.start_btn_state.emit(False) self.stop_btn_state.emit(True) if self.current_lang == "Polski": self.conn_stat_text.emit('Wybrano złą płytkę!') else: self.conn_stat_text.emit('Selected wrong board!') self.conn_stat_style.emit(self.set_style_sheet('red')) def show_status_reconnect(self): """ Show reconnect status in MainWindow """ self.start_btn_state.emit(False) self.stop_btn_state.emit(False) if self.current_lang == "Polski": self.conn_stat_text.emit('Ponowne łączenie... Czekaj 5s') else: self.conn_stat_text.emit('Reconnecting... Wait 5s') self.conn_stat_style.emit(self.set_style_sheet('orange')) @Slot(dict) def append_wifi_data(self, data): """ Append data to list in WiFi list tab """ self.clear_wifi_list.emit() i = 1 for i in data: if self.current_lang == "Polski": item = QListWidgetItem( 'BSID: ' + str(i) + ' \nKanał: ' + str(data[i][1]) + ' \nRSSI: ' + str(data[i][0]) + ' dBm \nMAC: ' + str(data[i][3]) + ' (Producent: ' + str(manu.search_manufacturer_by_mac(str(data[i][3]))) + ')') else: item = QListWidgetItem( 'BSID: ' + str(i) + ' \nChannel: ' + str(data[i][1]) + ' \nRSSI: ' + str(data[i][0]) + ' dBm \nMAC: ' + str(data[i][3]) + ' (Manufacturer: ' + str(manu.search_manufacturer_by_mac(str(data[i][3]))) + ')') if int(abs(data[i][0])) < 50: item.setIcon(QIcon('icons/icons8-no-connection-64-green.png')) elif int(abs(data[i][0])) >= 50 and int(abs(data[i][0])) < 70: item.setIcon(QIcon('icons/icons8-no-connection-64-blue.png')) elif int(abs(data[i][0])) >= 70 and int(abs(data[i][0])) < 80: item.setIcon(QIcon('icons/icons8-no-connection-64-orange.png')) elif int(abs(data[i][0])) >= 80 and int(abs(data[i][0])) < 90: item.setIcon(QIcon('icons/icons8-no-connection-64-red.png')) elif int(abs(data[i][0])) >= 90: item.setIcon(QIcon('icons/icons8-no-connection-64-black.png')) self.append_wifi_list_item.emit(item) self.loop = QEventLoop() QTimer.singleShot(50, self.loop.quit) self.loop.exec_() @Slot(dict) def append_blue_data(self, data): """ DOCSTRING """ self.clear_blue_list.emit() i = 1 for i in data: if self.current_lang == "Polski": item = QListWidgetItem('Nazwa: ' + str(i) + ' \nMAC: ' + str(data[i][0].upper()) + ' \nRSSI: ' + str(data[i][1]) + ' dBm\nProducent: ' + str( manu.search_manufacturer_by_mac( str(data[i][0].upper()))) + '') else: item = QListWidgetItem('Name: ' + str(i) + ' \nMAC: ' + str(data[i][0].upper()) + ' \nRSSI: ' + str(data[i][1]) + ' dBm\nManufacturer: ' + str( manu.search_manufacturer_by_mac( str(data[i][0].upper()))) + '') if int(abs(data[i][1])) < 50: item.setIcon(QIcon('icons/icons8-no-connection-64-green.png')) elif int(abs(data[i][1])) >= 50 and int(abs(data[i][1])) < 70: item.setIcon(QIcon('icons/icons8-no-connection-64-blue.png')) elif int(abs(data[i][1])) >= 70 and int(abs(data[i][1])) < 80: item.setIcon(QIcon('icons/icons8-no-connection-64-orange.png')) elif int(abs(data[i][1])) >= 80 and int(abs(data[i][1])) < 90: item.setIcon(QIcon('icons/icons8-no-connection-64-red.png')) elif int(abs(data[i][1])) >= 90: item.setIcon(QIcon('icons/icons8-no-connection-64-black.png')) self.append_blue_list_item.emit(item) self.loop = QEventLoop() QTimer.singleShot(50, self.loop.quit) self.loop.exec_() def disable_settings_options(self, state): """ DOCSTRING """ self.port_box_state.emit(state) self.baud_box_state.emit(state) self.board_box_state.emit(state) self.time_edit_state.emit(state) self.wifi_check_state.emit(state) self.blue_check_state.emit(state) if state is False: self.serial_port_placeholder.emit(self.current_port) else: self.serial_port_clear.emit() @Slot(dict) def append_data_to_wifi_graph(self, wifi_dict): """DOCSTRING""" self.clear_wifi_series.emit() for var_x in wifi_dict: self.line_series = QtCharts.QLineSeries() self.line_series.setName(str(var_x)) self.line_series.append( QPoint(2400 + (wifi_dict[var_x][1] * 5 - 4), -100)) self.line_series.append( QPoint(2411 + (wifi_dict[var_x][1] * 5 - 9), int(wifi_dict[var_x][0]))) self.line_series.append( QPoint(2411 + (wifi_dict[var_x][1] * 5 + 1), int(wifi_dict[var_x][0]))) self.line_series.append(2422 + (wifi_dict[var_x][1] * 5 - 4), -100) self.add_wifi_series.emit(self.line_series) self.set_axis_x_series.emit(self.line_series) self.set_axis_y_series.emit(self.line_series) self.loop = QEventLoop() QTimer.singleShot(50, self.loop.quit) self.loop.exec_() @Slot(dict) def append_data_to_wifi_timeline(self, wifi_dict): """ DOCSTRING """ self.default_array = [ -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100 ] for var_x in wifi_dict: self.append_lowest_rssi_to_array(wifi_dict[var_x][1], wifi_dict[var_x][0]) self.append_wifi_timeline_data.emit(self.default_array) self.default_tupple = tuple(self.default_array) self.final_tuple = (datetime.datetime.now().strftime('%H:%M:%S'), *self.default_tupple) self.save_wifi_timeline_data.emit(self.final_tuple) self.loop = QEventLoop() QTimer.singleShot(50, self.loop.quit) self.loop.exec_() def append_lowest_rssi_to_array(self, x, value): """ DOCSTRING """ for i in self.channels_occ[int(x)]: if self.default_array[i - 1] < value: self.default_array[i - 1] = value def enable_saving_to_db(self, state): """ DOCSTRING """ if state is True: self.save_data_to_db = True else: self.save_data_to_db = False def get_current_lang(self, lang): """ DOCSTRING """ self.current_lang = lang
def _wait_for_execution_finished(self): loop = QEventLoop() self.toolbox.project().project_execution_finished.connect(loop.quit) loop.exec_()
def _execute_forward(self, resources): """ Executes the Tool according to the Tool specification. Before launching the tool script in a separate instance, prepares the execution environment by creating all necessary directories and copying input files where needed. After execution archives the output files. Args: resources (list): a list of resources from direct predecessor items Returns: True if execution succeeded, False otherwise """ if self._tool_specification is None: self._logger.msg_warning.emit( f"Tool <b>{self.name}</b> has no Tool specification to execute" ) return False execution_dir = _execution_directory(self._work_dir, self._tool_specification) if execution_dir is None: return False if self._work_dir is not None: work_or_source = "work" # Make work directory anchor with path as tooltip work_anchor = ("<a style='color:#99CCFF;' title='" + execution_dir + "' href='file:///" + execution_dir + "'>work directory</a>") self._logger.msg.emit( f"*** Copying Tool specification <b>{self._tool_specification.name}" f"</b> source files to {work_anchor} ***") if not self._copy_program_files(execution_dir): self._logger.msg_error.emit( "Copying program files to base directory failed.") return False else: work_or_source = "source" # Make source directory anchor with path as tooltip anchor = ( f"<a style='color:#99CCFF;' title='{execution_dir}'" f"href='file:///{execution_dir}'>{work_or_source} directory</a>") self._logger.msg.emit( f"*** Executing Tool specification <b>{self._tool_specification.name}</b> in {anchor} ***" ) # Find required input files for ToolInstance (if any) if self._tool_specification.inputfiles: self._logger.msg.emit( "*** Checking Tool specification requirements ***") n_dirs, n_files = _count_files_and_dirs( self._tool_specification.inputfiles) if n_files > 0: self._logger.msg.emit( "*** Searching for required input files ***") file_paths = flatten_file_path_duplicates( self._find_input_files(resources), self._logger, log_duplicates=True) not_found = [k for k, v in file_paths.items() if v is None] if not_found: self._logger.msg_error.emit( f"Required file(s) <b>{', '.join(not_found)}</b> not found" ) return False self._logger.msg.emit( f"*** Copying input files to {work_or_source} directory ***" ) # Copy input files to ToolInstance work or source directory if not self._copy_input_files(file_paths, execution_dir): self._logger.msg_error.emit( "Copying input files failed. Tool execution aborted.") return False if n_dirs > 0: self._logger.msg.emit( f"*** Creating input subdirectories to {work_or_source} directory ***" ) if not self._create_input_dirs(execution_dir): # Creating directories failed -> abort self._logger.msg_error.emit( "Creating input subdirectories failed. Tool execution aborted." ) return False optional_file_copy_paths = dict() if self._tool_specification.inputfiles_opt: self._logger.msg.emit("*** Searching for optional input files ***") optional_file_paths = self._find_optional_input_files(resources) for k, v in optional_file_paths.items(): self._logger.msg.emit( f"\tFound <b>{len(v)}</b> files matching pattern <b>{k}</b>" ) optional_file_copy_paths = self._optional_output_destination_paths( optional_file_paths, execution_dir) self._copy_optional_input_files(optional_file_copy_paths) if not self._create_output_dirs(execution_dir): self._logger.msg_error.emit( "Creating output subdirectories failed. Tool execution aborted." ) return False input_database_urls = _database_urls_from_resources(resources) output_database_urls = _database_urls_from_resources( self._downstream_resources) self._tool_instance = self._tool_specification.create_tool_instance( execution_dir) try: self._tool_instance.prepare( list(optional_file_copy_paths.values()), input_database_urls, output_database_urls, self._cmd_line_args) except RuntimeError as error: self._logger.msg_error.emit( f"Failed to prepare tool instance: {error}") return False execution_token = _ExecutionToken(self, execution_dir) self._tool_instance.instance_finished.connect( execution_token.handle_execution_finished) self._logger.msg.emit( f"*** Starting instance of Tool specification <b>{self._tool_specification.name}</b> ***" ) # Wait for finished right here loop = QEventLoop() self._tool_instance.instance_finished.connect(loop.quit) self._tool_instance.execute() if self._tool_instance.is_running(): loop.exec_() return self._last_return_code == 0
class CFFEXSpider(QObject): spider_finished = Signal(str, bool) def __init__(self, *args, **kwargs): super(CFFEXSpider, self).__init__(*args, **kwargs) self.date = None self.event_loop = QEventLoop(self) # 用于网络请求同步事件阻塞 def set_date(self, date): self.date = datetime.strptime(date, '%Y-%m-%d') def get_daily_source_file(self): """ 获取每日行情数据源文件保存至本地 """ if self.date is None: raise DateValueError("请先使用`set_date`设置`CZCESpider`日期.") url = "http://www.cffex.com.cn/sj/hqsj/rtj/{}/{}/{}_1.csv".format( self.date.strftime('%Y%m'), self.date.strftime('%d'), self.date.strftime('%Y%m%d')) app = QApplication.instance() network_manager = getattr(app, "_network") request = QNetworkRequest(url=url) request.setHeader(QNetworkRequest.UserAgentHeader, random.choice(USER_AGENTS)) reply = network_manager.get(request) reply.finished.connect(self.daily_source_file_reply) def daily_source_file_reply(self): """ 获取日统计数据请求返回 """ reply = self.sender() if reply.error(): reply.deleteLater() self.spider_finished.emit("失败:" + str(reply.error()), True) return save_path = os.path.join( LOCAL_SPIDER_SRC, 'cffex/daily/{}.csv'.format(self.date.strftime("%Y-%m-%d"))) file_data = reply.readAll() file_obj = QFile(save_path) is_open = file_obj.open(QFile.WriteOnly) if is_open: file_obj.write(file_data) file_obj.close() reply.deleteLater() self.spider_finished.emit( "获取中金所{}日交易数据源文件成功!".format(self.date.strftime("%Y-%m-%d")), True) def get_rank_source_file(self): """ 获取日排名数据源文件 """ base_url = "http://www.cffex.com.cn/sj/ccpm/{}/{}/{}_1.csv" app = QApplication.instance() network_manager = getattr(app, "_network") for variety in VARIETY_LIST: url = base_url.format(self.date.strftime("%Y%m"), self.date.strftime("%d"), variety) self.spider_finished.emit("准备获取{}的日排名数据文件...".format(variety), False) request = QNetworkRequest(url=url) request.setHeader(QNetworkRequest.UserAgentHeader, random.choice(USER_AGENTS)) reply = network_manager.get(request) reply.finished.connect(self.rank_source_file_reply) time.sleep(1) self.event_loop.exec_() def rank_source_file_reply(self): """ 获取日排名请求返回 """ reply = self.sender() request_url = reply.request().url().url() # 解析出请求的品种 request_filename = request_url.rsplit("/", 1)[1] request_variety = request_filename.split("_")[0] if reply.error(): reply.deleteLater() self.spider_finished.emit( "获取{}排名数据文件。\n失败:{}".format(request_variety[:2], str(reply.error())), True) logger.error("获取{}排名数据文件失败了!".format(request_url[:2])) return save_path = os.path.join( LOCAL_SPIDER_SRC, 'cffex/rank/{}_{}.csv'.format(request_variety, self.date.strftime("%Y-%m-%d"))) file_data = reply.readAll() file_obj = QFile(save_path) is_open = file_obj.open(QFile.WriteOnly) if is_open: file_obj.write(file_data) file_obj.close() reply.deleteLater() tip = "获取中金所{}_{}日持仓排名数据保存到文件成功!".format( request_variety, self.date.strftime("%Y-%m-%d")) if request_variety == "T": tip = "获取中金所{}日所有品种持仓排名数据保存到文件成功!".format( self.date.strftime("%Y-%m-%d")) self.spider_finished.emit(tip, True) self.event_loop.quit()
def startAnimation(self): self.startFadeIn() loop = QEventLoop() self.animation.finished.connect(loop.quit) loop.exec_() QTimer.singleShot(1000, self.startFadeOut)