Пример #1
0
class AsyncThreadController(_AsyncAbstractController):
    def __init__(self,
      # See parent_.
      parent=None):

        super().__init__(parent)
        # Create a worker and a thread it runs in. This approach was
        # inspired by the example given in the QThread_ docs.
        self._worker = _AsyncWorker()
        self._workerThread = QThread(self)
        # Attach the worker to the thread's event queue.
        self._worker.moveToThread(self._workerThread)
        # Hook up signals.
        self._worker.startSignal.connect(self._worker.onStart)
        # Everything is ready. Start the worker thread, so it will
        # be ready for functions to run.
        self._workerThread.start()

    # See `_start`_.
    def _start(self, future):
        self._worker.startSignal.emit(future)

    # See `_terminate`_.
    def _terminate(self):
        # Shut down the thread the worker runs in.
        self._workerThread.quit()
        self._workerThread.wait()
        # Delete the worker, since it doesn't have a parent and therefore
        # won't be deleted by the Qt object tree.
        sip.delete(self._worker)
        del self._worker
Пример #2
0
class MetaDataCollector(QObject):

    sig_metadata_ready = pyqtSignal(Location, dict)
    sig_request_metadata = pyqtSignal(Location)
    sig_delete_metadatas = pyqtSignal(list)

    def __init__(self, vfs: StdioFilesystem) -> None:
        super().__init__()

        self._worker = MetaDataCollectorWorker(vfs)
        self._thread = QThread()
        self._worker.moveToThread(self._thread)

        self._thread.started.connect(self._worker.init)
        self.sig_request_metadata.connect(self._worker.on_metadata_requested)
        self.sig_delete_metadatas.connect(self._worker.on_delete_metadata_requested)
        self._worker.sig_metadata_ready.connect(self._on_metadata_ready)

        self._thread.start()

    def close(self):
        self._worker._close = True
        self._thread.quit()
        self._thread.wait()

    def request_metadata(self, location: Location) -> None:
        self.sig_request_metadata.emit(location)

    def request_delete_metadatas(self, locations: List[Location]) -> None:
        self.sig_delete_metadatas.emit(locations)

    def _on_metadata_ready(self, location: Location, metadata: Any):
        self.sig_metadata_ready.emit(location, metadata)
Пример #3
0
class MatcherUI(QDialog):
    thread_invoker = pyqtSignal()
    
    def __init__(self, argv, terminate=False):
        super(MatcherUI, self).__init__()

        self.btnStop = QPushButton('Stop')
        self.image_pane = QLabel()

        self.progress = QProgressBar(self)
        
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.btnStop)
        self.layout.addWidget(self.progress)
        self.layout.addWidget(self.image_pane)
        self.setLayout(self.layout)

        self.thread = QThread()
        self.thread.start()

        self.worker = Worker(argv)
        self.worker.moveToThread(self.thread)
        self.thread_invoker.connect(self.worker.task)
        self.thread_invoker.emit()
        
        self.worker.frameRendered.connect(self.updatePixmap)
        self.worker.finished.connect(self.finishIt)
        self.worker.progress.connect(self.progress.setValue)

        self.terminate = terminate
        self.btnStop.clicked.connect(lambda: self.worker.stop())
        self.btnStop.clicked.connect(self.terminateIt)
        self.terminated = False
        
    def updatePixmap(self, image):
        #it is called only when the pixmap is really updated by the thread.
        #resize image in advance.
        #w,h = image.width(), image.height()
        #scaled_image = image.scaled(int(w*self.preview_ratio), int(h*self.preview_ratio))
        pixmap = QPixmap.fromImage(image)
        self.image_pane.setPixmap(pixmap)
        #is it ok here?
        self.update()

    def terminateIt(self):
        self.close()
        if self.terminate:
            sys.exit(1)  #terminated
        self.terminated = True
        
    def finishIt(self):
        self.close()
        
    def closeEvent(self, event):
        self.stop_thread()
        
    def stop_thread(self):
        self.worker.stop()
        self.thread.quit()
        self.thread.wait()
Пример #4
0
def run(logger):
    menu = Menu()
    requests = queue.Queue()

    def resolve(command):
        command.execute(menu)

    with LinewiseSocket.context(socket_file) as sock:
        logger('listening on ' + socket_file + '\n')

        server_thread = QThread()
        server = Server(requests, logger=logger)
        server.moveToThread(server_thread)
        server_thread.started.connect(server.run)

        listener_thread = QThread()
        receiver = Listener(sock, requests)
        receiver.moveToThread(listener_thread)
        listener_thread.started.connect(receiver.run)

        server.dispatched.connect(resolve)
        menu.finished.connect(server_thread.exit)
        menu.finished.connect(listener_thread.exit)

        server_thread.start()
        logger('started server thread\n')

        listener_thread.start()
        logger('started receiver thread\n')

        return menu.exec_()
Пример #5
0
    def get_metadata(self, gal=None):
        thread = QThread(self)
        thread.setObjectName("App.get_metadata")
        fetch_instance = fetch.Fetch()
        if gal:
            galleries = [gal]
        else:
            if gui_constants.CONTINUE_AUTO_METADATA_FETCHER:
                galleries = [g for g in self.manga_list_view.gallery_model._data if not g.exed]
            else:
                galleries = self.manga_list_view.gallery_model._data
            if not galleries:
                self.notification_bar.add_text("Looks like we've already gone through all galleries!")
                return None
        fetch_instance.galleries = galleries

        self.notification_bar.begin_show()
        fetch_instance.moveToThread(thread)

        def done(status):
            self.notification_bar.end_show()
            fetch_instance.deleteLater()

        fetch_instance.GALLERY_PICKER.connect(self._web_metadata_picker)
        fetch_instance.GALLERY_EMITTER.connect(self.manga_list_view.replace_edit_gallery)
        fetch_instance.AUTO_METADATA_PROGRESS.connect(self.notification_bar.add_text)
        thread.started.connect(fetch_instance.auto_web_metadata)
        fetch_instance.FINISHED.connect(done)
        thread.finished.connect(thread.deleteLater)
        thread.start()
Пример #6
0
class PreviewParent(QObject):
    """ Class which communicates with the PlayerWorker Class (running on a separate thread) """

    # Signal when the frame position changes in the preview player
    def onPositionChanged(self, current_frame):
        self.parent.movePlayhead(current_frame)

    # Signal when the playback mode changes in the preview player (i.e PLAY, PAUSE, STOP)
    def onModeChanged(self, current_mode):
        log.info('onModeChanged')

    @pyqtSlot(object, object)
    def Init(self, parent, timeline, video_widget):
        # Important vars
        self.parent = parent
        self.timeline = timeline

        # Background Worker Thread (for preview video process)
        self.background = QThread(self)
        self.worker = PlayerWorker()  # no parent!

        # Init worker variables
        self.worker.Init(parent, timeline, video_widget)

        # Hook up signals to Background Worker
        self.worker.position_changed.connect(self.onPositionChanged)
        self.worker.mode_changed.connect(self.onModeChanged)
        self.background.started.connect(self.worker.Start)
        self.worker.finished.connect(self.background.quit)

        # Move Worker to new thread, and Start
        self.worker.moveToThread(self.background)
        self.background.start()
Пример #7
0
class AsyncThreadController(AsyncAbstractController):
    def __init__(self,
      # |parent|
      parent=None):

        super(AsyncThreadController, self).__init__(parent)
        # Create a worker and a thread it runs in. This approach was
        # inspired by  example given in the `QThread docs
        # <http://qt-project.org/doc/qt-4.8/qthread.html>`_.
        self._worker = _AsyncWorker()
        self._workerThread = QThread(parent)
        # Attach the worker to the thread's event queue.
        self._worker.moveToThread(self._workerThread)
        # Hook up signals.
        self._worker.startSignal.connect(self._worker.onStart)
        # Everything is ready. Start the worker thread, so it will
        # be ready for functions to run.
        self._workerThread.start()

    # |_start|
    def _start(self, future):
        self._worker.startSignal.emit(future)

    # |terminate|
    def _terminate(self):
        # Shut down the thread the Worker runs in.
        self._workerThread.quit()
        self._workerThread.wait()
        # Finally, detach (and probably garbage collect) the objects
        # used by this class.
        del self._worker
        del self._workerThread
Пример #8
0
	def start_msg_thread(self, runningObjectParam, onRun = None, onDone = None, onErrorMsg = None, onSuccessMsg = None ):
		
		if self.running:
			return
					
		self.running = True
		
		thr = QThread()
		thrName = 'EndicataLabToolMainWnd start_msg_thread[%d]' % (len( self.threadPool ) + 1)
		print thrName
		thr.setObjectName( thrName )
		self.threadPool.append( thr )
		
		self.runningObject = runningObjectParam
		self.runningObject.moveToThread(thr)
		if onRun:
			thr.started.connect( onRun )
		else:
			thr.started.connect( self.runningObject.run )

		self.runningObject.finished.connect( self.onUnlock )
		
		if onDone:
			self.runningObject.done.connect( onDone )
		if onErrorMsg:
			self.runningObject.errorMsg.connect( onErrorMsg )
		if onSuccessMsg:
			self.runningObject.successMsg.connect( onSuccessMsg )
		thr.start()
Пример #9
0
class MeasurementThread(QObject):
    specSignal = pyqtSignal(np.ndarray)
    progressSignal = pyqtSignal(float, str)
    finishSignal = pyqtSignal(np.ndarray)

    def __init__(self, spectrometer, parent=None):
        if getattr(self.__class__, '_has_instance', False):
            RuntimeError('Cannot create another instance')
        self.__class__._has_instance = True
        try:
            super(MeasurementThread, self).__init__(parent)
            self.spectrometer = spectrometer
            self.abort = False
            self.thread = QThread(parent)
            self.moveToThread(self.thread)
            self.thread.started.connect(self.process)
            self.thread.finished.connect(self.stop)
        except:
            (type, value, traceback) = sys.exc_info()
            sys.excepthook(type, value, traceback)

    def start(self):
        self.thread.start(QThread.HighPriority)

    @pyqtSlot()
    def stop(self):
        self.abort = True
        self.thread.quit()
        self.thread.wait(5000)


    def __del__(self):
        self.__class__.has_instance = False
        #try:
        #    self.specSignal.disconnect()
        #    self.progressSignal.disconnect()
        #    self.finishSignal.disconnect()
        #except:
        #    (type, value, traceback) = sys.exc_info()
        #    sys.excepthook(type, value, traceback)

    def work(self):
        self.specSignal.emit(self.spec)

    @pyqtSlot()
    def process(self):
        while not self.abort:
            try:
                self.spec = self.spectrometer.intensities()
                self.spec = self.spec[0:1024]
                self.work()
            except:
                (type, value, traceback) = sys.exc_info()
                print(type)
                print(value)
                print(traceback)
                sys.excepthook(type, value, traceback)
Пример #10
0
                            def add_gallery(gallery_list):
                                def append_to_model(x):
                                    self.manga_list_view.gallery_model.insertRows(x, None, len(x))

                                class A(QObject):
                                    done = pyqtSignal()
                                    prog = pyqtSignal(int)

                                    def __init__(self, obj, parent=None):
                                        super().__init__(parent)
                                        self.obj = obj
                                        self.galleries = []

                                    def add_to_db(self):
                                        gui_constants.NOTIF_BAR.begin_show()
                                        gui_constants.NOTIF_BAR.add_text("Populating database...")
                                        for y, x in enumerate(self.obj):
                                            gui_constants.NOTIF_BAR.add_text(
                                                "Populating database {}/{}".format(y + 1, len(self.obj))
                                            )
                                            gallerydb.add_method_queue(gallerydb.GalleryDB.add_gallery_return, False, x)
                                            self.galleries.append(x)
                                            y += 1
                                            self.prog.emit(y)
                                        append_to_model(self.galleries)
                                        gui_constants.NOTIF_BAR.end_show()
                                        self.done.emit()

                                loading.progress.setMaximum(len(gallery_list))
                                a_instance = A(gallery_list)
                                thread = QThread(self)
                                thread.setObjectName("Database populate")

                                def loading_show():
                                    loading.setText("Populating database.\nPlease wait...")
                                    loading.show()

                                def loading_hide():
                                    loading.close()
                                    self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.emit()

                                def del_later():
                                    try:
                                        a_instance.deleteLater()
                                    except NameError:
                                        pass

                                a_instance.moveToThread(thread)
                                a_instance.prog.connect(loading.progress.setValue)
                                thread.started.connect(loading_show)
                                thread.started.connect(a_instance.add_to_db)
                                a_instance.done.connect(loading_hide)
                                a_instance.done.connect(del_later)
                                thread.finished.connect(thread.deleteLater)
                                thread.start()
Пример #11
0
	def populate(self):
		"Populates the database with series from local drive'"
		msgbox = QMessageBox()
		msgbox.setText("<font color='red'><b>Use with care.</b></font> Choose a folder containing all your series'.")
		msgbox.setInformativeText("Oniichan, are you sure you want to do this?")
		msgbox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
		msgbox.setDefaultButton(QMessageBox.No)
		if msgbox.exec() == QMessageBox.Yes:
			path = QFileDialog.getExistingDirectory(None, "Choose a folder containing your series'")
			if len(path) is not 0:
				data_thread = QThread()
				loading_thread = QThread()
				loading = misc.Loading()

				if not loading.ON:
					misc.Loading.ON = True
					fetch_instance = fetch.Fetch()
					fetch_instance.series_path = path
					loading.show()

					def finished(status):
						if status:
							self.manga_list_view.series_model.populate_data()
							# TODO: make it spawn a dialog instead (from utils.py or misc.py)
							if loading.progress.maximum() == loading.progress.value():
								misc.Loading.ON = False
								loading.hide()
							data_thread.quit
						else:
							loading.setText("<font color=red>An error occured. Try restarting..</font>")
							loading.progress.setStyleSheet("background-color:red")
							data_thread.quit

					def fetch_deleteLater():
						try:
							fetch_instance.deleteLater
						except NameError:
							pass

					def thread_deleteLater(): #NOTE: Isn't this bad?
						data_thread.deleteLater

					def a_progress(prog):
						loading.progress.setValue(prog)
						loading.setText("Searching on local disk...\n(Will take a while on first time)")

					fetch_instance.moveToThread(data_thread)
					fetch_instance.DATA_COUNT.connect(loading.progress.setMaximum)
					fetch_instance.PROGRESS.connect(a_progress)
					data_thread.started.connect(fetch_instance.local)
					fetch_instance.FINISHED.connect(finished)
					fetch_instance.FINISHED.connect(fetch_deleteLater)
					fetch_instance.FINISHED.connect(thread_deleteLater)
					data_thread.start()
Пример #12
0
def make_transfer_dialog() -> TransferDialog:
    dialog = TransferDialog("/home/juser/Target Directory", None)

    thread = QThread(dialog)
    worker = TransferDialogTest(dialog, dialog)
    worker.moveToThread(thread)
    thread.started.connect(worker.on_started)
    thread.start()

    global g_keep_alive
    g_keep_alive += [worker, thread]

    return dialog
Пример #13
0
    def start_up(self):
        def normalize_first_time():
            settings.set(2, "Application", "first time level")

        def done():
            self.manga_list_view.gallery_model.init_data()
            if gui_constants.ENABLE_MONITOR and gui_constants.MONITOR_PATHS and all(gui_constants.MONITOR_PATHS):
                self.init_watchers()
            if gui_constants.FIRST_TIME_LEVEL != 2:
                normalize_first_time()

        if gui_constants.FIRST_TIME_LEVEL < 2:

            class FirstTime(file_misc.BasePopup):
                def __init__(self, parent=None):
                    super().__init__(parent)
                    main_layout = QVBoxLayout()
                    info_lbl = QLabel(
                        "Hi there! Some big changes are about to occur!\n"
                        + "Please wait.. This will take at most a few minutes.\n"
                        + "If not then try restarting the application."
                    )
                    info_lbl.setAlignment(Qt.AlignCenter)
                    main_layout.addWidget(info_lbl)
                    prog = QProgressBar(self)
                    prog.setMinimum(0)
                    prog.setMaximum(0)
                    prog.setTextVisible(False)
                    main_layout.addWidget(prog)
                    main_layout.addWidget(QLabel("Note: This popup will close itself when everything is ready"))
                    self.main_widget.setLayout(main_layout)

            ft_widget = FirstTime(self)
            log_i("Invoking first time level 2")
            bridge = gallerydb.Bridge()
            thread = QThread(self)
            thread.setObjectName("Startup")
            bridge.moveToThread(thread)
            thread.started.connect(bridge.rebuild_galleries)
            bridge.DONE.connect(ft_widget.close)
            bridge.DONE.connect(self.setEnabled)
            bridge.DONE.connect(done)
            bridge.DONE.connect(bridge.deleteLater)
            thread.finished.connect(thread.deleteLater)
            thread.start()
            ft_widget.adjustSize()
            ft_widget.show()
            self.setEnabled(False)
        else:
            done()
Пример #14
0
class WorkerThread(QObject):

    sig_close_requested = pyqtSignal()

    def __init__(self) -> None:
        super().__init__()

        self._worker: Optional[Worker] = None
        self._thread: Optional[QThread] = None

    def set_worker(self, worker: Worker):
        """Sets the Worker associated with this thread. Note this function has
        to be called before connecting any signals, otherwise the
        signals won't be associated with the right thread.

        """
        assert self._worker is None

        self._worker = worker
        self._thread = QThread()
        self._worker.moveToThread(self._thread)

        self._thread.started.connect(self._worker.on_thread_started)

        # close() is a blocking connection so the thread is properly
        # done after the signal was emit'ed and we don't have to fuss
        # around with sig_finished() and other stuff
        self.sig_close_requested.connect(self._worker.close, type=Qt.BlockingQueuedConnection)

    def start(self) -> None:
        assert self._worker is not None
        assert self._thread is not None

        self._thread.start()

    def is_running(self) -> bool:
        assert self._thread is not None

        return bool(self._thread.isRunning())

    def close(self) -> None:
        assert self._thread is not None
        assert self._worker is not None
        assert self._worker._close is False, "WorkerThread.close() was called twice"

        self._worker._close = True
        self.sig_close_requested.emit()
        self._thread.quit()
        self._thread.wait()
Пример #15
0
class WorkerFacade(QObject):

    sig_thing_requested = pyqtSignal(str, types.FunctionType)
    sig_quit_requested = pyqtSignal()

    def __init__(self, name):
        super().__init__()
        self.quit = False
        self.name = name

        self.worker = Worker("worker-" + name)
        self.thread = QThread()
        self.worker.moveToThread(self.thread)

        # startup and shutdown
        self.thread.started.connect(self.worker.init)
        self.sig_quit_requested.connect(self.worker.deinit)
        self.worker.sig_finished.connect(self.thread.quit)

        # requests and receive
        self.worker.sig_thing_finished.connect(self.on_thing_finished)
        self.sig_thing_requested.connect(self.worker.on_thing_requested)

        self.thread.start()

    def request_thing(self, thing, callback):
        self.worker.thing_requested(thing, callback)
        # self.sig_thing_requested.emit(self.name + "-" + thing, callback)

    def on_thing_finished(self, thing, callback):
        callback(thing)

    def on_quit(self):
        if self.worker.quit:
            return

        print(self.name, "WorkerFacade.on_quit()")
        # This signal won't be received until all work is done!!!!
        self.sig_quit_requested.emit()

        # Thus notify worker via sidechannel to stop doing it's job
        self.worker.quit = True

        # waiting for the thread to finish
        print(self.name, "evdi waiting")
        evdi = QAbstractEventDispatcher.instance()
        while self.thread.wait(10) is False:
            evdi.processEvents(QEventLoop.AllEvents)
        print(self.name, "evdi waiting: done")
Пример #16
0
class FinderUi(QWidget):
    """
        User Interface functions
    """
    def __init__(self):
        super(FinderUi, self).__init__()

        # Load UI file from Qt Designer
        uic.loadUi(find_data_file("finder.ui"), self)        
        
        # Setup the UDP listener in it's own thread
        self.finder = BCSFinder()
        self.thread = QThread()
        self.finder.moveToThread(self.thread)
        self.thread.started.connect(self.finder.listen)
        self.thread.finished.connect(app.exit)
        self.thread.start()
        
        # Connect to slots
        self.pushButton.clicked.connect(self.ping)
        self.finderTable.cellDoubleClicked.connect(self.open)
        self.finder.found.connect(self.located)
        

    def ping(self):
        # Clear table
        while(self.finderTable.rowCount() > 0):
            self.finderTable.removeRow(0)

        # Send ping
        self.finder.ping()
    
    def open(self, row, col):
        # When double clicked, open default web browser to the BCS
        webbrowser.open('http://{}/'.format(self.finderTable.item(row, 0).text()))
        
    def located(self, bcs):
        # Found a BCS, add a row to the table
        row = self.finderTable.rowCount()
        self.finderTable.insertRow(row)
        if bcs['port'] != 0 and bcs['port'] != 80:
            self.finderTable.setItem(row, 0, QTableWidgetItem(bcs['address'] + ":" + str(bcs['port'])))
        else:
            self.finderTable.setItem(row, 0, QTableWidgetItem(bcs['address']))
        self.finderTable.setItem(row, 1, QTableWidgetItem(bcs['name']))
        self.finderTable.setItem(row, 2, QTableWidgetItem(bcs['mac']))
        self.finderTable.setItem(row, 3, QTableWidgetItem(bcs['version']))
        self.finderTable.setItem(row, 4, QTableWidgetItem(bcs['type']))
        self.finderTable.resizeColumnsToContents()
Пример #17
0
class PreviewParent(QObject):
    """ Class which communicates with the PlayerWorker Class (running on a separate thread) """

    # Signal when the frame position changes in the preview player
    def onPositionChanged(self, current_frame):
        self.parent.movePlayhead(current_frame)

        # Check if we are at the end of the timeline
        if self.worker.player.Mode() == openshot.PLAYBACK_PLAY and current_frame >= self.worker.timeline_length and self.worker.timeline_length != -1:
            # Yes, pause the video
            self.parent.actionPlay.trigger()
            self.worker.timeline_length = -1

    # Signal when the playback mode changes in the preview player (i.e PLAY, PAUSE, STOP)
    def onModeChanged(self, current_mode):
        log.info('onModeChanged')

    @pyqtSlot(object, object)
    def Init(self, parent, timeline, video_widget):
        # Important vars
        self.parent = parent
        self.timeline = timeline

        # Background Worker Thread (for preview video process)
        self.background = QThread(self)
        self.worker = PlayerWorker()  # no parent!

        # Init worker variables
        self.worker.Init(parent, timeline, video_widget)

        # Hook up signals to Background Worker
        self.worker.position_changed.connect(self.onPositionChanged)
        self.worker.mode_changed.connect(self.onModeChanged)
        self.background.started.connect(self.worker.Start)
        self.worker.finished.connect(self.background.quit)

        # Connect preview thread to main UI signals
        self.parent.previewFrameSignal.connect(self.worker.previewFrame)
        self.parent.refreshFrameSignal.connect(self.worker.refreshFrame)
        self.parent.LoadFileSignal.connect(self.worker.LoadFile)
        self.parent.PlaySignal.connect(self.worker.Play)
        self.parent.PauseSignal.connect(self.worker.Pause)
        self.parent.SeekSignal.connect(self.worker.Seek)
        self.parent.SpeedSignal.connect(self.worker.Speed)
        self.parent.StopSignal.connect(self.worker.Stop)

        # Move Worker to new thread, and Start
        self.worker.moveToThread(self.background)
        self.background.start()
Пример #18
0
	def web_metadata(self, url, btn_widget, pgr_widget):
		self.link_lbl.setText(url)
		f = fetch.Fetch()
		thread = QThread(self)
		thread.setObjectName('Gallerydialog web metadata')
		btn_widget.hide()
		pgr_widget.show()

		def status(stat):
			def do_hide():
				try:
					pgr_widget.hide()
					btn_widget.show()
				except RuntimeError:
					pass

			if stat:
				do_hide()
			else:
				danger = """QProgressBar::chunk {
					background: QLinearGradient( x1: 0, y1: 0, x2: 1, y2: 0,stop: 0 #FF0350,stop: 0.4999 #FF0020,stop: 0.5 #FF0019,stop: 1 #FF0000 );
					border-bottom-right-radius: 5px;
					border-bottom-left-radius: 5px;
					border: .px solid black;}"""
				pgr_widget.setStyleSheet(danger)
				QTimer.singleShot(3000, do_hide)
			f.deleteLater()

		def gallery_picker(gallery, title_url_list, q):
			self.parent_widget._web_metadata_picker(gallery, title_url_list, q, self)

		try:
			dummy_gallery = self.make_gallery(self.gallery)
		except AttributeError:
			dummy_gallery = self.make_gallery(gallerydb.Gallery(), False)
		if not dummy_gallery:
			status(False)
			return None

		dummy_gallery.link = url
		f.galleries = [dummy_gallery]
		f.moveToThread(thread)
		f.GALLERY_PICKER.connect(gallery_picker)
		f.GALLERY_EMITTER.connect(self.set_web_metadata)
		thread.started.connect(f.auto_web_metadata)
		thread.finished.connect(thread.deleteLater)
		f.FINISHED.connect(status)
		thread.start()
Пример #19
0
						def add_gallery(gallery_list):
							def append_to_model(x):
								self.manga_list_view.sort_model.insertRows(x, None, len(x))
								self.manga_list_view.sort_model.init_search(
									self.manga_list_view.sort_model.current_term)

							class A(QObject):
								done = pyqtSignal()
								prog = pyqtSignal(int)
								def __init__(self, obj, parent=None):
									super().__init__(parent)
									self.obj = obj
									self.galleries = []

								def add_to_db(self):
									for y, x in enumerate(self.obj):
										gallerydb.add_method_queue(
											gallerydb.GalleryDB.add_gallery_return, False, x)
										self.galleries.append(x)
										y += 1
										self.prog.emit(y)
									append_to_model(self.galleries)
									self.done.emit()
							loading.progress.setMaximum(len(gallery_list))
							self.a_instance = A(gallery_list)
							thread = QThread(self)
							thread.setObjectName('Database populate')
							def loading_show(numb):
								if loading.isHidden():
									loading.show()
								loading.setText('Populating database ({}/{})\nPlease wait...'.format(
									numb, loading.progress.maximum()))
								loading.progress.setValue(numb)
								loading.show()

							def loading_hide():
								loading.close()
								self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.emit()

							self.a_instance.moveToThread(thread)
							self.a_instance.prog.connect(loading_show)
							thread.started.connect(self.a_instance.add_to_db)
							self.a_instance.done.connect(loading_hide)
							self.a_instance.done.connect(self.a_instance.deleteLater)
							#a_instance.add_to_db()
							thread.finished.connect(thread.deleteLater)
							thread.start()
class QThreadedWorker(QObject):
    def __init__(self):
        super().__init__()
        self.thread = QThread()

        self.moveToThread(self.thread)
        self.thread.start()

        # Thread initialisation
        self.finished.connect(self.thread.quit)
        self.finished.connect(self.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)

    finished = pyqtSignal()

    def finish(self):
        self.finished.emit()
Пример #21
0
    def _check_update(self):
        class upd_chk(QObject):
            UPDATE_CHECK = pyqtSignal(str)

            def __init__(self, **kwargs):
                super().__init__(**kwargs)

            def fetch_vs(self):
                import requests
                import time

                try:
                    log_d("Checking Update")
                    time.sleep(1.5)
                    r = requests.get(
                        "https://raw.githubusercontent.com/Pewpews/happypanda/master/VS.txt", verify="cacert.pem"
                    )
                    a = r.text
                    vs = a.strip()
                    self.UPDATE_CHECK.emit(vs)
                except:
                    log.exception("Checking Update: FAIL")
                    self.UPDATE_CHECK.emit("this is a very long text which is is sure to be over limit")

        def check_update(vs):
            log_i("Received version: {}\nCurrent version: {}".format(vs, gui_constants.vs))
            if vs != gui_constants.vs:
                if len(vs) < 10:
                    self.notification_bar.add_text(
                        "Version {} of Happypanda is".format(vs) + " available. Click here to update!", False
                    )
                    self.notification_bar.clicked.connect(
                        lambda: utils.open_web_link("https://github.com/Pewpews/happypanda/releases")
                    )
                    self.notification_bar.set_clickable(True)
                else:
                    self.notification_bar.add_text("An error occurred while checking for new version")

        self.update_instance = upd_chk()
        thread = QThread(self)
        self.update_instance.moveToThread(thread)
        thread.started.connect(self.update_instance.fetch_vs)
        self.update_instance.UPDATE_CHECK.connect(check_update)
        self.update_instance.UPDATE_CHECK.connect(self.update_instance.deleteLater)
        thread.finished.connect(thread.deleteLater)
        thread.start()
Пример #22
0
class TrackerGui(MainUi):
 
    def update_cam_list(self):
        cam_list = [str(cam) for cam in PyETCore.inst.cameras.values()]
        self.eye_cam_combo_box.clear()
        self.seeing_cam_combo_box.clear()
        self.eye_cam_combo_box.addItems(cam_list)
        self.seeing_cam_combo_box.addItems(cam_list)
        
    def update_res_list(self):
        self.eye_res_combo_box.clear()
        self.seeing_res_combo_box.clear()
        if PyETCore.inst.eye_cam:
            r_list = PyETCore.inst.eye_cam.str_resolutions
            self.eye_res_combo_box.addItems(r_list)
        if PyETCore.inst.seeing_cam:
            r_list = PyETCore.inst.seeing_cam.str_resolutions
            self.seeing_res_combo_box.addItems(r_list)
    
    def setup_ui(self, MainWindow):
        super().setupUi(MainWindow)
        self.f_updater = FrameUpdater()
        self.thread = QThread()
        self.f_updater.eye_frame_ready.connect(self.on_frame_ready)
        self.f_updater.seeing_frame_ready.connect(self.on_frame_ready)
        self.f_updater.moveToThread(self.thread)
        self.thread.started.connect(self.f_updater.process)
        self.thread.start()
        
        self.update_cam_list()
        self.update_res_list()
        
        self.start_cap_btn.released.connect(PyETCore.inst.record)
        self.stop_cap_btn.released.connect(PyETCore.inst.stop_record)

    def on_frame_ready(self, pixmap, cam_type):
        pixmap = pixmap.scaled(self.cam_view.size(), Qt.KeepAspectRatio)
        if cam_type is 1:
            self.cam_view_2.setPixmap(pixmap)
        elif cam_type is 2:
            self.cam_view.setPixmap(pixmap)
        app = QtCore.QCoreApplication.instance()
        app.processEvents()
Пример #23
0
class SearchStream(QObject):

    sig_close_requested = pyqtSignal()

    def __init__(self, abspath: str, pattern: str) -> None:
        super().__init__()
        self._worker = SearchStreamWorker(abspath, pattern)
        self._thread = QThread(self)
        self._worker.moveToThread(self._thread)

        self._thread.started.connect(self._worker.init)

        # close() is a blocking connection so the thread is properly
        # done after the signal was emit'ed and we don't have to fuss
        # around with sig_finished() and other stuff
        self.sig_close_requested.connect(self._worker.close, type=Qt.BlockingQueuedConnection)

    def start(self) -> None:
        self._thread.start()

    def close(self) -> None:
        assert self._worker._close is False
        self._worker._close = True
        self.sig_close_requested.emit()
        self._thread.quit()
        self._thread.wait()

    @property
    def sig_file_added(self):
        return self._worker.sig_file_added

    @property
    def sig_finished(self):
        return self._worker.sig_finished

    @property
    def sig_message(self):
        return self._worker.sig_message

    @property
    def sig_error(self):
        return self._worker.sig_error
Пример #24
0
    def start_pool_workers(self, files_list):
        try:
            for file in files_list:
                worker = WorkerYandexUpload(os.path.normpath(file), self.settings_dialog.getAPIKey())
                thread = QThread()

                list_widget_item = self.add_list_widget_item()
                worker.progress_bar.connect(self.listWidget.itemWidget(list_widget_item).set_progress)
                worker.name.connect(self.listWidget.itemWidget(list_widget_item).set_name)
                worker.status.connect(self.listWidget.itemWidget(list_widget_item).set_info)
                worker.hide_progress_bar.connect(self.listWidget.itemWidget(list_widget_item).hide_progress)
                worker.set_icon.connect(self.listWidget.itemWidget(list_widget_item).set_pixmap)
                # worker.send_email.connect(self.send_mail)  # TODO разлочить

                worker.moveToThread(thread)
                thread.started.connect(worker.run)
                thread.start()

                logging.debug('Создан новый работник')
        except Exception as e:
            logging.debug(e)
Пример #25
0
class WebJumpPrompt(Prompt):
    label = "url/webjump:"
    complete_options = {
        "autocomplete": True,
    }
    history = PromptHistory()

    ask_completions = Signal(object, str, str)
    force_new_buffer = False

    def completer_model(self):
        data = []
        for name, w in WEBJUMPS.items():
            if w.allow_args:
                name = name
            data.append((name, w.doc))
        return PromptTableModel(data)

    def enable(self, minibuffer):
        Prompt.enable(self, minibuffer)
        self.new_buffer = PromptNewBuffer(self.force_new_buffer)
        self.new_buffer.enable(minibuffer)
        minibuffer.input().textEdited.connect(self._text_edited)
        self._wc_model = QStringListModel()
        self._wb_model = minibuffer.input().completer_model()
        self._cthread = QThread(app())
        self._creceiver = CompletionReceiver()
        self._creceiver.moveToThread(self._cthread)
        self._completion_timer = 0
        self._active_webjump = None
        self._cthread.finished.connect(self._cthread.deleteLater)
        self.ask_completions.connect(self._creceiver.get_completions)
        self._creceiver.got_completions.connect(self._got_completions)
        self._cthread.start()

    def _text_edited(self, text):
        model = self._wb_model
        for name, w in WEBJUMPS.items():
            if w.allow_args and w.complete_fn:
                name = name
                if text.startswith(name):
                    model = self._wc_model
                    self._active_webjump = (w, name)
                    if self._completion_timer != 0:
                        self.killTimer(self._completion_timer)
                    self._completion_timer = self.startTimer(10)
                    break
        if self.minibuffer.input().completer_model() != model:
            self.minibuffer.input().popup().hide()
            self.minibuffer.input().set_completer_model(model)

    def timerEvent(self, _):
        text = self.minibuffer.input().text()
        w, name = self._active_webjump
        self.ask_completions.emit(w, name, text[len(name):])
        self.killTimer(self._completion_timer)
        self._completion_timer = 0

    @Slot(list)
    def _got_completions(self, data):
        self._wc_model.setStringList(data)
        self.minibuffer.input().show_completions()

    def close(self):
        Prompt.close(self)
        self._cthread.quit()
        # not sure if those are required;
        self._wb_model.deleteLater()
        self._wc_model.deleteLater()

    def get_buffer(self):
        return self.new_buffer.get_buffer()
Пример #26
0
class MainWindown(QMainWindow):
    load_data_ready = pyqtSignal(object)

    def __init__(self):
        self.PosData = None
        self.Clusterer = None
        self.show_visual = False
        self.show_bg = False
        self.pos_vis = None
        self.clustering_vis = None
        self.point_size = None

        self.pool = Pool(processes=1)  # experimenting

        self.app = QApplication([])
        QMainWindow.__init__(self)
        ui_fname = 'main_window.ui'
        self.ui = uic.loadUi(ui_fname)

        self.ui.button_data_fname.clicked.connect(self.button_open_data_file)
        self.ui.button_range_fname.clicked.connect(self.button_open_range_file)
        self.ui.button_load_data.clicked.connect(self.button_load_data_click)
        self.ui.button_show_ion_map.clicked.connect(
            self.button_show_ion_map_click)
        self.ui.button_run.clicked.connect(self.button_run_click)
        self.ui.button_terminate.clicked.connect(self.button_terminate_click)
        self.ui.button_show_visual.clicked.connect(
            self.button_show_visual_click)
        self.ui.button_show_visual.setEnabled(False)
        self.ui.button_save_output.clicked.connect(self.button_save_click)
        self.ui.groupBox_expert.hide()
        self.ui.checkBox_show_bg.stateChanged.connect(self.checked_bg)
        self.ui.checkBox_show_expert.stateChanged.connect(self.checked_expert)

        # self.my_stream = MyStream()
        # self.my_stream.message.connect(self.on_my_stream_message)
        # sys.stdout = self.my_stream # basically overide the stdout method with my_stream, and output it to the text box.

        self.ui.show()
        sys.exit(self.app.exec())

    @pyqtSlot(int, name='checked_bg')
    def checked_bg(self, state):
        if state == Qt.Checked:
            self.show_bg = True
        else:
            self.show_bg = False

    @pyqtSlot(int, name='checked_expert')
    def checked_expert(self, state):
        if state == Qt.Checked:
            self.ui.groupBox_expert.show()
        else:
            self.ui.groupBox_expert.hide()

    @pyqtSlot(str, name='on_myStream_message')
    def on_my_stream_message(self, message):
        self.ui.textBrowser.append(message)

    @pyqtSlot(name='button_open_data_file')
    def button_open_data_file(self):
        fname = QFileDialog.getOpenFileName(self, 'Open Pos File', os.getcwd(),
                                            "Pos files (*.pos)")
        self.ui.le_data_fname.setText(fname[0])

    @pyqtSlot(name='button_open_range_file')
    def button_open_range_file(self):
        fname = QFileDialog.getOpenFileName(self, 'Open Range File',
                                            os.getcwd(),
                                            "Range files (*.rrng, *.RRNG)")
        self.ui.le_rrng_fname.setText(fname[0])

    @pyqtSlot(name='button_load_data_click')
    def button_load_data_click(self):
        data_fname = self.ui.le_data_fname.text()
        rrng_fname = self.ui.le_rrng_fname.text()
        self.point_size = int(self.ui.le_point_size.text())
        self.ui.textBrowser.setText('Data file loaded: ' + data_fname + '\n' +
                                    'Range file loaded: ' + rrng_fname + '\n')
        self.ui.textBrowser.append('Loadeding...')

        self.DataLoader = APTDataLoader(data_fname, rrng_fname)
        self.thread_data_loader = QThread()
        self.DataLoader.data_ready.connect(self.get_load_data)
        self.DataLoader.finished.connect(self.thread_data_loader.quit)
        self.DataLoader.moveToThread(self.thread_data_loader)
        self.thread_data_loader.started.connect(self.DataLoader.load_APT_data)
        self.thread_data_loader.start()

    @pyqtSlot(object, name='get_load_data')
    def get_load_data(self, data):
        if data == None:
            self.ui.textBrowser.append(
                'Invalid filenames. Please make sure filenames are correct and files located in current folder.'
            )
        else:
            self.PosData = data
            self.ui.textBrowser.append('Loaded Success.\n')

    @pyqtSlot(name='button_show_ion_map_click')
    def button_show_ion_map_click(self):
        self.ui.textBrowser.append('Processing...\n')
        self.pos_vis = APTDataViewer(self.PosData,
                                     point_size=self.point_size,
                                     max_ion=2000)

    @pyqtSlot(name='button_run_click')
    def button_run_click(self):
        ion_types = self.ui.le_ion_types.text()
        ion_types = ion_types.split(', ')
        method = self.ui.le_method.text()
        eps = float(self.ui.le_eps.text())
        minpts = int(self.ui.le_minpts.text())
        min_cluster_size = int(self.ui.le_min_cluster_size.text(
        )) if self.ui.le_min_cluster_size.text() != 'None' else minpts

        rho = float(self.ui.le_density.text())
        det_eff = float(self.ui.le_det_eff.text())
        con = float(self.ui.le_solute_con.text())

        k = int(
            self.ui.le_k.text()) if self.ui.le_k.text() != 'None' else minpts
        significant_separation = float(
            self.ui.le_significant_separation.text())
        cluster_member_prob = float(self.ui.le_cluster_member_prob.text())
        min_node_size = int(self.ui.le_min_node_size.text(
        )) if self.ui.le_min_node_size.text() != 'None' else minpts
        est_bg = float(self.ui.le_est_bg.text()
                       ) if self.ui.le_est_bg.text() != 'None' else np.inf
        node_similarity = float(self.ui.le_similarity.text())

        if self.PosData == None:
            self.ui.textBrowser.append('Please Load Data First!\n')
        else:
            self.ui.textBrowser.append('Clustering started...')

            try:
                clustering_parameters = {
                    'ion_types': ion_types,
                    'method': method,
                    'eps': eps,
                    'minpts': minpts,
                    'min_node_size': min_node_size,
                    'significant_separation': significant_separation,
                    'k': k,
                    'node_similarity': node_similarity,
                    'cluster_member_prob': cluster_member_prob,
                    'est_bg': est_bg,
                    'min_cluster_size': min_cluster_size
                }

                self.DoClustering = DoClustering(self.PosData,
                                                 clustering_parameters)
                self.thread_do_clustering = QThread()
                self.DoClustering.cluster_ready.connect(
                    self.get_clustering_result)
                self.DoClustering.finished.connect(
                    self.thread_do_clustering.quit)
                self.DoClustering.moveToThread(self.thread_do_clustering)
                self.thread_do_clustering.started.connect(
                    self.DoClustering.cluster_analysis)
                self.thread_do_clustering.start()
            except:
                self.ui.textBrowser.append(
                    'Something is wrong in cluster analysis setting, please check again...'
                )

    @pyqtSlot(object, name='get_clustering_result')
    def get_clustering_result(self, data):
        self.Clusterer = data
        self.ui.textBrowser.append('Clustering finished.\n')
        self.ui.button_show_visual.setEnabled(True)

    @pyqtSlot(name='button_terminate_click')
    def button_terminate_click(self):
        self.thread_do_clustering.quit()
        self.ui.textBrowser.append('Cluster analysis terminated!\n')

    @pyqtSlot(name='button_show_visual_click')
    def button_show_visual_click(self):
        self.clustering_vis = ClusteringView(self.Clusterer, self.show_bg,
                                             self.point_size)

    @pyqtSlot(name='button_save_click')
    def button_save_click(self):
        output_filename = self.ui.le_output_fname.text()

        try:
            if self.Clusterer.RD is not None:
                self.Clusterer.write_RD_to_file(output_filename + '_ol_RD_CD')

            self.Clusterer.cluster_stats(output_filename + '_cluster_stats')
            self.Clusterer.write_cluster_to_file(output_filename + '_clusters')
            self.Clusterer.write_indexed_cluster_rrng(output_filename +
                                                      '_clusters')
            self.Clusterer.write_log(output_filename + '_log')
        except:
            self.ui.textBrowser.append('Invalid output filename. Try again.')
Пример #27
0
class ThreadlinkUtility(QMainWindow):
    """Main class for the PCBA Test Utility.
    
    Creates main window for the program, the file menu, status bar, and the 
    settings/configuration window.
    """
    def __init__(self):
        super().__init__()
        self.system_font = QApplication.font().family()
        self.label_font = QFont(self.system_font, 12)
        self.config_font = QFont(self.system_font, 12)
        self.config_path_font = QFont(self.system_font, 12)

        self.settings = QSettings("BeadedStream", "Threadlink TestUtility")

        settings_defaults = {
            "user_id": "",
            "port1_tac_id": "",
            "hex_files_path": "/path/to/hex/files",
            "report_dir_path": "/path/to/report/folder",
            "atprogram_file_path": "/path/to/atprogram.exe"
        }

        for key in settings_defaults:
            if not self.settings.value(key):
                self.settings.setValue(key, settings_defaults[key])

        self.sm = serialmanager.SerialManager()
        self.serial_thread = QThread()
        self.sm.moveToThread(self.serial_thread)
        self.serial_thread.start()

        self.m = model.Model()
        self.r = report.Report()

        self.sm.port_unavailable_signal.connect(self.port_unavailable)

        # Part number : [serial prefix, procedure class]
        self.product_data = {
            "45211-01": ["THL", threadlink.Threadlink],
        }
        # Create program actions.
        self.config = QAction("Settings", self)
        self.config.setShortcut("Ctrl+E")
        self.config.setStatusTip("Program Settings")
        self.config.triggered.connect(self.configuration)

        self.quit = QAction("Quit", self)
        self.quit.setShortcut("Ctrl+Q")
        self.quit.setStatusTip("Exit Program")
        self.quit.triggered.connect(self.close)

        self.about_tu = QAction("About Threadlink Utility", self)
        self.about_tu.setShortcut("Ctrl+U")
        self.about_tu.setStatusTip("About Program")
        self.about_tu.triggered.connect(self.about_program)

        self.aboutqt = QAction("About Qt", self)
        self.aboutqt.setShortcut("Ctrl+I")
        self.aboutqt.setStatusTip("About Qt")
        self.aboutqt.triggered.connect(self.about_qt)

        # Create menubar
        self.menubar = self.menuBar()
        self.file_menu = self.menubar.addMenu("&File")
        self.file_menu.addAction(self.config)
        self.file_menu.addAction(self.quit)

        self.serial_menu = self.menubar.addMenu("&Serial")
        self.serial_menu.installEventFilter(self)
        self.ports_menu = QMenu("&Ports", self)
        self.serial_menu.addMenu(self.ports_menu)
        self.ports_menu.aboutToShow.connect(self.populate_ports)
        self.ports_group = QActionGroup(self)
        self.ports_group.triggered.connect(self.connect_port)

        self.help_menu = self.menubar.addMenu("&Help")
        self.help_menu.addAction(self.about_tu)
        self.help_menu.addAction(self.aboutqt)

        self.initUI()
        self.center()

    def center(self):
        """Centers the application on the screen the mouse pointer is
        currently on."""
        frameGm = self.frameGeometry()
        screen = QApplication.desktop().screenNumber(
            QApplication.desktop().cursor().pos())
        centerPoint = QApplication.desktop().screenGeometry(screen).center()
        frameGm.moveCenter(centerPoint)
        self.move(frameGm.topLeft())

    def resource_path(self, relative_path):
        """Gets the path of the application relative root path to allow us
        to find the logo."""
        if hasattr(sys, '_MEIPASS'):
            return os.path.join(sys._MEIPASS, relative_path)
        return os.path.join(os.path.abspath("."), relative_path)

    def initUI(self):
        """"Sets up the UI."""
        RIGHT_SPACING = 350
        LINE_EDIT_WIDTH = 200
        self.central_widget = QWidget()

        self.tester_id_lbl = QLabel("Please enter tester ID: ")
        self.tester_id_lbl.setFont(self.label_font)
        self.pcba_pn_lbl = QLabel("Please select PCBA part number: ")
        self.pcba_pn_lbl.setFont(self.label_font)
        self.pcba_sn_lbl = QLabel("Please enter or scan DUT serial number: ")
        self.pcba_sn_lbl.setFont(self.label_font)

        self.tester_id_input = QLineEdit()
        self.tester_id_input.setText(self.settings.value("user_id"))
        self.pcba_sn_input = QLineEdit()
        self.tester_id_input.setFixedWidth(LINE_EDIT_WIDTH)
        self.pcba_sn_input.setFixedWidth(LINE_EDIT_WIDTH)

        self.pcba_pn_input = QComboBox()
        self.pcba_pn_input.addItem("45211-01")
        self.pcba_pn_input.setFixedWidth(LINE_EDIT_WIDTH)

        self.start_btn = QPushButton("Start")
        self.start_btn.setFixedWidth(200)
        self.start_btn.setAutoDefault(True)
        self.start_btn.clicked.connect(self.parse_values)

        self.logo_img = QPixmap(self.resource_path("h_logo.png"))
        self.logo_img = self.logo_img.scaledToWidth(600)
        self.logo = QLabel()
        self.logo.setPixmap(self.logo_img)

        hbox_logo = QHBoxLayout()
        hbox_logo.addStretch()
        hbox_logo.addWidget(self.logo)
        hbox_logo.addStretch()

        hbox_test_id = QHBoxLayout()
        hbox_test_id.addStretch()
        hbox_test_id.addWidget(self.tester_id_lbl)
        hbox_test_id.addWidget(self.tester_id_input)
        hbox_test_id.addSpacing(RIGHT_SPACING)

        hbox_pn = QHBoxLayout()
        hbox_pn.addStretch()
        hbox_pn.addWidget(self.pcba_pn_lbl)
        hbox_pn.addWidget(self.pcba_pn_input)
        hbox_pn.addSpacing(RIGHT_SPACING)

        hbox_sn = QHBoxLayout()
        hbox_sn.addStretch()
        hbox_sn.addWidget(self.pcba_sn_lbl)
        hbox_sn.addWidget(self.pcba_sn_input)
        hbox_sn.addSpacing(RIGHT_SPACING)

        hbox_start_btn = QHBoxLayout()
        hbox_start_btn.addStretch()
        hbox_start_btn.addWidget(self.start_btn)
        hbox_start_btn.addSpacing(RIGHT_SPACING)

        vbox = QVBoxLayout()
        vbox.addStretch()
        vbox.addLayout(hbox_logo)
        vbox.addSpacing(100)
        vbox.addLayout(hbox_test_id)
        vbox.addSpacing(50)
        vbox.addLayout(hbox_pn)
        vbox.addSpacing(50)
        vbox.addLayout(hbox_sn)
        vbox.addSpacing(50)
        vbox.addLayout(hbox_start_btn)
        vbox.addStretch()

        self.central_widget.setLayout(vbox)
        self.setCentralWidget(self.central_widget)
        self.setFixedSize(WINDOW_WIDTH, WINDOW_HEIGHT)
        self.setWindowTitle(
            "BeadedStream Manufacturing Threadlink Test Utility")

    def create_messagebox(self, type, title, text, info_text):
        """A helper method for creating message boxes."""
        msgbox = QMessageBox(self)
        msgbox.setWindowTitle(title)
        msgbox.setText(text)
        msgbox.setInformativeText(info_text)
        if type == "Warning":
            msgbox.setIcon(QMessageBox.Warning)
        elif type == "Error":
            msgbox.setIcon(QMessageBox.Error)
        elif type == "Information":
            msgbox.setIcon(QMessageBox.Information)
        else:
            raise InvalidMsgType
        return msgbox

    def about_program(self):
        """Displays information about the program."""
        QMessageBox.about(self, "About Threadlink Test Utility", ABOUT_TEXT)

    def about_qt(self):
        """Displays information about Qt."""
        QMessageBox.aboutQt(self, "About Qt")

    def populate_ports(self):
        """Populates ports menu from connected COM ports."""
        ports = serialmanager.SerialManager.scan_ports()
        self.ports_menu.clear()

        if not ports:
            self.ports_menu.addAction("None")
            self.sm.close_port()

        for port in ports:
            port_description = port.description
            action = self.ports_menu.addAction(port_description)
            port_name = port.device
            if self.sm.is_connected(port_name):
                action.setCheckable(True)
                action.setChecked(True)
            self.ports_group.addAction(action)

    def connect_port(self, action: QAction):
        """Connects to a COM port by parsing the text from a clicked QAction
        menu object."""
        p = "COM[0-9]+"
        m = re.search(p, action.text())
        if m:
            port_name = m.group()
            self.sm.open_port(port_name)
        else:
            QMessageBox.warning(self, "Warning", "Invalid port selection!")

    def port_unavailable(self):
        """Displays warning message about unavailable port."""
        QMessageBox.warning(self, "Warning", "Port unavailable!")

    def parse_values(self):
        """Parses and validates input values from the start page."""
        self.tester_id = self.tester_id_input.text().upper()
        self.settings.setValue("user_id", self.tester_id)
        self.pcba_pn = self.pcba_pn_input.currentText()
        self.pcba_sn = self.pcba_sn_input.text().upper()

        if (self.tester_id and self.pcba_pn and self.pcba_sn):

            # The serial number should be seven characters long and start with
            # the specific prefix for the given product.
            if (self.pcba_sn[0:3] == self.product_data[self.pcba_pn][0]
                    and len(self.pcba_sn) == 7):
                self.r.write_data("tester_id", self.tester_id, "PASS")
                self.r.write_data("pcba_sn", self.pcba_sn, "PASS")
                self.r.write_data("pcba_pn", self.pcba_pn, "PASS")
            else:
                self.err_msg = self.create_messagebox("Warning", "Error",
                                                      "Error",
                                                      "Bad serial number!")
                self.err_msg.show()
                return
        else:
            self.err_msg = self.create_messagebox("Warning", "Error", "Error",
                                                  "Missing value!")
            self.err_msg.show()
            return

        self.start_procedure()

    def start_procedure(self):
        """Sets up procedure layout by creating test statuses and initializing
        the appropriate board class (currently D505, potentially others in 
        the future)."""
        central_widget = QWidget()

        status_lbl_stylesheet = ("QLabel {border: 2px solid grey;"
                                 "color: black; font-size: 20px}")
        status_style_pass = """QLabel {background: #8cff66;
                                border: 2px solid grey; font-size: 20px}"""

        # ______Labels______
        self.tester_id_status = QLabel(f"Tester ID: {self.tester_id}")
        self.pcba_pn_status = QLabel(f"PCBA PN: {self.pcba_pn}")
        self.pcba_sn_status = QLabel(f"PCBA SN: {self.pcba_sn}")
        self.input_i_status = QLabel(f"Input Current: _____ mA")
        self.supply_5v_status = QLabel("5V Supply: _____V")
        self.output_2p5v_status = QLabel("2.5V Output: _____V")
        self.supply_1p8v_status = QLabel("1.8V Supply: _____V")
        self.xmega_prog_status = QLabel("XMega Programming: _____")
        self.one_wire_prog_status = QLabel("1-Wire Programming:_____")
        self.internal_5v_status = QLabel("Internal 5V: _____V")
        self.tac_id_status = QLabel("TAC ID: _____")
        self.hall_effect_status = QLabel("Hall Effect Sensor Test:_____")
        self.led_test_status = QLabel("LED Test:_____")

        self.tester_id_status.setStyleSheet(status_style_pass)
        self.pcba_pn_status.setStyleSheet(status_style_pass)
        self.pcba_sn_status.setStyleSheet(status_style_pass)
        self.input_i_status.setStyleSheet(status_lbl_stylesheet)
        self.supply_5v_status.setStyleSheet(status_lbl_stylesheet)
        self.output_2p5v_status.setStyleSheet(status_lbl_stylesheet)
        self.supply_1p8v_status.setStyleSheet(status_lbl_stylesheet)
        self.xmega_prog_status.setStyleSheet(status_lbl_stylesheet)
        self.one_wire_prog_status.setStyleSheet(status_lbl_stylesheet)
        self.internal_5v_status.setStyleSheet(status_lbl_stylesheet)
        self.tac_id_status.setStyleSheet(status_lbl_stylesheet)
        self.hall_effect_status.setStyleSheet(status_lbl_stylesheet)
        self.led_test_status.setStyleSheet(status_lbl_stylesheet)

        # ______Layout______
        status_vbox1 = QVBoxLayout()
        status_vbox1.setSpacing(10)
        status_vbox1.addWidget(self.tester_id_status)
        status_vbox1.addWidget(self.pcba_pn_status)
        status_vbox1.addWidget(self.pcba_sn_status)
        status_vbox1.addWidget(self.input_i_status)
        status_vbox1.addWidget(self.supply_5v_status)
        status_vbox1.addWidget(self.output_2p5v_status)
        status_vbox1.addWidget(self.supply_1p8v_status)
        status_vbox1.addWidget(self.xmega_prog_status)
        status_vbox1.addWidget(self.one_wire_prog_status)
        status_vbox1.addWidget(self.internal_5v_status)
        status_vbox1.addWidget(self.tac_id_status)
        status_vbox1.addWidget(self.hall_effect_status)
        status_vbox1.addWidget(self.led_test_status)
        status_vbox1.addStretch()

        status_group = QGroupBox("Test Statuses")
        status_group.setFont(self.label_font)
        status_group.setLayout(status_vbox1)

        # Use the product data dictionary to call the procdure class that
        # corresponds to the part number. Create an instance of it passing it
        # the instances of test_utility, model, serial_manager and report.
        self.procedure = self.product_data[self.pcba_pn][1](self, self.m,
                                                            self.sm, self.r)

        grid = QGridLayout()
        grid.setColumnStretch(0, 5)
        grid.setColumnStretch(1, 15)
        grid.addWidget(status_group, 0, 0, Qt.AlignTop)
        grid.addWidget(self.procedure, 0, 1)

        # layout = QHBoxLayout()
        # layout.addWidget(self.procedure)

        central_widget.setLayout(grid)

        self.setCentralWidget(central_widget)

    def configuration(self):
        """Sets up configuration/settings window elements."""
        FILE_BTN_WIDTH = 30

        self.settings_widget = QDialog(self)

        port1_lbl = QLabel("Port 1 TAC ID:")
        port1_lbl.setFont(self.config_font)
        self.port1_tac_id = QLineEdit(self.settings.value("port1_tac_id"))

        port_layout = QGridLayout()
        port_layout.addWidget(port1_lbl, 0, 0)
        port_layout.addWidget(self.port1_tac_id, 0, 1)

        port_group = QGroupBox("TAC IDs")
        port_group.setLayout(port_layout)

        self.hex_btn = QPushButton("[...]")
        self.hex_btn.setFixedWidth(FILE_BTN_WIDTH)
        self.hex_btn.clicked.connect(self.set_hex_dir)
        self.hex_lbl = QLabel("Choose the location of hex files: ")
        self.hex_lbl.setFont(self.config_font)
        self.hex_path_lbl = QLabel(self.settings.value("hex_files_path"))
        self.hex_path_lbl.setFont(self.config_path_font)
        self.hex_path_lbl.setStyleSheet("QLabel {color: blue}")

        self.report_btn = QPushButton("[...]")
        self.report_btn.setFixedWidth(FILE_BTN_WIDTH)
        self.report_btn.clicked.connect(self.set_report_location)
        self.report_lbl = QLabel("Set report save location: ")
        self.report_lbl.setFont(self.config_font)
        self.report_path_lbl = QLabel(self.settings.value("report_dir_path"))
        self.report_path_lbl.setFont(self.config_path_font)
        self.report_path_lbl.setStyleSheet("QLabel {color: blue}")

        self.atprogram_btn = QPushButton("[...]")
        self.atprogram_btn.setFixedWidth(FILE_BTN_WIDTH)
        self.atprogram_btn.clicked.connect(self.choose_atprogram_file)
        self.atprogram_lbl = QLabel("Select atprogram.exe.")
        self.atprogram_lbl.setFont(self.config_font)
        self.atprogram_path_lbl = QLabel(
            self.settings.value("atprogram_file_path"))
        self.atprogram_path_lbl.setFont(self.config_path_font)
        self.atprogram_path_lbl.setStyleSheet("QLabel {color: blue}")

        save_loc_layout = QGridLayout()
        save_loc_layout.addWidget(self.hex_lbl, 0, 0)
        save_loc_layout.addWidget(self.hex_btn, 0, 1)
        save_loc_layout.addWidget(self.hex_path_lbl, 1, 0)
        save_loc_layout.addWidget(self.report_lbl, 2, 0)
        save_loc_layout.addWidget(self.report_btn, 2, 1)
        save_loc_layout.addWidget(self.report_path_lbl, 3, 0)
        save_loc_layout.addWidget(self.atprogram_lbl, 4, 0)
        save_loc_layout.addWidget(self.atprogram_btn, 4, 1)
        save_loc_layout.addWidget(self.atprogram_path_lbl, 5, 0)

        save_loc_group = QGroupBox("Save Locations")
        save_loc_group.setLayout(save_loc_layout)

        apply_btn = QPushButton("Apply Settings")
        apply_btn.clicked.connect(self.apply_settings)
        cancel_btn = QPushButton("Cancel")
        cancel_btn.clicked.connect(self.cancel_settings)

        button_layout = QHBoxLayout()
        button_layout.addWidget(cancel_btn)
        button_layout.addStretch()
        button_layout.addWidget(apply_btn)

        hbox_top = QHBoxLayout()
        hbox_top.addWidget(port_group)

        hbox_bottom = QHBoxLayout()
        # hbox_bottom.addStretch()
        hbox_bottom.addWidget(save_loc_group)
        # hbox_bottom.addStretch()

        grid = QGridLayout()
        grid.addLayout(hbox_top, 0, 0)
        grid.addLayout(hbox_bottom, 1, 0)
        grid.addLayout(button_layout, 2, 0)
        grid.setHorizontalSpacing(100)

        self.settings_widget.setLayout(grid)
        self.settings_widget.setWindowTitle(
            "Threadlink Configuration Settings")
        self.settings_widget.show()
        # self.settings_widget.resize(800, 600)

        # frameGm = self.frameGeometry()
        # screen = QApplication.desktop().screenNumber(
        #     QApplication.desktop().cursor().pos())
        # centerPoint = QApplication.desktop().screenGeometry(screen).center()
        # frameGm.moveCenter(centerPoint)
        # self.settings_widget.move(frameGm.topLeft())

    def set_hex_dir(self):
        """Opens file dialog for selecting the hex files directory."""

        hex_files_path = QFileDialog.getExistingDirectory(
            self, "Select hex files directory.")
        self.hex_path_lbl.setText(hex_files_path)

    def set_report_location(self):
        """Opens file dialog for setting the save location for the report."""

        report_dir = QFileDialog.getExistingDirectory(
            self, "Select report save location.")
        self.report_path_lbl.setText(report_dir)

    def choose_atprogram_file(self):
        """Opens file dialog for selecting the atprogram executable."""

        atprogram_file_path = QFileDialog.getOpenFileName(
            self, "Select atprogram.exe.", "", "Application (*.exe)")[0]
        self.atprogram_path_lbl.setText(atprogram_file_path)

    def cancel_settings(self):
        """Close the settings widget without applying changes."""

        self.settings_widget.close()

    def apply_settings(self):
        """Read user inputs and apply settings."""

        p = r"([a-fA-F0-9]){8}"

        port1_value = self.port1_tac_id.text()

        if re.fullmatch(p, port1_value):
            self.settings.setValue("port1_tac_id", port1_value)

        else:
            QMessageBox.warning(
                self.settings_widget, "Warning!", f"Bad TAC ID!\n"
                "IDs are 8 digit hex values.\n"
                "E.g.: 000a5296")
            return

        self.settings.setValue("hex_files_path", self.hex_path_lbl.text())
        self.settings.setValue("report_dir_path", self.report_path_lbl.text())
        self.settings.setValue("atprogram_file_path",
                               self.atprogram_path_lbl.text())

        QMessageBox.information(self.settings_widget, "Information",
                                "Settings applied!")

        self.settings_widget.close()

    def closeEvent(self, event):
        """Override QWidget closeEvent to provide user with confirmation
        dialog and ensure threads are terminated appropriately."""

        event.accept()

        quit_msg = "Are you sure you want to exit the program?"
        confirmation = QMessageBox.question(self, 'Message', quit_msg,
                                            QMessageBox.Yes, QMessageBox.No)

        if confirmation == QMessageBox.Yes:
            self.serial_thread.quit()
            self.serial_thread.wait()
            event.accept()
        else:
            event.ignore()
Пример #28
0
class ConsoleWidget(RichJupyterWidget, PMDockObject):
    def __init__(self, *args, **kwargs):
        super(ConsoleWidget, self).__init__(*args, **kwargs)
        self.is_first_execution = True
        self.confirm_restart = False

        # print(cmd)
        self.commands_pool = []  # [('import sys',True,''),(cmd,False,'')]
        # print(self.commands_pool)

    def change_ui_theme(self, style: str):
        if style == 'Fusion':
            self.style_sheet = default_light_style_sheet
            self.syntax_style = default_light_syntax_style
        elif style == 'Qdarkstyle':
            self.style_sheet = default_dark_style_sheet
            self.syntax_style = default_dark_syntax_style

        elif style.lower() == 'windowsvista':
            self.style_sheet = default_light_style_sheet
            self.syntax_style = default_light_syntax_style

        elif style.lower() == 'windows':
            self.style_sheet = default_light_style_sheet
            self.syntax_style = default_light_syntax_style

    def _handle_kernel_died(self, since_last_heartbit):
        self.is_first_execution = True
        self.restart_kernel(None, True)
        self.initialize_ipython_builtins()
        self.execute_command('')
        return True

    def _handle_execute_input(self, msg):
        super()._handle_execute_result(msg)

    def setup_ui(self):
        self.kernel_manager = None
        self.kernel_client = None
        # initialize by thread
        self.init_thread = QThread(self)
        self.console_object = ConsoleInitThread()
        self.console_object.moveToThread(self.init_thread)
        self.console_object.initialized.connect(self.slot_initialized)
        self.init_thread.finished.connect(self.console_object.deleteLater)
        self.init_thread.finished.connect(self.init_thread.deleteLater)
        self.init_thread.started.connect(self.console_object.run)
        self.init_thread.start()
        cursor: QTextCursor = self._prompt_cursor
        cursor.movePosition(QTextCursor.End)

        _ = lambda s: s
        self.context_menu = QMenu()
        restart_action = self.context_menu.addAction(_('Restart'))
        restart_action.triggered.connect(self._restart_kernel)
        save_action = self.context_menu.addAction(_('Save as '))
        cancel_action = self.context_menu.addAction(_('Undo'))
        redo_action = self.context_menu.addAction(_('Redo'))
        delete_action = self.context_menu.addAction(_('Delete'))
        style = self.lib.Program.get_settings()['theme']
        self.change_ui_theme(style)

    def _custom_context_menu_requested(self, pos):
        self.context_menu.exec_(self.mapToGlobal(pos))

    def _restart_kernel(self, arg1):
        self.is_first_execution = True
        self.restart_kernel(None, True)
        self.initialize_ipython_builtins()
        self.execute_command('')
        return True

    def connect_to_datamanager(self, data_manager):
        self.data_manager = data_manager
        self.lib = self.data_manager

    def slot_initialized(self, kernel_manager, kernel_client):
        """
        Args:
            kernel_manager: `qtconsole.manager.QtKernelManager`
            kernel_client: `qtconsole.manager.QtKernelManager.client`

        Returns:
        """
        self.kernel_manager = kernel_manager
        self.kernel_client = kernel_client
        self.initialize_ipython_builtins()

    def initialize_ipython_builtins(self):
        from pmgwidgets import get_parent_path
        path = get_parent_path(__file__, 5)
        cmd = 'import sys;sys.path.append(r\'%s\')' % path
        self.execute_command(cmd, True, '')
        ini_py = os.path.join(self.lib.get_main_program_dir(), 'extensions',
                              'packages', 'ipython_console',
                              'initialisation.py')
        self.execute_file(ini_py, hidden=True)
        for source, hidden, hint_text in self.commands_pool:
            self.execute_command(source, hidden, hint_text)

    def _update_list(self):
        try:
            super(ConsoleWidget, self)._update_list()
        except BaseException:
            import traceback
            traceback.print_exc()

    def _handle_complete_reply(self, msg):
        """
        重写,加上trycatch,直接禁用了没有其他的变化,故不做类型标注。
        :param msg:
        :return:
        """
        try:
            super()._handle_complete_reply(msg)
        except BaseException:
            import traceback
            traceback.print_exc()

    def _banner_default(self):
        """
        自定义控制台开始的文字
        Returns:
        """
        return 'Welcome To PyMiner!\n'

    def closeEvent(self, event):
        if self.init_thread.isRunning():
            self.console_object.stop()
            self.init_thread.quit()
            self.init_thread.wait(500)
        super(ConsoleWidget, self).closeEvent(event)

    def execute_file(self, file: str, hidden: bool = False):
        if not os.path.exists(file) or not file.endswith('.py'):
            raise FileNotFoundError(f'{file} not found or invalid')
        base = os.path.basename(file)
        cmd = os.path.splitext(base)[0]
        with open(file, 'r', encoding='utf-8') as f:
            source = f.read()

        self.execute_command(source, hidden=hidden, hint_text=cmd)

    def execute_command(self,
                        source,
                        hidden: bool = False,
                        hint_text: str = ''):
        """

        :param source:
        :param hidden:
        :param hint_text: 运行代码前显示的提示
        :return:
        """
        cursor: QTextCursor = self._prompt_cursor
        cursor.movePosition(QTextCursor.End)
        # 运行文件时,显示文件名,无换行符,执行选中内容时,包含换行符
        # 检测换行符,在ipy console中显示执行脚本内容
        hint_row_list = hint_text.split("\n")
        for hint in hint_row_list:
            if hint != "":
                cursor.insertText('%s\n' % hint)
                self._insert_continuation_prompt(cursor)
        else:
            # 删除多余的continuation_prompt
            self.undo()

        self._finalize_input_request(
        )  # display input string buffer in console.
        cursor.movePosition(QTextCursor.End)
        if self.kernel_client is None:
            self.commands_pool.append((source, hidden, hint_text))

        else:
            self._execute(source, hidden)

    def _handle_stream(self, msg):
        cursor: QTextCursor = self._prompt_cursor
        cursor.movePosition(QTextCursor.End)
        print(msg['content']['text'])
        msg_lines = msg['content']['text'].strip().split('\n')
        data_b64 = msg_lines[-1]
        try:
            data_b64_dic = pickle.loads(base64.b64decode(data_b64))  # got data
        except BaseException:
            pass
        else:
            msg['content']['text'] = '\n'.join(msg_lines[:-1])
            data = {}
            for key in data_b64_dic:
                var = data_b64_dic[key]
                try:
                    data[key] = pickle.loads(
                        base64.b64decode(data_b64_dic[key]))
                except BaseException:
                    import traceback
                    traceback.print_exc()
            self.data = self.data_manager.get_all_var()
            data = {
                k: v
                for k, v in data.items()
                if k not in self.data or str(self.data[k]) != str(v)
            }
            try:
                self.data_manager.set_var_dict(data, 'ipython')
            except Exception as e:
                msg['content']['text'] += f'\n{type(e).__name__}: {e}'
                import traceback
                traceback.print_exc()
        super()._handle_stream(msg)

    def append_stream(self, text):
        """重写的方法。原本before_prompt属性是False。"""
        self._append_plain_text(text, before_prompt=False)

    def _execute(self, source: str, hidden: bool = False):
        if not self.is_source_code_legal(source):
            QMessageBox.warning(self, '警告', '命令\n\"%s\"\n为无效命令。' % source)
            return
        # if self.is_first_execution:
        #     self.is_first_execution = False
        # else:
        #     self.data = self.data_manager.get_all_var()  # send data
        #     data_b64_dic = {}
        #     for key in self.data:
        #         var = self.data[key]
        #         try:
        #             data_b64_dic[key] = base64.b64encode(
        #                 pickle.dumps(var)).decode('ascii')
        #         except BaseException:
        #             import traceback
        #             traceback.print_exc()
        #     data_b64 = base64.b64encode(
        #         pickle.dumps(data_b64_dic)).decode('ascii')
        # source = f'__inject("{data_b64}")\n{source}'
        super()._execute(source, hidden)

    def is_source_code_legal(self, source_code: str) -> bool:
        """
        判断注入到shell的命令是否合法,不合法的话,就避免执行这个函数。
        :param source_code:
        :return:
        """
        s = source_code.strip()
        s = s.split('(')[0]
        # if s in self.illegal_commands:
        #     return False
        return True
class MainWindow(QMainWindow):
    def __init__(self, params_obj):
        super(MainWindow, self).__init__()

        self.params_cb = params_obj
        self.sim = Simulation(params_obj)
        self.sim.set_params(params_obj)
        grid = QGridLayout()
        grid.addWidget(
            self.createActivation('Stance to lift off', 6,
                                  params_obj.set_activation,
                                  params_obj.toggle), 0, 0)
        grid.addWidget(
            self.createActivation('Swing to touch down', 3,
                                  params_obj.set_activation,
                                  params_obj.toggle), 0, 1)
        grid.addWidget(
            self.createActivation('Touch down to stance', 2,
                                  params_obj.set_activation,
                                  params_obj.toggle), 1, 0)
        grid.addWidget(
            self.createActivation('Lift off to swing', 2,
                                  params_obj.set_activation,
                                  params_obj.toggle), 1, 1)

        grid.addWidget(self.createReflex(key='Hip angle liftoff'), 2, 0)
        grid.addWidget(self.createReflex(key='Ankle unloading liftoff'), 2, 1)
        grid.addWidget(self.createReflex(key='Hip angle touchdown'), 3, 0)
        grid.addWidget(self.createReflex(key='Ankle unloading touchdown'), 3,
                       1)

        self.sim_thread = QThread()
        self.sim.moveToThread(self.sim_thread)
        self.sim_thread.started.connect(self.sim.run)
        self.sim_thread.start()
        b = QPushButton('Run')
        b.pressed.connect(self.sim_thread.start)
        grid.addWidget(b, 3, 2)
        self.w = QWidget()
        self.w.setLayout(grid)
        self.setCentralWidget(self.w)
        self.setWindowTitle("Parameter tuning")
        self.resize(400, 300)
        self.show()

    def createActivation(self, key, n_values, value_cb, toggle_cb):
        print key
        groupBox = QGroupBox(key)
        radio1 = QRadioButton('Enable ?')
        radio1.setChecked(False)
        radio1.toggled.connect(lambda: toggle_cb(key))
        vbox = QVBoxLayout()
        vbox.addWidget(radio1)
        for i in range(n_values):
            vbox.addWidget(self.get_slider(key, value_cb, i))
            vbox.addStretch(1)
        groupBox.setLayout(vbox)
        return groupBox

    def get_slider(self, key, cb_method, idx=0):
        slider = QSlider(Qt.Horizontal)
        slider.setFocusPolicy(Qt.StrongFocus)
        slider.setTickPosition(QSlider.TicksBothSides)
        slider.setTickInterval(10)
        slider.setTickPosition(1)
        slider.setSingleStep(1)
        slider.sliderReleased.connect(
            lambda: cb_method(key, idx, slider.value()))
        return slider

    def createReflex(self, key='Default'):
        groupBox = QGroupBox(key)
        vbox = QVBoxLayout()
        slider = QSlider(Qt.Horizontal)
        slider.setFocusPolicy(Qt.StrongFocus)
        slider.setTickPosition(QSlider.TicksBothSides)
        slider.setTickInterval(10)
        slider.setSingleStep(1)
        vbox.addWidget(slider)
        vbox.addStretch(1)
        groupBox.setLayout(vbox)
        return groupBox
Пример #30
0
class Windows(QDialog, mainUI.Ui_Dialog):
    isRunning = False

    def __init__(self, parent=None):
        super(Windows, self).__init__(parent)
        self.selectedDict = None
        self.currentConfig = dict()
        self.localWords = []
        self.selectedGroups = []

        self.workerThread = QThread(self)
        self.workerThread.start()
        self.updateCheckThead = QThread(self)
        self.updateCheckThead.start()
        self.audioDownloadThread = QThread(self)

        self.updateCheckWork = None
        self.loginWorker = None
        self.queryWorker = None
        self.pullWorker = None
        self.audioDownloadWorker = None

        self.setupUi(self)
        self.setWindowTitle(MODEL_NAME)
        self.setupLogger()
        self.initCore()
        self.checkUpdate()
        # self.__dev() # 以备调试时使用

    def __dev(self):
        def on_dev():
            logger.debug('whatever')

        self.devBtn = QPushButton('Magic Button', self.mainTab)
        self.devBtn.clicked.connect(on_dev)
        self.gridLayout_4.addWidget(self.devBtn, 4, 3, 1, 1)

    def closeEvent(self, event):
        # 插件关闭时退出所有线程
        if self.workerThread.isRunning():
            self.workerThread.requestInterruption()
            self.workerThread.quit()
            self.workerThread.wait()

        if self.updateCheckThead.isRunning():
            self.updateCheckThead.quit()
            self.updateCheckThead.wait()

        if self.audioDownloadThread.isRunning():
            self.audioDownloadThread.requestInterruption()
            self.workerThread.quit()
            self.workerThread.wait()

        event.accept()

    def setupLogger(self):
        """初始化 Logger """

        def onDestroyed():
            logger.removeHandler(QtHandler)

        # 防止 debug 信息写入stdout/stderr 导致 Anki 崩溃
        logging.basicConfig(handlers=[logging.FileHandler('dict2anki.log', 'w', 'utf-8')], level=logging.DEBUG, format='[%(asctime)s][%(levelname)8s] -- %(message)s - (%(name)s)')

        logTextBox = QPlainTextEdit(self)
        logTextBox.setLineWrapMode(QPlainTextEdit.NoWrap)
        layout = QVBoxLayout()
        layout.addWidget(logTextBox)
        self.logTab.setLayout(layout)
        QtHandler = Handler(self)
        logger.addHandler(QtHandler)
        QtHandler.newRecord.connect(logTextBox.appendPlainText)

        # 日志Widget销毁时移除 Handlers
        logTextBox.destroyed.connect(onDestroyed)

    def setupGUIByConfig(self):
        config = mw.addonManager.getConfig(__name__)
        self.deckComboBox.setCurrentText(config['deck'])
        self.dictionaryComboBox.setCurrentIndex(config['selectedDict'])
        self.apiComboBox.setCurrentIndex(config['selectedApi'])
        self.usernameLineEdit.setText(config['credential'][config['selectedDict']]['username'])
        self.passwordLineEdit.setText(config['credential'][config['selectedDict']]['password'])
        self.cookieLineEdit.setText(config['credential'][config['selectedDict']]['cookie'])
        self.definitionCheckBox.setChecked(config['definition'])
        self.imageCheckBox.setChecked(config['image'])
        self.sentenceCheckBox.setChecked(config['sentence'])
        self.phraseCheckBox.setChecked(config['phrase'])
        self.AmEPhoneticCheckBox.setChecked(config['AmEPhonetic'])
        self.BrEPhoneticCheckBox.setChecked(config['BrEPhonetic'])
        self.BrEPronRadioButton.setChecked(config['BrEPron'])
        self.AmEPronRadioButton.setChecked(config['AmEPron'])
        self.noPronRadioButton.setChecked(config['noPron'])
        self.selectedGroups = config['selectedGroup']

    def initCore(self):
        self.usernameLineEdit.hide()
        self.usernameLabel.hide()
        self.passwordLabel.hide()
        self.passwordLineEdit.hide()
        self.dictionaryComboBox.addItems([d.name for d in dictionaries])
        self.apiComboBox.addItems([d.name for d in apis])
        self.deckComboBox.addItems(getDeckList())
        self.setupGUIByConfig()

    def getAndSaveCurrentConfig(self) -> dict:
        """获取当前设置"""
        currentConfig = dict(
            selectedDict=self.dictionaryComboBox.currentIndex(),
            selectedApi=self.apiComboBox.currentIndex(),
            selectedGroup=self.selectedGroups,
            deck=self.deckComboBox.currentText(),
            username=self.usernameLineEdit.text(),
            password=Mask(self.passwordLineEdit.text()),
            cookie=Mask(self.cookieLineEdit.text()),
            definition=self.definitionCheckBox.isChecked(),
            sentence=self.sentenceCheckBox.isChecked(),
            image=self.imageCheckBox.isChecked(),
            phrase=self.phraseCheckBox.isChecked(),
            AmEPhonetic=self.AmEPhoneticCheckBox.isChecked(),
            BrEPhonetic=self.BrEPhoneticCheckBox.isChecked(),
            BrEPron=self.BrEPronRadioButton.isChecked(),
            AmEPron=self.AmEPronRadioButton.isChecked(),
            noPron=self.noPronRadioButton.isChecked(),
        )
        logger.info(f'当前设置:{currentConfig}')
        self._saveConfig(currentConfig)
        self.currentConfig = currentConfig
        return currentConfig

    @staticmethod
    def _saveConfig(config):
        _config = deepcopy(config)
        _config['credential'] = [dict(username='', password='', cookie='')] * len(dictionaries)
        _config['credential'][_config['selectedDict']] = dict(
            username=_config.pop('username'),
            password=str(_config.pop('password')),
            cookie=str(_config.pop('cookie'))
        )
        maskedConfig = deepcopy(_config)
        maskedCredential = [
            dict(
                username=c['username'],
                password=Mask(c['password']),
                cookie=Mask(c['cookie'])) for c in maskedConfig['credential']
        ]
        maskedConfig['credential'] = maskedCredential
        logger.info(f'保存配置项:{maskedConfig}')
        mw.addonManager.writeConfig(__name__, _config)

    def checkUpdate(self):
        @pyqtSlot(str, str)
        return
        def on_haveNewVersion(version, changeLog):
            if askUser(f'有新版本:{version}是否更新?\n\n{changeLog.strip()}'):
                openLink(RELEASE_URL)

        self.updateCheckWork = VersionCheckWorker()
        self.updateCheckWork.moveToThread(self.updateCheckThead)
        self.updateCheckWork.haveNewVersion.connect(on_haveNewVersion)
        self.updateCheckWork.finished.connect(self.updateCheckThead.quit)
        self.updateCheckWork.start.connect(self.updateCheckWork.run)
        self.updateCheckWork.start.emit()

    @pyqtSlot(int)
    def on_dictionaryComboBox_currentIndexChanged(self, index):
        """词典候选框改变事件"""
        self.currentDictionaryLabel.setText(f'当前选择词典: {self.dictionaryComboBox.currentText()}')
        config = mw.addonManager.getConfig(__name__)
        self.cookieLineEdit.setText(config['credential'][index]['cookie'])

    @pyqtSlot()
    def on_pullRemoteWordsBtn_clicked(self):
        """获取单词按钮点击事件"""
        if not self.deckComboBox.currentText():
            showInfo('\n请选择或输入要同步的牌组')
            return

        self.mainTab.setEnabled(False)
        self.progressBar.setValue(0)
        self.progressBar.setMaximum(0)

        currentConfig = self.getAndSaveCurrentConfig()
        self.selectedDict = dictionaries[currentConfig['selectedDict']]()

        # 登陆线程
        self.loginWorker = LoginStateCheckWorker(self.selectedDict.checkCookie, json.loads(self.cookieLineEdit.text() or '{}'))
        self.loginWorker.moveToThread(self.workerThread)
        self.loginWorker.start.connect(self.loginWorker.run)
        self.loginWorker.logSuccess.connect(self.onLogSuccess)
        self.loginWorker.logFailed.connect(self.onLoginFailed)
        self.loginWorker.start.emit()

    @pyqtSlot()
    def onLoginFailed(self):
        showCritical('第一次登录或cookie失效!请重新登录')
        self.progressBar.setValue(0)
        self.progressBar.setMaximum(1)
        self.mainTab.setEnabled(True)
        self.cookieLineEdit.clear()
        self.loginDialog = LoginDialog(
            loginUrl=self.selectedDict.loginUrl,
            loginCheckCallbackFn=self.selectedDict.loginCheckCallbackFn,
            parent=self
        )
        self.loginDialog.loginSucceed.connect(self.onLogSuccess)
        self.loginDialog.show()

    @pyqtSlot(str)
    def onLogSuccess(self, cookie):
        self.cookieLineEdit.setText(cookie)
        self.getAndSaveCurrentConfig()
        self.selectedDict.checkCookie(json.loads(cookie))
        self.selectedDict.getGroups()

        container = QDialog(self)
        group = wordGroup.Ui_Dialog()
        group.setupUi(container)

        for groupName in [str(group_name) for group_name, _ in self.selectedDict.groups]:
            item = QListWidgetItem()
            item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
            item.setText(groupName)
            item.setCheckState(Qt.Unchecked)
            group.wordGroupListWidget.addItem(item)

        # 恢复上次选择的单词本分组
        if self.selectedGroups:
            for groupName in self.selectedGroups[self.currentConfig['selectedDict']]:
                items = group.wordGroupListWidget.findItems(groupName, Qt.MatchExactly)
                for item in items:
                    item.setCheckState(Qt.Checked)
        else:
            self.selectedGroups = [list()] * len(dictionaries)

        def onAccepted():
            """选择单词本弹窗确定事件"""
            # 清空 listWidget
            self.newWordListWidget.clear()
            self.needDeleteWordListWidget.clear()
            self.mainTab.setEnabled(False)

            selectedGroups = [group.wordGroupListWidget.item(index).text() for index in range(group.wordGroupListWidget.count()) if
                              group.wordGroupListWidget.item(index).checkState() == Qt.Checked]
            # 保存分组记录
            self.selectedGroups[self.currentConfig['selectedDict']] = selectedGroups
            self.progressBar.setValue(0)
            self.progressBar.setMaximum(1)
            logger.info(f'选中单词本{selectedGroups}')
            self.getRemoteWordList(selectedGroups)

        def onRejected():
            """选择单词本弹窗取消事件"""
            self.progressBar.setValue(0)
            self.progressBar.setMaximum(1)
            self.mainTab.setEnabled(True)

        group.buttonBox.accepted.connect(onAccepted)
        group.buttonBox.rejected.connect(onRejected)
        container.exec()

    def getRemoteWordList(self, selected_groups: [str]):
        """根据选中到分组获取分组下到全部单词,并添加到 newWordListWidget"""
        group_map = dict(self.selectedDict.groups)
        self.localWords = getWordsByDeck(self.deckComboBox.currentText())

        # 启动单词获取线程
        self.pullWorker = RemoteWordFetchingWorker(self.selectedDict, [(group_name, group_map[group_name],) for group_name in selected_groups])
        self.pullWorker.moveToThread(self.workerThread)
        self.pullWorker.start.connect(self.pullWorker.run)
        self.pullWorker.tick.connect(lambda: self.progressBar.setValue(self.progressBar.value() + 1))
        self.pullWorker.setProgress.connect(self.progressBar.setMaximum)
        self.pullWorker.doneThisGroup.connect(self.insertWordToListWidget)
        self.pullWorker.done.connect(self.on_allPullWork_done)
        self.pullWorker.start.emit()

    @pyqtSlot(list)
    def insertWordToListWidget(self, words: list):
        """一个分组获取完毕事件"""
        for word in words:
            wordItem = QListWidgetItem(word, self.newWordListWidget)
            wordItem.setData(Qt.UserRole, None)
        self.newWordListWidget.clearSelection()

    @pyqtSlot()
    def on_allPullWork_done(self):
        """全部分组获取完毕事件"""
        localWordList = set(getWordsByDeck(self.deckComboBox.currentText()))
        remoteWordList = set([self.newWordListWidget.item(row).text() for row in range(self.newWordListWidget.count())])

        newWords = remoteWordList - localWordList  # 新单词
        needToDeleteWords = localWordList - remoteWordList  # 需要删除的单词
        logger.info(f'本地: {localWordList}')
        logger.info(f'远程: {remoteWordList}')
        logger.info(f'待查: {newWords}')
        logger.info(f'待删: {needToDeleteWords}')
        waitIcon = QIcon(':/icons/wait.png')
        delIcon = QIcon(':/icons/delete.png')
        self.newWordListWidget.clear()
        self.needDeleteWordListWidget.clear()

        for word in needToDeleteWords:
            item = QListWidgetItem(word)
            item.setCheckState(Qt.Checked)
            item.setIcon(delIcon)
            self.needDeleteWordListWidget.addItem(item)

        for word in newWords:
            item = QListWidgetItem(word)
            item.setIcon(waitIcon)
            self.newWordListWidget.addItem(item)
        self.newWordListWidget.clearSelection()

        self.dictionaryComboBox.setEnabled(True)
        self.apiComboBox.setEnabled(True)
        self.deckComboBox.setEnabled(True)
        self.pullRemoteWordsBtn.setEnabled(True)
        self.queryBtn.setEnabled(self.newWordListWidget.count() > 0)
        self.syncBtn.setEnabled(self.newWordListWidget.count() == 0 and self.needDeleteWordListWidget.count() > 0)
        if self.needDeleteWordListWidget.count() == self.newWordListWidget.count() == 0:
            logger.info('无需同步')
            tooltip('无需同步')
        self.mainTab.setEnabled(True)

    @pyqtSlot()
    def on_queryBtn_clicked(self):
        logger.info('点击查询按钮')
        currentConfig = self.getAndSaveCurrentConfig()
        self.queryBtn.setEnabled(False)
        self.pullRemoteWordsBtn.setEnabled(False)
        self.syncBtn.setEnabled(False)

        wordList = []
        selectedWords = self.newWordListWidget.selectedItems()
        if selectedWords:
            # 如果选中单词则只查询选中的单词
            for wordItem in selectedWords:
                wordBundle = dict()
                row = self.newWordListWidget.row(wordItem)
                wordBundle['term'] = wordItem.text()
                for configName in BASIC_OPTION + EXTRA_OPTION:
                    wordBundle[configName] = currentConfig[configName]
                    wordBundle['row'] = row
                wordList.append(wordBundle)
        else:  # 没有选择则查询全部
            for row in range(self.newWordListWidget.count()):
                wordBundle = dict()
                wordItem = self.newWordListWidget.item(row)
                wordBundle['term'] = wordItem.text()
                for configName in BASIC_OPTION + EXTRA_OPTION:
                    wordBundle[configName] = currentConfig[configName]
                    wordBundle['row'] = row
                wordList.append(wordBundle)

        logger.info(f'待查询单词{wordList}')
        # 查询线程
        self.progressBar.setMaximum(len(wordList))
        self.queryWorker = QueryWorker(wordList, apis[currentConfig['selectedApi']])
        self.queryWorker.moveToThread(self.workerThread)
        self.queryWorker.thisRowDone.connect(self.on_thisRowDone)
        self.queryWorker.thisRowFailed.connect(self.on_thisRowFailed)
        self.queryWorker.tick.connect(lambda: self.progressBar.setValue(self.progressBar.value() + 1))
        self.queryWorker.allQueryDone.connect(self.on_allQueryDone)
        self.queryWorker.start.connect(self.queryWorker.run)
        self.queryWorker.start.emit()

    @pyqtSlot(int, dict)
    def on_thisRowDone(self, row, result):
        """该行单词查询完毕"""
        doneIcon = QIcon(':/icons/done.png')
        wordItem = self.newWordListWidget.item(row)
        wordItem.setIcon(doneIcon)
        wordItem.setData(Qt.UserRole, result)

    @pyqtSlot(int)
    def on_thisRowFailed(self, row):
        failedIcon = QIcon(':/icons/failed.png')
        failedWordItem = self.newWordListWidget.item(row)
        failedWordItem.setIcon(failedIcon)

    @pyqtSlot()
    def on_allQueryDone(self):
        failed = []

        for i in range(self.newWordListWidget.count()):
            wordItem = self.newWordListWidget.item(i)
            if not wordItem.data(Qt.UserRole):
                failed.append(wordItem.text())

        if failed:
            logger.warning(f'查询失败或未查询:{failed}')

        self.pullRemoteWordsBtn.setEnabled(True)
        self.queryBtn.setEnabled(True)
        self.syncBtn.setEnabled(True)

    @pyqtSlot()
    def on_syncBtn_clicked(self):

        failedGenerator = (self.newWordListWidget.item(row).data(Qt.UserRole) is None for row in range(self.newWordListWidget.count()))
        if any(failedGenerator):
            if not askUser('存在未查询或失败的单词,确定要加入单词本吗?\n 你可以选择失败的单词点击 "查询按钮" 来重试。'):
                return

        currentConfig = self.getAndSaveCurrentConfig()
        model = getOrCreateModel(MODEL_NAME)
        getOrCreateModelCardTemplate(model, 'default')
        deck = getOrCreateDeck(self.deckComboBox.currentText())

        logger.info('同步点击')
        audiosDownloadTasks = []
        newWordCount = self.newWordListWidget.count()

        # 判断是否需要下载发音
        if currentConfig['noPron']:
            logger.info('不下载发音')
            whichPron = None
        else:
            whichPron = 'AmEPron' if self.AmEPronRadioButton.isChecked() else 'BrEPron'
            logger.info(f'下载发音{whichPron}')

        added = 0
        for row in range(newWordCount):
            wordItem = self.newWordListWidget.item(row)
            wordItemData = wordItem.data(Qt.UserRole)
            if wordItemData:
                addNoteToDeck(deck, model, currentConfig, wordItemData)
                added += 1
                # 添加发音任务
                if whichPron and wordItemData.get(whichPron):
                    audiosDownloadTasks.append((f"{whichPron}_{wordItemData['term']}.mp3", wordItemData[whichPron],))
        mw.reset()

        logger.info(f'发音下载任务:{audiosDownloadTasks}')

        if audiosDownloadTasks:
            self.syncBtn.setEnabled(False)
            self.progressBar.setValue(0)
            self.progressBar.setMaximum(len(audiosDownloadTasks))
            if self.audioDownloadThread is not None:
                self.audioDownloadThread.requestInterruption()
                self.audioDownloadThread.quit()
                self.audioDownloadThread.wait()

            self.audioDownloadThread = QThread(self)
            self.audioDownloadThread.start()
            self.audioDownloadWorker = AudioDownloadWorker(audiosDownloadTasks)
            self.audioDownloadWorker.moveToThread(self.audioDownloadThread)
            self.audioDownloadWorker.tick.connect(lambda: self.progressBar.setValue(self.progressBar.value() + 1))
            self.audioDownloadWorker.start.connect(self.audioDownloadWorker.run)
            self.audioDownloadWorker.done.connect(lambda: tooltip(f'发音下载完成'))
            self.audioDownloadWorker.done.connect(self.audioDownloadThread.quit)
            self.audioDownloadWorker.start.emit()

        self.newWordListWidget.clear()

        needToDeleteWordItems = [
            self.needDeleteWordListWidget.item(row)
            for row in range(self.needDeleteWordListWidget.count())
            if self.needDeleteWordListWidget.item(row).checkState() == Qt.Checked
        ]
        needToDeleteWords = [i.text() for i in needToDeleteWordItems]

        deleted = 0

        if needToDeleteWords and askUser(f'确定要删除这些单词吗:{needToDeleteWords[:3]}...({len(needToDeleteWords)}个)', title='Dict2Anki', parent=self):
            needToDeleteWordNoteIds = getNotes(needToDeleteWords, currentConfig['deck'])
            mw.col.remNotes(needToDeleteWordNoteIds)
            deleted += 1
            mw.col.reset()
            mw.reset()
            for item in needToDeleteWordItems:
                self.needDeleteWordListWidget.takeItem(self.needDeleteWordListWidget.row(item))
            logger.info('删除完成')
        logger.info('完成')

        if not audiosDownloadTasks:
            tooltip(f'添加{added}个笔记\n删除{deleted}个笔记')
Пример #31
0
class NetifconQueryTab(AnalysisTab):
    """A netifcon query."""
    def __init__(self, parent, policy, perm_map):
        super(NetifconQueryTab, self).__init__(parent)
        self.log = logging.getLogger(__name__)
        self.policy = policy
        self.query = NetifconQuery(policy)
        self.setupUi()

    def __del__(self):
        self.thread.quit()
        self.thread.wait(5000)
        logging.getLogger("setools.netifconquery").removeHandler(self.handler)

    def setupUi(self):
        self.load_ui("apol/netifconquery.ui")

        # set up user autocompletion
        user_completion_list = [str(u) for u in self.policy.users()]
        user_completer_model = QStringListModel(self)
        user_completer_model.setStringList(sorted(user_completion_list))
        self.user_completion = QCompleter()
        self.user_completion.setModel(user_completer_model)
        self.user.setCompleter(self.user_completion)

        # set up role autocompletion
        role_completion_list = [str(r) for r in self.policy.roles()]
        role_completer_model = QStringListModel(self)
        role_completer_model.setStringList(sorted(role_completion_list))
        self.role_completion = QCompleter()
        self.role_completion.setModel(role_completer_model)
        self.role.setCompleter(self.role_completion)

        # set up type autocompletion
        type_completion_list = [str(t) for t in self.policy.types()]
        type_completer_model = QStringListModel(self)
        type_completer_model.setStringList(sorted(type_completion_list))
        self.type_completion = QCompleter()
        self.type_completion.setModel(type_completer_model)
        self.type_.setCompleter(self.type_completion)

        # setup indications of errors on source/target/default
        self.errors = set()
        self.orig_palette = self.type_.palette()
        self.error_palette = self.type_.palette()
        self.error_palette.setColor(QPalette.Base, Qt.red)
        self.clear_name_error()
        self.clear_user_error()
        self.clear_type_error()
        self.clear_role_error()
        self.clear_range_error()

        # set up results
        self.table_results_model = NetifconTableModel(self)
        self.sort_proxy = QSortFilterProxyModel(self)
        self.sort_proxy.setSourceModel(self.table_results_model)
        self.table_results.setModel(self.sort_proxy)
        self.table_results.sortByColumn(0, Qt.AscendingOrder)

        # set up processing thread
        self.thread = QThread()
        self.worker = QueryResultsUpdater(self.query, self.table_results_model)
        self.worker.moveToThread(self.thread)
        self.worker.raw_line.connect(self.raw_results.appendPlainText)
        self.worker.finished.connect(self.update_complete)
        self.worker.finished.connect(self.thread.quit)
        self.thread.started.connect(self.worker.update)

        # create a "busy, please wait" dialog
        self.busy = QProgressDialog(self)
        self.busy.setModal(True)
        self.busy.setRange(0, 0)
        self.busy.setMinimumDuration(0)
        self.busy.canceled.connect(self.thread.requestInterruption)
        self.busy.reset()

        # update busy dialog from query INFO logs
        self.handler = LogHandlerToSignal()
        self.handler.message.connect(self.busy.setLabelText)
        logging.getLogger("setools.netifconquery").addHandler(self.handler)

        # Ensure settings are consistent with the initial .ui state
        self.set_name_regex(self.name_regex.isChecked())
        self.criteria_frame.setHidden(not self.criteria_expander.isChecked())
        self.notes.setHidden(not self.notes_expander.isChecked())

        # Range criteria is available only if policy is MLS
        if not self.policy.mls:
            self.range_criteria.setEnabled(False)
            self.range_criteria.setToolTip("MLS is disabled in this policy.")
            self.range_.setToolTip("MLS is disabled in this policy.")
            self.range_exact.setToolTip("MLS is disabled in this policy.")
            self.range_overlap.setToolTip("MLS is disabled in this policy.")
            self.range_subset.setToolTip("MLS is disabled in this policy.")
            self.range_superset.setToolTip("MLS is disabled in this policy.")

        # connect signals
        self.buttonBox.clicked.connect(self.run)
        self.name.textEdited.connect(self.clear_name_error)
        self.name.editingFinished.connect(self.set_name)
        self.name_regex.toggled.connect(self.set_name_regex)
        self.user.textEdited.connect(self.clear_user_error)
        self.user.editingFinished.connect(self.set_user)
        self.user_regex.toggled.connect(self.set_user_regex)
        self.role.textEdited.connect(self.clear_role_error)
        self.role.editingFinished.connect(self.set_role)
        self.role_regex.toggled.connect(self.set_role_regex)
        self.type_.textEdited.connect(self.clear_type_error)
        self.type_.editingFinished.connect(self.set_type)
        self.type_regex.toggled.connect(self.set_type_regex)
        self.range_.textEdited.connect(self.clear_range_error)
        self.range_.editingFinished.connect(self.set_range)

    #
    # Name criteria
    #
    def clear_name_error(self):
        self.clear_criteria_error(self.name, "Match the device name.")

    def set_name(self):
        try:
            self.query.name = self.name.text()
        except Exception as ex:
            self.log.error("Device name error: {0}".format(ex))
            self.set_criteria_error(self.name, ex)

    def set_name_regex(self, state):
        self.log.debug("Setting name_regex {0}".format(state))
        self.query.name_regex = state
        self.clear_name_error()
        self.set_name()

    #
    # User criteria
    #
    def clear_user_error(self):
        self.clear_criteria_error(self.user, "Match the user of the context.")

    def set_user(self):
        try:
            self.query.user = self.user.text()
        except Exception as ex:
            self.log.error("Context user error: {0}".format(ex))
            self.set_criteria_error(self.user, ex)

    def set_user_regex(self, state):
        self.log.debug("Setting user_regex {0}".format(state))
        self.query.user_regex = state
        self.clear_user_error()
        self.set_user()

    #
    # Role criteria
    #
    def clear_role_error(self):
        self.clear_criteria_error(self.role, "Match the role of the context.")

    def set_role(self):
        try:
            self.query.role = self.role.text()
        except Exception as ex:
            self.log.error("Context role error: {0}".format(ex))
            self.set_criteria_error(self.role, ex)

    def set_role_regex(self, state):
        self.log.debug("Setting role_regex {0}".format(state))
        self.query.role_regex = state
        self.clear_role_error()
        self.set_role()

    #
    # Type criteria
    #
    def clear_type_error(self):
        self.clear_criteria_error(self.type_, "Match the type of the context.")

    def set_type(self):
        try:
            self.query.type_ = self.type_.text()
        except Exception as ex:
            self.log.error("Context type error: {0}".format(ex))
            self.set_criteria_error(self.type_, ex)

    def set_type_regex(self, state):
        self.log.debug("Setting type_regex {0}".format(state))
        self.query.type_regex = state
        self.clear_type_error()
        self.set_type()

    #
    # Range criteria
    #
    def clear_range_error(self):
        self.clear_criteria_error(self.range_,
                                  "Match the range of the context.")

    def set_range(self):
        try:
            self.query.range_ = self.range_.text()
        except Exception as ex:
            self.log.info("Context range error: " + str(ex))
            self.set_criteria_error(self.range_, ex)

    #
    # Save/Load tab
    #
    def save(self):
        """Return a dictionary of settings."""
        if self.errors:
            raise TabFieldError("Field(s) are in error: {0}".format(" ".join(
                o.objectName() for o in self.errors)))

        settings = {}
        save_checkboxes(self, settings, [
            "criteria_expander", "notes_expander", "name_regex", "user_regex",
            "role_regex", "type_regex", "range_exact", "range_overlap",
            "range_subset", "range_superset"
        ])
        save_lineedits(self, settings,
                       ["name", "user", "role", "type_", "range_"])
        save_textedits(self, settings, ["notes"])
        return settings

    def load(self, settings):
        load_checkboxes(self, settings, [
            "criteria_expander", "notes_expander", "name_regex", "user_regex",
            "role_regex", "type_regex", "range_exact", "range_overlap",
            "range_subset", "range_superset"
        ])
        load_lineedits(self, settings,
                       ["name", "user", "role", "type_", "range_"])
        load_textedits(self, settings, ["notes"])

    #
    # Results runner
    #
    def run(self, button):
        # right now there is only one button.
        self.query.range_overlap = self.range_overlap.isChecked()
        self.query.range_subset = self.range_subset.isChecked()
        self.query.range_superset = self.range_superset.isChecked()

        # start processing
        self.busy.setLabelText("Processing query...")
        self.busy.show()
        self.raw_results.clear()
        self.thread.start()

    def update_complete(self, count):
        self.log.info("{0} netifcon statement(s) found.".format(count))

        # update sizes/location of result displays
        if not self.busy.wasCanceled():
            self.busy.setLabelText(
                "Resizing the result table's columns; GUI may be unresponsive")
            self.busy.repaint()
            self.table_results.resizeColumnsToContents()

        if not self.busy.wasCanceled():
            self.busy.setLabelText(
                "Resizing the result table's rows; GUI may be unresponsive")
            self.busy.repaint()
            self.table_results.resizeRowsToContents()

        if not self.busy.wasCanceled():
            self.busy.setLabelText(
                "Moving the raw result to top; GUI may be unresponsive")
            self.busy.repaint()
            self.raw_results.moveCursor(QTextCursor.Start)

        self.busy.reset()
Пример #32
0
class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent=parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.setWindowTitle("Youtube downloader")

        self.checked_all = False  # declares which state should be setted by check_all-button

        # thread
        self.thread = None
        self.downloader = None

        # videos
        self.videos = []

        # slots
        self.ui.addButton.clicked.connect(self.search)
        self.ui.downloadButton.clicked.connect(self.download)
        self.ui.checkAllButton.clicked.connect(self.check_all)
        self.ui.cleanButton.clicked.connect(self.clean_list)

        # ui
        self.ui.listWidget.setSelectionMode(
            QAbstractItemView.SelectionMode.NoSelection)
        self.ui.mp3CheckBox.setChecked(True)
        self.ui.labelInformation.setVisible(False)
        self.ui.progressBarSingle.setVisible(False)
        self.ui.progressBarMultiple.setVisible(False)

        self.mp3_options = {
            'format':
            'bestaudio',  # 'bestaudio/best',
            'postprocessors': [{
                'key': 'FFmpegExtractAudio',
                'preferredcodec': 'mp3',
                'preferredquality': '320',  # 192',
            }],
            'logger':
            MainWindow.MyLogger(),
            'progress_hooks': [self.my_hook],
        }

        # TODO don't work video downloading
        # self.video_options = {
        #     'format': 'bestvideo+bestaudio',
        #     'noplaylist': True,
        #     # 'ext': '3gp',  # 'bestaudio/best',
        #     'logger': MainWindow.MyLogger(),
        #     'progress_hooks': [self.my_hook],
        # }

        # TODO test
        # self.ui.searchLineEdit.setText(
        #    "https://www.youtube.com/watch?v=8GW6sLrK40k&list=PLit8GzUQ7d7F0Lf2WNNL754TYQHb23b8t&t=0s&index=2")
        self.ui.searchLineEdit.setText(
            "https://www.youtube.com/watch?v=kfchvCyHmsc")
        # TODO test
        self.search()

    class MyLogger:  # TODO
        def debug(self, msg):
            #if msg.startswith("[download]"):
            print("XD")

        def warning(self, msg):
            print("XDD12" + msg)

        def error(self, msg):
            print("XDD123" + msg)

    def my_hook(self, d):
        if d['status'] == 'finished':
            print('Done downloading, now converting ...')

    @pyqtSlot()
    def search(self):

        self.ui.labelInformation.setVisible(False)

        if self.ui.searchLineEdit.text():
            try:
                # TODO so bad it is very slow, but this is a youtube-dl problem
                with yt.YoutubeDL(self.get_options()) as ydl:
                    result = ydl.extract_info(
                        self.ui.searchLineEdit.text(),
                        download=False  # We just want to extract the info
                    )

                # TODO test
                pp = pprint.PrettyPrinter(indent=4)
                pp.pprint(result)

                self.ui.progressBarSingle.setVisible(False)
                self.ui.progressBarMultiple.setVisible(False)

                self.update_list(result)

            except yt.DownloadError:
                self.print_error(
                    str(yt.DownloadError)
                )  # ("Download is not possible.\nCheck you internet connection or link.")

    @pyqtSlot()
    def download(self):

        self.ui.labelInformation.setVisible(False)

        if self.videos:
            try:
                # TODO test
                # path = QtWidgets.QFileDialog.getExistingDirectory(
                #     self,
                #     "Otwórz",
                #     expanduser("~"),
                #     QtWidgets.QFileDialog.ShowDirsOnly
                # )

                # TODO test
                path = '/home/marek/Desktop'

                if path and self.videos:
                    self.ui.progressBarSingle.setVisible(False)
                    self.ui.progressBarMultiple.setVisible(False)

                    self.thread = QThread()
                    self.downloader = Downloader(path, self.videos,
                                                 self.get_options())

                    self.downloader.moveToThread(self.thread)
                    self.thread.started.connect(self.downloader.download)
                    self.download_is_running(True)
                    self.downloader.finished.connect(
                        lambda: self.download_is_running(False))
                    self.downloader.error_occured.connect(self.print_error)

                    self.thread.start()

            except yt.DownloadError:
                self.print_error(
                    "Download is not possible.\nCheck you internet connection or link."
                )

    def update_list(self, videos):

        if 'entries' in videos:
            for video in videos['entries']:
                if video['title'] not in (v.original_title
                                          for v in self.videos):
                    record = Video(video['url'], video['title'],
                                   video['duration'], video['thumbnail'])
                    record.is_checked = True
                    record.filesize = self.get_filesize(
                        video['formats'],
                        'best')  # TODO now its only best quality
                    self.videos.append(record)

        else:

            if videos['title'] not in (v.original_title for v in self.videos):
                record = Video(videos['url'], videos['title'],
                               videos['duration'], videos['thumbnail'])
                record.is_checked = True
                record.filesize = self.get_filesize(
                    videos['formats'],
                    'best')  # TODO now its only best quality
                self.videos.append(record)

        self.show_list()

    @pyqtSlot()
    def check_all(self):

        self.ui.labelInformation.setVisible(False)

        if self.videos:
            for v in self.videos:
                v.is_checked = not self.checked_all

            self.checked_all = not self.checked_all

            text = 'Odznacz' if self.checked_all else 'Zaznacz'
            self.ui.checkAllButton.setText(text)

    def show_list(self):

        self.ui.listWidget.clear()

        for video in self.videos:
            # create an item
            item = QtWidgets.QListWidgetItem(self.ui.listWidget)
            # create a custom widget
            item_widget = ListItem()

            # set thumbnail
            url = video.thumbnail
            data = urllib.request.urlopen(url).read()

            image = QtGui.QImage()
            image.loadFromData(data)
            image = image.scaled(100, 100, QtCore.Qt.KeepAspectRatio)
            item_widget.setPixmap(QtGui.QPixmap(image))

            item_widget.setTitle(video.title)
            item_widget.setDuration(video.duration)

            item_widget.setChecked(video.is_checked)
            # self.checkboxes.append(Item_Widget.getCheckBox())

            # set the size from the item to the same of the widget
            item.setSizeHint(item_widget.sizeHint())
            # Item.setFlags(Item.flags() | QtCore.Qt.ItemIsSelectable)  # TODO not working
            # Item.
            # I add it to the list
            self.ui.listWidget.addItem(item)
            # self.items.append(Item)
            video.checkbox = item_widget.getCheckBox()
            video.line_edit = item_widget.getLineEdit()

            self.ui.listWidget.setItemWidget(item, item_widget)

    @pyqtSlot()
    def clean_list(self):

        self.videos = []
        self.checked_all = False
        self.ui.labelInformation.setVisible(False)
        self.ui.progressBarSingle.setVisible(False)
        self.ui.progressBarMultiple.setVisible(False)
        self.show_list()

    def download_is_running(self, is_running):
        self.ui.downloadButton.setEnabled(not is_running)
        self.ui.progressBarSingle.setVisible(is_running)
        self.ui.progressBarMultiple.setVisible(is_running)

        if self.thread:
            self.thread.exit()

    def get_options(self):
        if self.ui.mp3CheckBox.isChecked():
            return self.mp3_options
        else:
            return self.video_options

    @pyqtSlot(str)
    def print_error(self, msg):
        self.ui.labelInformation.setVisible(True)
        self.ui.labelInformation.setText(msg)

    def get_filesize(self, formats, choosen_format):
        # TODO it's fake in case of MP3 files. Here is only video filesize
        if choosen_format == 'best':
            filesizes = [frm['filesize'] for frm in formats]
            print('----------------' + str(max(filesizes)))
            return max(filesizes)
        else:
            # TODO there is a problem in downloading videos at all
            raise NotImplementedError
Пример #33
0
 def start(self, entity, kwargs):
     self.entity = entity
     self.kwargs = kwargs
     QThread.start(self)
Пример #34
0
class MLSRuleQueryTab(AnalysisTab):

    """An MLS rule query."""

    def __init__(self, parent, policy, perm_map):
        super(MLSRuleQueryTab, self).__init__(parent)
        self.log = logging.getLogger(__name__)
        self.policy = policy
        self.query = MLSRuleQuery(policy)
        self.setupUi()

    def __del__(self):
        self.thread.quit()
        self.thread.wait(5000)
        logging.getLogger("setools.mlsrulequery").removeHandler(self.handler)

    def setupUi(self):
        self.load_ui("apol/mlsrulequery.ui")

        # set up source/target autocompletion
        typeattr_completion_list = [str(t) for t in self.policy.types()]
        typeattr_completion_list.extend(str(a) for a in self.policy.typeattributes())
        typeattr_completer_model = QStringListModel(self)
        typeattr_completer_model.setStringList(sorted(typeattr_completion_list))
        self.typeattr_completion = QCompleter()
        self.typeattr_completion.setModel(typeattr_completer_model)
        self.source.setCompleter(self.typeattr_completion)
        self.target.setCompleter(self.typeattr_completion)

        # setup indications of errors on source/target/default
        self.errors = set()
        self.orig_palette = self.source.palette()
        self.error_palette = self.source.palette()
        self.error_palette.setColor(QPalette.Base, Qt.red)
        self.clear_source_error()
        self.clear_target_error()
        self.clear_default_error()

        # populate class list
        self.class_model = SEToolsListModel(self)
        self.class_model.item_list = sorted(self.policy.classes())
        self.tclass.setModel(self.class_model)

        # set up results
        self.table_results_model = MLSRuleTableModel(self)
        self.sort_proxy = QSortFilterProxyModel(self)
        self.sort_proxy.setSourceModel(self.table_results_model)
        self.table_results.setModel(self.sort_proxy)
        self.table_results.sortByColumn(1, Qt.AscendingOrder)

        # set up processing thread
        self.thread = QThread()
        self.worker = QueryResultsUpdater(self.query, self.table_results_model)
        self.worker.moveToThread(self.thread)
        self.worker.raw_line.connect(self.raw_results.appendPlainText)
        self.worker.finished.connect(self.update_complete)
        self.worker.finished.connect(self.thread.quit)
        self.thread.started.connect(self.worker.update)

        # create a "busy, please wait" dialog
        self.busy = QProgressDialog(self)
        self.busy.setModal(True)
        self.busy.setRange(0, 0)
        self.busy.setMinimumDuration(0)
        self.busy.canceled.connect(self.thread.requestInterruption)
        self.busy.reset()

        # update busy dialog from query INFO logs
        self.handler = LogHandlerToSignal()
        self.handler.message.connect(self.busy.setLabelText)
        logging.getLogger("setools.mlsrulequery").addHandler(self.handler)

        # Ensure settings are consistent with the initial .ui state
        self.set_source_regex(self.source_regex.isChecked())
        self.set_target_regex(self.target_regex.isChecked())
        self.criteria_frame.setHidden(not self.criteria_expander.isChecked())
        self.notes.setHidden(not self.notes_expander.isChecked())

        # connect signals
        self.buttonBox.clicked.connect(self.run)
        self.clear_ruletypes.clicked.connect(self.clear_all_ruletypes)
        self.all_ruletypes.clicked.connect(self.set_all_ruletypes)
        self.source.textEdited.connect(self.clear_source_error)
        self.source.editingFinished.connect(self.set_source)
        self.source_regex.toggled.connect(self.set_source_regex)
        self.target.textEdited.connect(self.clear_target_error)
        self.target.editingFinished.connect(self.set_target)
        self.target_regex.toggled.connect(self.set_target_regex)
        self.tclass.selectionModel().selectionChanged.connect(self.set_tclass)
        self.invert_class.clicked.connect(self.invert_tclass_selection)
        self.default_range.textEdited.connect(self.clear_default_error)
        self.default_range.editingFinished.connect(self.set_default_range)

    #
    # Ruletype criteria
    #

    def _set_ruletypes(self, value):
        self.range_transition.setChecked(value)

    def set_all_ruletypes(self):
        self._set_ruletypes(True)

    def clear_all_ruletypes(self):
        self._set_ruletypes(False)

    #
    # Source criteria
    #

    def clear_source_error(self):
        self.clear_criteria_error(self.source, "Match the source type/attribute of the rule.")

    def set_source(self):
        try:
            self.query.source = self.source.text()
        except Exception as ex:
            self.log.error("Source type/attribute error: {0}".format(ex))
            self.set_criteria_error(self.source, ex)

    def set_source_regex(self, state):
        self.log.debug("Setting source_regex {0}".format(state))
        self.query.source_regex = state
        self.clear_source_error()
        self.set_source()

    #
    # Target criteria
    #

    def clear_target_error(self):
        self.clear_criteria_error(self.target, "Match the target type/attribute of the rule.")

    def set_target(self):
        try:
            self.query.target = self.target.text()
        except Exception as ex:
            self.log.error("Target type/attribute error: {0}".format(ex))
            self.set_criteria_error(self.target, ex)

    def set_target_regex(self, state):
        self.log.debug("Setting target_regex {0}".format(state))
        self.query.target_regex = state
        self.clear_target_error()
        self.set_target()

    #
    # Class criteria
    #

    def set_tclass(self):
        selected_classes = []
        for index in self.tclass.selectionModel().selectedIndexes():
            selected_classes.append(self.class_model.data(index, Qt.UserRole))

        self.query.tclass = selected_classes

    def invert_tclass_selection(self):
        invert_list_selection(self.tclass.selectionModel())

    #
    # Default criteria
    #

    def clear_default_error(self):
        self.clear_criteria_error(self.default_range, "Match the default type the rule.")

    def set_default_range(self):
        try:
            self.query.default = self.default_range.text()
        except Exception as ex:
            self.log.error("Default range error: {0}".format(ex))
            self.set_criteria_error(self.default_range, ex)

    #
    # Save/Load tab
    #
    def save(self):
        """Return a dictionary of settings."""
        if self.errors:
            raise TabFieldError("Field(s) are in error: {0}".
                                format(" ".join(o.objectName() for o in self.errors)))

        settings = {}
        save_checkboxes(self, settings, ["criteria_expander", "notes_expander",
                                         "range_transition",
                                         "source_indirect", "source_regex",
                                         "target_indirect", "target_regex"])
        save_lineedits(self, settings, ["source", "target", "default_range"])
        save_listviews(self, settings, ["tclass"])
        save_textedits(self, settings, ["notes"])
        return settings

    def load(self, settings):
        load_checkboxes(self, settings, ["criteria_expander", "notes_expander",
                                         "range_transition",
                                         "source_indirect", "source_regex",
                                         "target_indirect", "target_regex"])
        load_lineedits(self, settings, ["source", "target", "default_range"])
        load_listviews(self, settings, ["tclass"])
        load_textedits(self, settings, ["notes"])

    #
    # Results runner
    #

    def run(self, button):
        # right now there is only one button.

        self.query.ruletype = ['range_transition']
        self.query.source_indirect = self.source_indirect.isChecked()
        self.query.target_indirect = self.target_indirect.isChecked()

        # start processing
        self.busy.setLabelText("Processing query...")
        self.busy.show()
        self.raw_results.clear()
        self.thread.start()

    def update_complete(self, count):
        self.log.info("{0} MLS rule(s) found.".format(count))

        # update sizes/location of result displays
        if not self.busy.wasCanceled():
            self.busy.setLabelText("Resizing the result table's columns; GUI may be unresponsive")
            self.busy.repaint()
            self.table_results.resizeColumnsToContents()

        if not self.busy.wasCanceled():
            self.busy.setLabelText("Resizing the result table's rows; GUI may be unresponsive")
            self.busy.repaint()
            self.table_results.resizeRowsToContents()

        if not self.busy.wasCanceled():
            self.busy.setLabelText("Moving the raw result to top; GUI may be unresponsive")
            self.busy.repaint()
            self.raw_results.moveCursor(QTextCursor.Start)

        self.busy.reset()
Пример #35
0
class InitialSIDQueryTab(SEToolsWidget, QScrollArea):

    """An initial SID query."""

    def __init__(self, parent, policy, perm_map):
        super(InitialSIDQueryTab, self).__init__(parent)
        self.log = logging.getLogger(__name__)
        self.policy = policy
        self.query = InitialSIDQuery(policy)
        self.setupUi()

    def __del__(self):
        self.thread.quit()
        self.thread.wait(5000)
        logging.getLogger("setools.initsidquery").removeHandler(self.handler)

    def setupUi(self):
        self.load_ui("initsidquery.ui")

        # set up user autocompletion
        user_completion_list = [str(u) for u in self.policy.users()]
        user_completer_model = QStringListModel(self)
        user_completer_model.setStringList(sorted(user_completion_list))
        self.user_completion = QCompleter()
        self.user_completion.setModel(user_completer_model)
        self.user.setCompleter(self.user_completion)

        # set up role autocompletion
        role_completion_list = [str(r) for r in self.policy.roles()]
        role_completer_model = QStringListModel(self)
        role_completer_model.setStringList(sorted(role_completion_list))
        self.role_completion = QCompleter()
        self.role_completion.setModel(role_completer_model)
        self.role.setCompleter(self.role_completion)

        # set up type autocompletion
        type_completion_list = [str(t) for t in self.policy.types()]
        type_completer_model = QStringListModel(self)
        type_completer_model.setStringList(sorted(type_completion_list))
        self.type_completion = QCompleter()
        self.type_completion.setModel(type_completer_model)
        self.type_.setCompleter(self.type_completion)

        # setup indications of errors on source/target/default
        self.orig_palette = self.type_.palette()
        self.error_palette = self.type_.palette()
        self.error_palette.setColor(QPalette.Base, Qt.red)
        self.clear_name_error()
        self.clear_user_error()
        self.clear_type_error()
        self.clear_role_error()
        self.clear_range_error()

        # set up results
        self.table_results_model = InitialSIDTableModel(self)
        self.sort_proxy = QSortFilterProxyModel(self)
        self.sort_proxy.setSourceModel(self.table_results_model)
        self.table_results.setModel(self.sort_proxy)
        self.table_results.sortByColumn(0, Qt.AscendingOrder)

        # set up processing thread
        self.thread = QThread()
        self.worker = QueryResultsUpdater(self.query, self.table_results_model)
        self.worker.moveToThread(self.thread)
        self.worker.raw_line.connect(self.raw_results.appendPlainText)
        self.worker.finished.connect(self.update_complete)
        self.worker.finished.connect(self.thread.quit)
        self.thread.started.connect(self.worker.update)

        # create a "busy, please wait" dialog
        self.busy = QProgressDialog(self)
        self.busy.setModal(True)
        self.busy.setRange(0, 0)
        self.busy.setMinimumDuration(0)
        self.busy.canceled.connect(self.thread.requestInterruption)
        self.busy.reset()

        # update busy dialog from query INFO logs
        self.handler = LogHandlerToSignal()
        self.handler.message.connect(self.busy.setLabelText)
        logging.getLogger("setools.initsidquery").addHandler(self.handler)

        # Ensure settings are consistent with the initial .ui state
        self.set_name_regex(self.name_regex.isChecked())
        self.criteria_frame.setHidden(not self.criteria_expander.isChecked())
        self.notes.setHidden(not self.notes_expander.isChecked())

        # Range criteria is available only if policy is MLS
        if not self.policy.mls:
            self.range_criteria.setEnabled(False)
            self.range_criteria.setToolTip("MLS is disabled in this policy.")
            self.range_.setToolTip("MLS is disabled in this policy.")
            self.range_exact.setToolTip("MLS is disabled in this policy.")
            self.range_overlap.setToolTip("MLS is disabled in this policy.")
            self.range_subset.setToolTip("MLS is disabled in this policy.")
            self.range_superset.setToolTip("MLS is disabled in this policy.")

        # connect signals
        self.buttonBox.clicked.connect(self.run)
        self.name.textEdited.connect(self.clear_name_error)
        self.name.editingFinished.connect(self.set_name)
        self.name_regex.toggled.connect(self.set_name_regex)
        self.user.textEdited.connect(self.clear_user_error)
        self.user.editingFinished.connect(self.set_user)
        self.user_regex.toggled.connect(self.set_user_regex)
        self.role.textEdited.connect(self.clear_role_error)
        self.role.editingFinished.connect(self.set_role)
        self.role_regex.toggled.connect(self.set_role_regex)
        self.type_.textEdited.connect(self.clear_type_error)
        self.type_.editingFinished.connect(self.set_type)
        self.type_regex.toggled.connect(self.set_type_regex)
        self.range_.textEdited.connect(self.clear_range_error)
        self.range_.editingFinished.connect(self.set_range)

    #
    # Name criteria
    #
    def clear_name_error(self):
        self.name.setToolTip("Match the name.")
        self.name.setPalette(self.orig_palette)

    def set_name(self):
        try:
            self.query.name = self.name.text()
        except Exception as ex:
            self.log.error("Name error: {0}".format(ex))
            self.name.setToolTip("Error: " + str(ex))
            self.name.setPalette(self.error_palette)

    def set_name_regex(self, state):
        self.log.debug("Setting name_regex {0}".format(state))
        self.query.name_regex = state
        self.clear_name_error()
        self.set_name()

    #
    # User criteria
    #
    def clear_user_error(self):
        self.user.setToolTip("Match the user of the context.")
        self.user.setPalette(self.orig_palette)

    def set_user(self):
        try:
            self.query.user = self.user.text()
        except Exception as ex:
            self.log.error("Context user error: {0}".format(ex))
            self.user.setToolTip("Error: " + str(ex))
            self.user.setPalette(self.error_palette)

    def set_user_regex(self, state):
        self.log.debug("Setting user_regex {0}".format(state))
        self.query.user_regex = state
        self.clear_user_error()
        self.set_user()

    #
    # Role criteria
    #
    def clear_role_error(self):
        self.role.setToolTip("Match the role of the context.")
        self.role.setPalette(self.orig_palette)

    def set_role(self):
        try:
            self.query.role = self.role.text()
        except Exception as ex:
            self.log.error("Context role error: {0}".format(ex))
            self.role.setToolTip("Error: " + str(ex))
            self.role.setPalette(self.error_palette)

    def set_role_regex(self, state):
        self.log.debug("Setting role_regex {0}".format(state))
        self.query.role_regex = state
        self.clear_role_error()
        self.set_role()

    #
    # Type criteria
    #
    def clear_type_error(self):
        self.type_.setToolTip("Match the type of the context.")
        self.type_.setPalette(self.orig_palette)

    def set_type(self):
        try:
            self.query.type_ = self.type_.text()
        except Exception as ex:
            self.log.error("Context type error: {0}".format(ex))
            self.type_.setToolTip("Error: " + str(ex))
            self.type_.setPalette(self.error_palette)

    def set_type_regex(self, state):
        self.log.debug("Setting type_regex {0}".format(state))
        self.query.type_regex = state
        self.clear_type_error()
        self.set_type()

    #
    # Range criteria
    #
    def clear_range_error(self):
        self.range_.setToolTip("Match the range of the context.")
        self.range_.setPalette(self.orig_palette)

    def set_range(self):
        try:
            self.query.range_ = self.range_.text()
        except Exception as ex:
            self.log.info("Context range error: " + str(ex))
            self.range_.setToolTip("Error: " + str(ex))
            self.range_.setPalette(self.error_palette)

    #
    # Results runner
    #
    def run(self, button):
        # right now there is only one button.
        self.query.range_overlap = self.range_overlap.isChecked()
        self.query.range_subset = self.range_subset.isChecked()
        self.query.range_superset = self.range_superset.isChecked()

        # start processing
        self.busy.setLabelText("Processing query...")
        self.busy.show()
        self.raw_results.clear()
        self.thread.start()

    def update_complete(self, count):
        self.log.info("{0} initial SID statment(s) found.".format(count))

        # update sizes/location of result displays
        if not self.busy.wasCanceled():
            self.busy.setLabelText("Resizing the result table's columns; GUI may be unresponsive")
            self.busy.repaint()
            self.table_results.resizeColumnsToContents()

        if not self.busy.wasCanceled():
            self.busy.setLabelText("Resizing the result table's rows; GUI may be unresponsive")
            self.busy.repaint()
            self.table_results.resizeRowsToContents()

        if not self.busy.wasCanceled():
            self.busy.setLabelText("Moving the raw result to top; GUI may be unresponsive")
            self.busy.repaint()
            self.raw_results.moveCursor(QTextCursor.Start)

        self.busy.reset()
Пример #36
0
class NetworkHandler:
    def __init__(self, addr, port):
        self.addr = (addr, port)
        self.s = connector_client(addr, port)
        if self.s == "offline":
            logger.log("error", "No route to server")
            logger.log("info", "Starting internal server")
            self.offline = True
            self.server_thread = threading.Thread(target=self.server_listner)
            self.server_thread.start()
            time.sleep(0.8)
            self.s = connector_client(addr, port)
        else:
            self.offline = False

        self.netQueue = []
        #self.listening_thread = threading.Thread(target=self.listening)
        #self.listening_thread.start()
        self.listening_thread = QThread()
        self.worker = AsyncThreadThing(self)
        self.worker.moveToThread(self.listening_thread)
        self.listening_thread.started.connect(self.worker.run)
        self.worker.update.connect(lambda n: self.textbox.setPlainText(n))
        self.listening_thread.start()

    def recv_data(self):
        while len(self.netQueue) == 0:
            pass
        return self.netQueue.pop()

    def server_listner(self):
        s = connecter_server(self.addr[0], self.addr[1])
        conn, addr = s.accept()

        self.con = Connection(conn)
        self.con.run()

    def send_data(self, data):
        #time.sleep(0.5)
        string = json.dumps(data)
        string = struct.pack('>I', len(
            string.encode("utf-8"))) + string.encode("utf-8")
        self.s.sendall(string)

    def recv_data_raw(self):
        raw_msglen = self.recvall(4)
        if not raw_msglen:
            return None
        msglen = struct.unpack('>I', raw_msglen)[0]
        s = self.recvall(msglen).decode("utf-8")
        return json.loads(s)

    def recvall(self, n):
        data = b''
        while len(data) < n:
            packet = self.s.recv(n - len(data))
            if not packet:
                return None
            data += packet
        return data

    def sendUpdate(self, text):
        self.send_data({"request": "update", "data": text})

    def close(self):
        self.s.close()
        if self.offline:
            self.con.exit_()
Пример #37
0
class MDTGUISingleModel(QMainWindow, Ui_MainWindow):
    def __init__(self, shared_state, computations_thread):
        super().__init__()
        self.setupUi(self)
        self._shared_state = shared_state

        self._computations_thread = computations_thread
        self._computations_thread.signal_starting.connect(
            self.computations_started)
        self._computations_thread.signal_finished.connect(
            self.computations_finished)

        self._stdout_old = sys.stdout
        self._stderr_old = sys.stderr
        self._logging_update_queue = Queue()
        self._logging_update_thread = QThread()

        self._message_receiver = MessageReceiver(self._logging_update_queue)
        self._message_receiver.text_message_signal.connect(self.update_log)

        self._message_receiver.moveToThread(self._logging_update_thread)
        self._logging_update_thread.started.connect(self._message_receiver.run)
        self._logging_update_thread.start()

        sys.stdout = ForwardingListener(self._logging_update_queue)
        sys.stderr = ForwardingListener(self._logging_update_queue)
        LogDispatchHandler.add_listener(
            ForwardingListener(self._logging_update_queue))
        print_welcome_message()

        self.actionExit.setShortcuts(['Ctrl+q', 'Ctrl+w'])

        self.action_RuntimeSettings.triggered.connect(
            lambda: RuntimeSettingsDialog(self).exec_())
        self.action_MapsVisualizer.triggered.connect(
            lambda: mdt.gui.maps_visualizer.main.start_gui(app_exec=False))
        self.actionAbout.triggered.connect(lambda: AboutDialog(self).exec_())
        self.action_GetExampleData.triggered.connect(
            lambda: GetExampleDataDialog(self, shared_state).exec_())

        self.executionStatusLabel.setText('Idle')
        self.executionStatusIcon.setPixmap(
            QtGui.QPixmap(":/main_gui/icon_status_red.png"))

        self.fit_model_tab = FitModelTab(shared_state,
                                         self._computations_thread)
        self.fit_model_tab.setupUi(self.fitModelTab)

        self.generate_mask_tab = GenerateBrainMaskTab(
            shared_state, self._computations_thread)
        self.generate_mask_tab.setupUi(self.generateBrainMaskTab)

        self.generate_roi_mask_tab = GenerateROIMaskTab(
            shared_state, self._computations_thread)
        self.generate_roi_mask_tab.setupUi(self.generateROIMaskTab)

        self.generate_protocol_tab = GenerateProtocolTab(
            shared_state, self._computations_thread)
        self.generate_protocol_tab.setupUi(self.generateProtocolTab)

        self.tabs = [
            self.fit_model_tab, self.generate_mask_tab,
            self.generate_roi_mask_tab, self.generate_protocol_tab
        ]

        self.MainTabs.currentChanged.connect(
            lambda index: self.tabs[index].tab_opened())

    def closeEvent(self, event):
        sys.stdout = self._stdout_old
        sys.stderr = self._stderr_old
        self._message_receiver.is_running = False
        self._logging_update_thread.quit()
        self._logging_update_thread.wait(10)
        super().closeEvent(event)

    def send_sigint(self, *args):
        self.close()

    @pyqtSlot()
    def computations_started(self):
        self.executionStatusLabel.setText('Computing')
        self.executionStatusIcon.setPixmap(
            QtGui.QPixmap(":/main_gui/icon_status_green.png"))

    @pyqtSlot()
    def computations_finished(self):
        self.executionStatusLabel.setText('Idle')
        self.executionStatusIcon.setPixmap(
            QtGui.QPixmap(":/main_gui/icon_status_red.png"))

    @pyqtSlot(str)
    def update_log(self, string):
        sb = self.loggingTextBox.verticalScrollBar()
        scrollbar_position = sb.value()
        autoscroll = scrollbar_position == sb.maximum()
        self.loggingTextBox.moveCursor(QtGui.QTextCursor.End)
        self.loggingTextBox.insertPlainText(string)

        if autoscroll:
            sb.setValue(sb.maximum())
        else:
            sb.setValue(scrollbar_position)
Пример #38
0
class ClusteringView(QObject):
    """
    A viewer for viewing clustering result of OPTICS-APT, showing ion maps, RD, RD histogram.
    """
    def __init__(self, Clusterer, show_bg, point_size=3):
        super().__init__()

        self.clustering_view = QWidget()
        self.layout = QGridLayout()
        self.clustering_view.setLayout(self.layout)

        self.RD_plot = pg.PlotWidget()
        self.RD_hist = pg.PlotWidget()
        self.point_cloud_view = gl.GLViewWidget()

        self.RD_plot.sizeHint = pg.QtCore.QSize(800, 600)
        self.point_cloud_view.sizeHint = lambda: pg.QtCore.QSize(800, 600)
        self.point_cloud_view.setSizePolicy(self.RD_plot.sizePolicy())

        self.layout.addWidget(self.point_cloud_view, 0, 0, 2, 1)
        self.layout.addWidget(self.RD_plot, 0, 1)
        self.layout.addWidget(self.RD_hist, 1, 1)

        self.point_cloud_view.setBackgroundColor('w')
        self.point_cloud_view.opts['distance'] = 2000
        self.point_cloud_view.opts['fov'] = 1

        self.thread_show_clustering = QThread()
        self.ShowClustering = ShowClustering(Clusterer, point_size, show_bg)
        self.ShowClustering.gl_finished.connect(
            self.thread_show_clustering.quit)
        self.ShowClustering.gl_ready.connect(self.point_cloud_ready)
        self.ShowClustering.moveToThread(self.thread_show_clustering)
        self.thread_show_clustering.started.connect(
            self.ShowClustering.show_clustering)
        self.thread_show_clustering.start()

        # start plot RD
        ordered_RD = Clusterer.RD[Clusterer.ordered_lst]
        self.RD_plot.setBackground('w')
        self.RD_plot.setDownsampling(auto=True)
        self.RD_plot.plot(ordered_RD, pen=pg.mkPen('k', width=2))

        color_table = ColorTable.gen_color_table(style='int')
        color_table.pop(0)
        cycol = cycle(color_table)
        # num_leaves = len(leaves)
        leaves = Clusterer.obtain_leaf_nodes()
        count = 0
        for node in leaves:
            if node.is_cluster:
                item = node.index_range
                ave_RD = node.average_RD(ordered_RD)
                self.RD_plot.plot(item, [ave_RD, ave_RD],
                                  pen=pg.mkPen(color=next(cycol), width=2))
                count += 1

        # start plot RD hist
        self.RD_hist.setBackground('w')
        hist, bins = np.histogram(Clusterer.RD, bins=50, density=True)
        curve = pg.PlotCurveItem(bins,
                                 hist,
                                 pen=pg.mkPen('b', width=2),
                                 stepMode=True)
        self.RD_hist.addItem(curve)

        # self.thread_show_RD = QThread()

        # self.thread_show_RD_hist = QThread()

        self.clustering_view.show()

    @pyqtSlot(float, name='processing_time')
    def processing_time(self, t):
        print('processing time is', t, 's')

    @pyqtSlot(object, name='point_cloud_ready')
    def point_cloud_ready(self, sp1):
        self.point_cloud_view.addItem(sp1)

    @pyqtSlot(object, name='RD_ready')
    def RD_ready(self, item):
        # self.point_cloud_view.addItem(sp1)
        pass

    @pyqtSlot(object, name='RD_hist_ready')
    def RD_hist_ready(self, item):
        # self.point_cloud_view.addItem(sp1)
        pass
Пример #39
0
class MainWindow(QMainWindow):
    """
    Defines the mainWindow class for the neurodecode GUI.
    """

    hide_recordTerminal = pyqtSignal(bool)
    signal_error = pyqtSignal(str)

    #----------------------------------------------------------------------
    def __init__(self):
        """
        Constructor.
        """
        super(MainWindow, self).__init__()

        self.cfg_struct = None  # loaded module containing all param possible values
        self.cfg_subject = None  # loaded module containing subject specific values
        self.paramsWidgets = {}  # dict of all the created parameters widgets

        self.load_ui_from_file()

        self.redirect_stdout()

        self.connect_signals_to_slots()

        # Define in which modality we are
        self.modality = None

        # Recording process
        self.record_terminal = None
        self.recordLogger = logging.getLogger('recorder')
        self.recordLogger.propagate = False
        init_logger(self.recordLogger)

        # To display errors
        self.error_dialog = QErrorMessage(self)
        self.error_dialog.setWindowTitle('Warning')

        # Mp sharing variables
        self.record_state = mp.Value('i', 0)
        self.protocol_state = mp.Value('i', 0)
        self.lsl_state = mp.Value('i', 0)
        self.viewer_state = mp.Value('i', 0)

        # Disable widgets
        self.ui.groupBox_Modality.setEnabled(False)
        self.ui.groupBox_Launch.setEnabled(False)

    # ----------------------------------------------------------------------
    def redirect_stdout(self):
        """
        Create Queue and redirect sys.stdout to this queue.
        Create thread that will listen on the other end of the queue, and send the text to the textedit_terminal.
        """
        queue = mp.Queue()

        self.thread = QThread()

        self.my_receiver = MyReceiver(queue)
        self.my_receiver.mysignal.connect(self.on_terminal_append)
        self.my_receiver.moveToThread(self.thread)

        self.thread.started.connect(self.my_receiver.run)
        self.thread.start()

        redirect_stdout_to_queue(logger, self.my_receiver.queue, 'INFO')

    #----------------------------------------------------------------------
    def load_ui_from_file(self):
        """
        Loads the UI interface from file.
        """
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # Protocol terminal
        self.ui.textEdit_terminal.setReadOnly(1)
        font = QFont()
        font.setPointSize(10)
        self.ui.textEdit_terminal.setFont(font)

        # Viewer button
        self.ui.pushButton_Viewer.setEnabled(False)

    #----------------------------------------------------------------------
    def clear_params(self):
        """
        Clear all previously loaded params widgets.
        """

        if self.ui.scrollAreaWidgetContents_Basics.layout() != None:
            QWidget().setLayout(self.ui.scrollAreaWidgetContents_Adv.layout())
            QWidget().setLayout(
                self.ui.scrollAreaWidgetContents_Basics.layout())

    # ----------------------------------------------------------------------
    def extract_value_from_module(self, key, values):
        """
        Extracts the subject's specific value associated with key.
        key = parameter name.
        values = list of all the parameters values.
        """
        for v in values:
            if v[0] == key:
                return v[1]

    # ----------------------------------------------------------------------
    def disp_params(self, cfg_template_module, cfg_module):
        """
        Displays the parameters in the corresponding UI scrollArea.
        cfg = config module
        """

        self.clear_params()
        # Extract the parameters and their possible values from the template modules.
        params = inspect.getmembers(cfg_template_module)

        # Extract the chosen values from the subject's specific module.
        all_chosen_values = inspect.getmembers(cfg_module)

        filePath = self.ui.lineEdit_pathSearch.text()

        # Load channels
        if self.modality == 'trainer':
            subjectDataPath = Path(
                '%s/%s/%s/fif' %
                (os.environ['NEUROD_DATA'], filePath.split('/')[-2],
                 filePath.split('/')[-1]))
            self.channels = read_params_from_file(subjectDataPath,
                                                  'channelsList.txt')

        self.directions = ()

        # Iterates over the classes
        for par in range(2):
            param = inspect.getmembers(params[par][1])
            # Create layouts
            layout = QFormLayout()

            # Iterates over the list
            for p in param:
                # Remove useless attributes
                if '__' in p[0]:
                    continue

                # Iterates over the dict
                for key, values in p[1].items():
                    chosen_value = self.extract_value_from_module(
                        key, all_chosen_values)

                    # For the feedback directions [offline and online].
                    if 'DIRECTIONS' in key:
                        self.directions = values

                        if self.modality is 'offline':
                            nb_directions = 4
                            directions = Connect_Directions(
                                key, chosen_value, values, nb_directions)

                        elif self.modality is 'online':
                            chosen_events = [
                                event[1] for event in chosen_value
                            ]
                            chosen_value = [val[0] for val in chosen_value]
                            nb_directions = len(chosen_value)
                            directions = Connect_Directions_Online(
                                key, chosen_value, values, nb_directions,
                                chosen_events, [None])

                        directions.signal_paramChanged[str, list].connect(
                            self.on_guichanges)
                        self.paramsWidgets.update({key: directions})
                        layout.addRow(key, directions.l)

                    # For the special case of choosing the trigger classes to train on
                    elif 'TRIGGER_DEF' in key:

                        trigger_def = Connect_Directions(
                            key, chosen_value, [None], 4)
                        trigger_def.signal_paramChanged[str, list].connect(
                            self.on_guichanges)
                        self.paramsWidgets.update({key: trigger_def})
                        layout.addRow(key, trigger_def.l)

                    # For providing a folder path.
                    elif 'PATH' in key:
                        pathfolderfinder = PathFolderFinder(
                            key, os.environ['NEUROD_DATA'], chosen_value)
                        pathfolderfinder.signal_pathChanged[str, str].connect(
                            self.on_guichanges)
                        pathfolderfinder.signal_error[str].connect(
                            self.on_error)
                        self.paramsWidgets.update({key: pathfolderfinder})
                        layout.addRow(key, pathfolderfinder.layout)

                        if not chosen_value:
                            self.signal_error[str].emit(
                                key +
                                ' is empty! Provide a path before starting.')
                        continue

                    # For providing a file path.
                    elif 'FILE' in key:
                        pathfilefinder = PathFileFinder(key, chosen_value)
                        pathfilefinder.signal_pathChanged[str, str].connect(
                            self.on_guichanges)
                        pathfilefinder.signal_error[str].connect(self.on_error)
                        self.paramsWidgets.update({key: pathfilefinder})
                        layout.addRow(key, pathfilefinder.layout)

                        if not chosen_value:
                            self.signal_error[str].emit(
                                key +
                                ' is empty! Provide a file before starting.')

                    # To select specific electrodes
                    elif '_CHANNELS' in key or 'CHANNELS_' in key:
                        ch_select = Channel_Select(key, self.channels,
                                                   chosen_value)
                        ch_select.signal_paramChanged[str, list].connect(
                            self.on_guichanges)
                        self.paramsWidgets.update({key: ch_select})
                        layout.addRow(key, ch_select.layout)

                    elif 'BIAS' in key:
                        #  Add None to the list in case of no bias wanted
                        self.directions = tuple([None] + list(self.directions))
                        bias = Connect_Bias(key, self.directions, chosen_value)
                        bias.signal_paramChanged[str, object].connect(
                            self.on_guichanges)
                        self.paramsWidgets.update({key: bias})
                        layout.addRow(key, bias.l)

                    # For all the int values.
                    elif values is int:
                        spinBox = Connect_SpinBox(key, chosen_value)
                        spinBox.signal_paramChanged[str, int].connect(
                            self.on_guichanges)
                        self.paramsWidgets.update({key: spinBox})
                        layout.addRow(key, spinBox)

                    # For all the float values.
                    elif values is float:
                        doublespinBox = Connect_DoubleSpinBox(
                            key, chosen_value)
                        doublespinBox.signal_paramChanged[str, float].connect(
                            self.on_guichanges)
                        self.paramsWidgets.update({key: doublespinBox})
                        layout.addRow(key, doublespinBox)

                    # For parameters with multiple non-fixed values in a list (user can modify them)
                    elif values is list:
                        modifiable_list = Connect_Modifiable_List(
                            key, chosen_value)
                        modifiable_list.signal_paramChanged[str, list].connect(
                            self.on_guichanges)
                        self.paramsWidgets.update({key: modifiable_list})
                        layout.addRow(key, modifiable_list)
                        continue

                    #  For parameters containing a string to modify
                    elif values is str:
                        lineEdit = Connect_LineEdit(key, chosen_value)
                        lineEdit.signal_paramChanged[str, str].connect(
                            self.on_guichanges)
                        lineEdit.signal_paramChanged[str, type(None)].connect(
                            self.on_guichanges)
                        self.paramsWidgets.update({key: lineEdit})
                        layout.addRow(key, lineEdit)

                    # For parameters with multiple fixed values.
                    elif type(values) is tuple:
                        comboParams = Connect_ComboBox(key, chosen_value,
                                                       values)
                        comboParams.signal_paramChanged[str, object].connect(
                            self.on_guichanges)
                        comboParams.signal_additionalParamChanged[
                            str, dict].connect(self.on_guichanges)
                        self.paramsWidgets.update({key: comboParams})
                        layout.addRow(key, comboParams.layout)
                        continue

                    # For parameters with multiple non-fixed values in a dict (user can modify them)
                    elif type(values) is dict:
                        try:
                            selection = chosen_value['selected']
                            comboParams = Connect_ComboBox(
                                key, chosen_value, values)
                            comboParams.signal_paramChanged[
                                str, object].connect(self.on_guichanges)
                            comboParams.signal_additionalParamChanged[
                                str, dict].connect(self.on_guichanges)
                            self.paramsWidgets.update({key: comboParams})
                            layout.addRow(key, comboParams.layout)
                        except:
                            modifiable_dict = Connect_Modifiable_Dict(
                                key, chosen_value, values)
                            modifiable_dict.signal_paramChanged[
                                str, dict].connect(self.on_guichanges)
                            self.paramsWidgets.update({key: modifiable_dict})
                            layout.addRow(key, modifiable_dict)

                # Add a horizontal line to separate parameters' type.
                if p != param[-1]:
                    separator = QFrame()
                    separator.setFrameShape(QFrame.HLine)
                    separator.setFrameShadow(QFrame.Sunken)
                    layout.addRow(separator)

                # Display the parameters according to their types.
                if params[par][0] == 'Basic':
                    self.ui.scrollAreaWidgetContents_Basics.setLayout(layout)
                elif params[par][0] == 'Advanced':
                    self.ui.scrollAreaWidgetContents_Adv.setLayout(layout)

        # Connect inter-widgets signals and slots
        if self.modality == 'trainer':
            self.paramsWidgets['TRIGGER_FILE'].signal_pathChanged[
                str, str].connect(trigger_def.on_new_tdef_file)
            self.paramsWidgets['TRIGGER_FILE'].on_selected()

        if self.modality == 'online':
            self.paramsWidgets['TRIGGER_FILE'].signal_pathChanged[
                str, str].connect(directions.on_new_tdef_file)
            self.paramsWidgets['TRIGGER_FILE'].on_selected()

            self.paramsWidgets['DECODER_FILE'].signal_pathChanged[
                str, str].connect(directions.on_new_decoder_file)
            self.paramsWidgets['DECODER_FILE'].on_selected()

    # ----------------------------------------------------------------------
    def load_config(self, cfg_file):
        """
        Dynamic loading of a config file.
        Format the lib to fit the previous developed neurodecode code if subject specific file (not for the templates).
        cfg_file: tuple containing the path and the config file name.
        """
        if self.cfg_subject == None or cfg_file[
                1] not in self.cfg_subject.__file__:
            # Dynamic loading
            sys.path.append(cfg_file[0])
            cfg_module = import_module(cfg_file[1].split('.')[0])
        else:
            cfg_module = reload(self.cfg_subject)

        return cfg_module

    #----------------------------------------------------------------------
    def load_all_params(self, cfg_template, cfg_file):
        """
        Loads the params structure and assign the subject/s specific value.
        It also checks the sanity of the loaded params according to the protocol.
        """
        try:
            # Loads the subject's specific values
            self.cfg_subject = self.load_config(cfg_file)

            # Loads the template
            if self.cfg_struct == None or cfg_template[
                    1] not in self.cfg_struct.__file__:
                self.cfg_struct = self.load_config(cfg_template)

            # Display parameters on the GUI
            self.disp_params(self.cfg_struct, self.cfg_subject)

            # Check the parameters integrity
            self.m.check_config(self.cfg_subject)

        except Exception as e:
            self.signal_error[str].emit(str(e))

    @pyqtSlot(str, str)
    @pyqtSlot(str, bool)
    @pyqtSlot(str, list)
    @pyqtSlot(str, float)
    @pyqtSlot(str, int)
    @pyqtSlot(str, dict)
    @pyqtSlot(str, tuple)
    @pyqtSlot(str, type(None))
    # ----------------------------------------------------------------------
    def on_guichanges(self, name, new_Value):
        """
        Apply the modification to the corresponding param of the cfg module

        name = parameter name
        new_value = new str value to to change in the module
        """

        # In case of a dict containing several option (contains 'selected')
        try:
            tmp = getattr(self.cfg_subject, name)
            tmp['selected'] = new_Value['selected']
            tmp[new_Value['selected']] = new_Value[new_Value['selected']]
            setattr(self.cfg_subject, name, tmp)
        # In case of simple data format
        except:
            setattr(self.cfg_subject, name, new_Value)

        print("The parameter %s has been changed to %s" %
              (name, getattr(self.cfg_subject, name)))

    # ----------------------------------------------------------------------
    @pyqtSlot()
    def on_click_pathSearch(self):
        """
        Opens the File dialog window when the search button is pressed.
        """

        buttonIcon = self.ui.pushButton_Search.text()

        if buttonIcon == 'Search':
            path_name = QFileDialog.getExistingDirectory(
                caption="Choose the subject's directory",
                directory=os.environ['NEUROD_SCRIPTS'])

            if path_name:
                self.ui.lineEdit_pathSearch.clear()
                self.ui.lineEdit_pathSearch.insert(path_name)
                self.ui.pushButton_Search.setText('Accept')
                self.ui.pushButton_Search.setStyleSheet("color: red;")
        else:
            self.ui.pushButton_Search.setText('Search')
            self.ui.pushButton_Search.setStyleSheet("color: black;")
            self.on_enable_modality()

    # ----------------------------------------------------------------------
    def look_for_subject_file(self, modality):
        '''
        Look if the subject config file is contained in the subject folder
        
        modality = offline, trainer or online
        '''
        is_found = False
        cfg_file = None
        cfg_path = Path(self.ui.lineEdit_pathSearch.text())

        for f in glob(os.fspath(cfg_path / "*.py"), recursive=False):
            fileName = os.path.split(f)[-1]
            if modality in fileName and 'structure' not in fileName:
                is_found = True
                cfg_file = f
                break
        return is_found, cfg_file

    #----------------------------------------------------------------------
    def find_structure_file(self, cfg_file, modality):
        """
        Find the structure config file associated with the subject config file
        
        cfg_file: subject specific config file
        modality = offline, trainer or online
        """
        # Find the config template
        tmp = cfg_file.split('.')[0]  # Remove the .py
        self.protocol = tmp.split('-')[-1]  # Extract the protocol name
        template_path = Path(
            os.environ['NEUROD_ROOT']
        ) / 'neurodecode' / 'config_files' / self.protocol / 'structure_files'

        for f in glob(os.fspath(template_path / "*.py"), recursive=False):
            fileName = os.path.split(f)[-1]
            if modality in fileName and 'structure' in fileName:
                return f

    #----------------------------------------------------------------------
    def prepare_config_files(self, modality):
        """
        Find both the subject config file and the associated structure config
        file paths
        """
        is_found, cfg_file = self.look_for_subject_file(modality)

        if is_found is False:
            self.error_dialog.showMessage(
                'Config file missing: copy an ' + modality +
                ' config file to the subject folder or create a new subjet')
            return None, None
        else:
            cfg_template = self.find_structure_file(cfg_file, modality)
            cfg_file = os.path.split(cfg_file)
            cfg_template = os.path.split(cfg_template)

            return cfg_file, cfg_template

    # ----------------------------------------------------------------------
    def load_protocol_module(self, module_name):
        """
        Load or reload the protocol's module associated with the modality
        
        module_name = name of the module to load
        """
        if module_name not in sys.modules:
            path2protocol = os.path.split(
                self.ui.lineEdit_pathSearch.text())[0]
            sys.path.append(path2protocol)
            self.m = import_module(module_name)
        else:
            self.m = reload(sys.modules[module_name])

    # ----------------------------------------------------------------------
    @pyqtSlot()
    def on_click_offline(self):
        """
        Loads the Offline parameters.
        """
        self.modality = 'offline'

        cfg_file, cfg_template = self.prepare_config_files(self.modality)
        module_name = 'offline_' + self.protocol

        self.load_protocol_module(module_name)

        self.ui.checkBox_Record.setChecked(True)
        self.ui.checkBox_Record.setEnabled(False)

        if cfg_file and cfg_template:
            self.load_all_params(cfg_template, cfg_file)

        self.ui.groupBox_Launch.setEnabled(True)

    # ----------------------------------------------------------------------
    @pyqtSlot()
    def on_click_train(self):
        """
        Loads the Training parameters.
        """
        self.modality = 'trainer'

        cfg_file, cfg_template = self.prepare_config_files(self.modality)
        module_name = 'trainer_' + self.protocol

        self.load_protocol_module(module_name)

        self.ui.checkBox_Record.setChecked(False)
        self.ui.checkBox_Record.setEnabled(False)

        if cfg_file and cfg_template:
            self.load_all_params(cfg_template, cfg_file)

        self.ui.groupBox_Launch.setEnabled(True)

    #----------------------------------------------------------------------
    @pyqtSlot()
    def on_click_online(self):
        """
        Loads the Online parameters.
        """
        self.modality = 'online'

        cfg_file, cfg_template = self.prepare_config_files(self.modality)
        module_name = 'online_' + self.protocol

        self.load_protocol_module(module_name)

        self.ui.checkBox_Record.setChecked(True)
        self.ui.checkBox_Record.setEnabled(True)

        if cfg_file and cfg_template:
            self.load_all_params(cfg_template, cfg_file)

        self.ui.groupBox_Launch.setEnabled(True)

    #----------------------------------------------------------------------v
    @pyqtSlot()
    def on_click_start(self):
        """
        Launch the selected protocol. It can be Offline, Train or Online.
        """
        self.record_dir = Path(self.cfg_subject.DATA_PATH)

        ccfg = cfg_class(self.cfg_subject)  #  because a module is not pickable

        with self.record_state.get_lock():
            self.record_state.value = 0

        if not self.protocol_state.value:
            self.ui.textEdit_terminal.clear()

            # Recording shared variable + recording terminal
            if self.ui.checkBox_Record.isChecked():

                amp = self.ui.comboBox_LSL.currentData()
                if not amp:
                    self.signal_error[str].emit('No LSL amplifier specified.')
                    return

                if not self.record_terminal:
                    self.record_terminal = GuiTerminal(self.recordLogger,
                                                       'INFO', self.width())
                    self.hide_recordTerminal[bool].connect(
                        self.record_terminal.setHidden)

                else:
                    self.record_terminal.textEdit.clear()
                    self.record_terminal.textEdit.insertPlainText(
                        'Waiting for the recording to start...\n')
                    self.hide_recordTerminal[bool].emit(False)

                # Protocol shared variable
                with self.protocol_state.get_lock():
                    self.protocol_state.value = 2  #  0=stop, 1=start, 2=wait

                processesToLaunch = [('recording', recorder.run_gui, [self.record_state, self.protocol_state, self.record_dir, self.recordLogger, amp['name'], amp['serial'], False, self.record_terminal.my_receiver.queue]), \
                                     ('protocol', self.m.run, [ccfg, self.protocol_state, self.my_receiver.queue])]

            else:
                # Protocol shared variable
                with self.protocol_state.get_lock():
                    self.protocol_state.value = 1  #  0=stop, 1=start, 2=wait

                processesToLaunch = [
                    ('protocol', self.m.run,
                     [ccfg, self.protocol_state, self.my_receiver.queue])
                ]

            launchedProcess = Thread(target=self.launching_subprocesses,
                                     args=processesToLaunch)
            launchedProcess.start()
            logger.info(self.modality + ' protocol starting...')
            self.ui.pushButton_Start.setText('Stop')

        else:
            with self.protocol_state.get_lock():
                self.protocol_state.value = 0

            time.sleep(2)
            self.hide_recordTerminal[bool].emit(True)
            self.ui.pushButton_Start.setText('Start')

    #----------------------------------------------------------------------
    @pyqtSlot(str)
    def on_terminal_append(self, text):
        """
        Writes to the QtextEdit_terminal the redirected stdout.
        """
        self.ui.textEdit_terminal.moveCursor(QTextCursor.End)
        self.ui.textEdit_terminal.insertPlainText(text)

    @pyqtSlot()
    #----------------------------------------------------------------------
    def on_click_newSubject(self):
        """
        Instance a Connect_NewSubject QDialog class
        """
        buttonIcon = self.ui.pushButton_NewSubject.text()

        if buttonIcon == 'New':
            qdialog = Connect_NewSubject(self, self.ui.lineEdit_pathSearch)
            qdialog.signal_error[str].connect(self.on_error)
            self.ui.pushButton_NewSubject.setText('Accept')
            self.ui.pushButton_NewSubject.setStyleSheet("color: red;")
        else:
            self.ui.pushButton_NewSubject.setText('New')
            self.ui.pushButton_NewSubject.setStyleSheet("color: black;")
            self.on_enable_modality()

    #----------------------------------------------------------------------
    def on_error(self, errorMsg):
        """
        Display the error message into a QErrorMessage
        """
        self.error_dialog.showMessage(errorMsg)

    #----------------------------------------------------------------------
    def on_click_save_params_to_file(self):
        """
        Save the params to a config_file
        """
        filePath, fileName = os.path.split(self.cfg_subject.__file__)
        fileName = fileName.split('.')[0]  # Remove the .py

        file = self.cfg_subject.__file__.split(
            '.')[0] + '_' + datetime.now().strftime('%m.%d.%d.%M') + '.py'
        filePath = QFileDialog.getSaveFileName(self, 'Save config file', file,
                                               'python(*.py)')
        if filePath[0]:
            save_params_to_file(filePath[0], cfg_class(self.cfg_subject))

    @pyqtSlot(list)
    #----------------------------------------------------------------------
    def fill_comboBox_lsl(self, amp_list):
        """
        Fill the comboBox with the available lsl streams
        """
        # Clear the comboBox_lsl first
        self.ui.comboBox_LSL.clear()

        for amp in amp_list:
            amp_formated = '{} ({})'.format(amp[1], amp[2])
            self.ui.comboBox_LSL.addItem(amp_formated, {
                'name': amp[1],
                'serial': amp[2]
            })
        self.ui.pushButton_LSL.setText('Search')
        self.ui.pushButton_Viewer.setEnabled(True)

    #----------------------------------------------------------------------
    def on_click_lsl_button(self):
        """
        Find the available lsl streams and display them in the comboBox_LSL
        """
        if self.lsl_state.value == 1:

            with self.lsl_state.get_lock():
                self.lsl_state.value = 0

            self.lsl_thread.terminate()
            self.lsl_thread.wait()
            self.ui.pushButton_LSL.setText('Search')

        else:
            self.ui.textEdit_terminal.clear()

            with self.lsl_state.get_lock():
                self.lsl_state.value = 1

            self.lsl_thread = search_lsl_streams_thread(self.lsl_state, logger)
            self.lsl_thread.signal_lsl_found[list].connect(
                self.fill_comboBox_lsl)
            self.lsl_thread.start()

            self.ui.pushButton_LSL.setText('Stop')

    #----------------------------------------------------------------------
    def on_click_start_viewer(self):
        """
        Launch the viewer to check the signals in a seperate process 
        """
        # Start Viewer
        if not self.viewer_state.value:
            self.ui.textEdit_terminal.clear()

            with self.viewer_state.get_lock():
                self.viewer_state.value = 1

            amp = self.ui.comboBox_LSL.currentData()
            viewerprocess = mp.Process(
                target=instantiate_scope,
                args=[amp, self.viewer_state, logger, self.my_receiver.queue])
            viewerprocess.start()

            self.ui.pushButton_Viewer.setText('Stop')

        # Stop Viewer
        else:
            with self.viewer_state.get_lock():
                self.viewer_state.value = 0

            self.ui.pushButton_Viewer.setEnabled(True)
            self.ui.pushButton_Viewer.setText('Viewer')

    @pyqtSlot()
    #----------------------------------------------------------------------
    def on_enable_modality(self):
        """
        Enable the modalities groupBox if the provided path exists
        """
        subjectFolder = self.ui.lineEdit_pathSearch.text()

        if subjectFolder:
            exist = os.path.isdir(subjectFolder)

            if not exist:
                self.signal_error[str].emit(
                    'The provided subject folder does not exists.')
            else:
                self.ui.groupBox_Modality.setEnabled(True)

    #----------------------------------------------------------------------
    def connect_signals_to_slots(self):
        """Connects the signals to the slots"""

        # New subject button
        self.ui.pushButton_NewSubject.clicked.connect(self.on_click_newSubject)

        # Search Subject folder Search button
        self.ui.pushButton_Search.clicked.connect(self.on_click_pathSearch)

        # Enable modality when subject folder path is given
        self.ui.lineEdit_pathSearch.editingFinished.connect(
            self.on_enable_modality)

        # Offline button
        self.ui.pushButton_Offline.clicked.connect(self.on_click_offline)

        # Train button
        self.ui.pushButton_Train.clicked.connect(self.on_click_train)

        # Online button
        self.ui.pushButton_Online.clicked.connect(self.on_click_online)

        # Start button
        self.ui.pushButton_Start.clicked.connect(self.on_click_start)

        # Save conf file
        self.ui.actionSave_config_file.triggered.connect(
            self.on_click_save_params_to_file)

        # Error dialog
        self.signal_error[str].connect(self.on_error)

        # Start viewer button
        self.ui.pushButton_Viewer.clicked.connect(self.on_click_start_viewer)

        # LSL button
        self.ui.pushButton_LSL.clicked.connect(self.on_click_lsl_button)

    #----------------------------------------------------------------------
    def launching_subprocesses(*args):
        """
        Launch subprocesses
        
        processesToLaunch = list of tuple containing the functions to launch
        and their args
        """
        launchedProcesses = dict()

        for p in args[1:]:
            launchedProcesses[p[0]] = mp.Process(target=p[1], args=p[2])
            launchedProcesses[p[0]].start()

        # Wait that the protocol is finished to stop recording
        launchedProcesses['protocol'].join()

        try:
            launchedProcesses['recording']
            recordState = args[1][2][0]  #  Sharing variable
            with recordState.get_lock():
                recordState.value = 0
        except:
            pass
Пример #40
0
class _POSIXUserscriptRunner(_BaseUserscriptRunner):

    """Userscript runner to be used on POSIX. Uses _BlockingFIFOReader.

    The OS must have support for named pipes and select(). Commands are
    executed immediately when they arrive in the FIFO.

    Attributes:
        _reader: The _BlockingFIFOReader instance.
        _thread: The QThread where reader runs.
    """

    def __init__(self, win_id, parent=None):
        super().__init__(win_id, parent)
        self._reader = None
        self._thread = None

    def run(self, cmd, *args, env=None):
        rundir = standarddir.get(QStandardPaths.RuntimeLocation)
        # tempfile.mktemp is deprecated and discouraged, but we use it here to
        # create a FIFO since the only other alternative would be to create a
        # directory and place the FIFO there, which sucks. Since os.kfifo will
        # raise an exception anyways when the path doesn't exist, it shouldn't
        # be a big issue.
        self._filepath = tempfile.mktemp(prefix='userscript-', dir=rundir)
        os.mkfifo(self._filepath)  # pylint: disable=no-member

        self._reader = _BlockingFIFOReader(self._filepath)
        self._thread = QThread(self)
        self._reader.moveToThread(self._thread)
        self._reader.got_line.connect(self.got_cmd)
        self._thread.started.connect(self._reader.read)
        self._reader.finished.connect(self.on_reader_finished)
        self._thread.finished.connect(self.on_thread_finished)

        self._run_process(cmd, *args, env=env)
        self._thread.start()

    def on_proc_finished(self):
        """Interrupt the reader when the process finished."""
        log.procs.debug("proc finished")
        self._thread.requestInterruption()

    def on_proc_error(self, error):
        """Interrupt the reader when the process had an error."""
        super().on_proc_error(error)
        self._thread.requestInterruption()

    def on_reader_finished(self):
        """Quit the thread and clean up when the reader finished."""
        log.procs.debug("reader finished")
        self._thread.quit()
        self._reader.fifo.close()
        self._reader.deleteLater()
        super()._cleanup()
        self.finished.emit()

    def on_thread_finished(self):
        """Clean up the QThread object when the thread finished."""
        log.procs.debug("thread finished")
        self._thread.deleteLater()
Пример #41
0
class APTDataViewer(QObject):
    """
    A class to show ion maps and mass spectrum.
    """

    # finished = pyqtSignal()

    def __init__(self, PosData, point_size=2, max_ion=1000):
        super().__init__()
        self.apt_data_viewer = QWidget()
        self.layout = QGridLayout()
        self.apt_data_viewer.setLayout(self.layout)
        self.apt_data_viewer.sizeHint = lambda: pg.QtCore.QSize(1920, 1200)

        self.point_cloud_view = gl.GLViewWidget()
        self.point_cloud_view.sizeHint = lambda: pg.QtCore.QSize(1920, 700)
        self.point_cloud_view.setBackgroundColor('w')
        self.point_cloud_view.opts['distance'] = 2000
        self.point_cloud_view.opts['fov'] = 1

        vb = CustomViewBox(
        )  # A custom view box added to mass spec view, this way the visualization performance is greatly enhanced.
        self.mass_spec = pg.PlotWidget(viewBox=vb)
        self.mass_spec.setBackground('w')
        self.mass_spec.sizeHint = lambda: pg.QtCore.QSize(1920, 500)

        self.mass_spec.setDownsampling(
            auto=True, mode='mean'
        )  # because there are some werid issue using downsampling mode when there are 0s in mass spec
        # self.mass_spec.setLogMode(y=True)

        self.mass_spec.setLabel('bottom', text='Mass-to-Charge (Dalton)')
        self.mass_spec.setLabel('left', text='Counts')

        self.layout.addWidget(self.point_cloud_view, 0, 0)
        self.layout.addWidget(self.mass_spec, 1, 0)

        self.MassWorker = APTMassWorker(PosData.m2z, PosData.intensity)
        self.thread_mass = QThread()
        self.MassWorker.plot_ready.connect(self.mass_plot_ready)
        self.MassWorker.finished.connect(self.thread_mass.quit)
        self.MassWorker.moveToThread(self.thread_mass)
        self.thread_mass.started.connect(self.MassWorker.plot_mass_spec)
        self.thread_mass.start()

        self.PosWorker = APTPosWorker(PosData.pos[:, 0:3], PosData.identity,
                                      PosData.ions, point_size, max_ion)
        self.thread_pos = QThread()
        self.PosWorker.gl_ready.connect(self.point_cloud_ready)
        self.PosWorker.finished.connect(self.thread_pos.quit)
        self.PosWorker.moveToThread(self.thread_pos)
        self.thread_pos.started.connect(self.PosWorker.visualize_pos)
        self.thread_pos.start()

        self.apt_data_viewer.show()

    @pyqtSlot(object, name='mass_plot_ready')
    def mass_plot_ready(self, item):
        self.mass_spec.addItem(item)

    @pyqtSlot(object, name='point_cloud_ready')
    def point_cloud_ready(self, sp1):
        self.point_cloud_view.addItem(sp1)
Пример #42
0
class ProcessDialog(QDialog):
    def __init__(self, port, **kwargs):
        super().__init__()

        self.setWindowTitle('Tasmotizing...')
        self.setFixedWidth(400)

        self.exception = None

        esptool.sw.progress.connect(self.update_progress)

        self.nam = QNetworkAccessManager()
        self.nrBinFile = QNetworkRequest()
        self.bin_data = b''

        self.setLayout(VLayout(5, 5))
        self.actions_layout = QFormLayout()
        self.actions_layout.setSpacing(5)

        self.layout().addLayout(self.actions_layout)

        self._actions = []
        self._action_widgets = {}

        self.port = port

        self.auto_reset = kwargs.get('auto_reset', False)

        self.file_path = kwargs.get('file_path')
        if self.file_path and self.file_path.startswith('http'):
            self._actions.append('download')

        self.backup = kwargs.get('backup')
        if self.backup:
            self._actions.append('backup')
            self.backup_size = kwargs.get('backup_size')

        self.erase = kwargs.get('erase')
        if self.erase:
            self._actions.append('erase')

        if self.file_path:
            self._actions.append('write')

        self.create_ui()
        self.start_process()

    def create_ui(self):
        for action in self._actions:
            pb = QProgressBar()
            pb.setFixedHeight(35)
            self._action_widgets[action] = pb
            self.actions_layout.addRow(action.capitalize(), pb)

        self.btns = QDialogButtonBox(QDialogButtonBox.Abort)
        self.btns.rejected.connect(self.abort)
        self.layout().addWidget(self.btns)

        self.sb = QStatusBar()
        self.layout().addWidget(self.sb)

    def appendBinFile(self):
        self.bin_data += self.bin_reply.readAll()

    def saveBinFile(self):
        if self.bin_reply.error() == QNetworkReply.NoError:
            self.file_path = self.file_path.split('/')[-1]
            with open(self.file_path, 'wb') as f:
                f.write(self.bin_data)
            self.run_esp()
        else:
            raise NetworkError

    def updateBinProgress(self, recv, total):
        self._action_widgets['download'].setValue(recv // total * 100)

    def download_bin(self):
        self.nrBinFile.setUrl(QUrl(self.file_path))
        self.bin_reply = self.nam.get(self.nrBinFile)
        self.bin_reply.readyRead.connect(self.appendBinFile)
        self.bin_reply.downloadProgress.connect(self.updateBinProgress)
        self.bin_reply.finished.connect(self.saveBinFile)

    def show_connection_state(self, state):
        self.sb.showMessage(state, 0)

    def run_esp(self):
        params = {
            'file_path': self.file_path,
            'auto_reset': self.auto_reset,
            'erase': self.erase
        }

        if self.backup:
            backup_size = f'0x{2 ** self.backup_size}00000'
            params['backup_size'] = backup_size

        self.esp_thread = QThread()
        self.esp = ESPWorker(self.port, self._actions, **params)
        esptool.sw.connection_state.connect(self.show_connection_state)
        self.esp.waiting.connect(self.wait_for_user)
        self.esp.done.connect(self.accept)
        self.esp.error.connect(self.error)
        self.esp.moveToThread(self.esp_thread)
        self.esp_thread.started.connect(self.esp.run)
        self.esp_thread.start()

    def start_process(self):
        if 'download' in self._actions:
            self.download_bin()
            self._actions = self._actions[1:]
        else:
            self.run_esp()

    def update_progress(self, action, value):
        self._action_widgets[action].setValue(value)

    @pyqtSlot()
    def wait_for_user(self):
        dlg = QMessageBox.information(
            self, 'User action required',
            'Please power cycle the device, wait a moment and press OK',
            QMessageBox.Ok | QMessageBox.Cancel)
        if dlg == QMessageBox.Ok:
            self.esp.continue_ok()
        elif dlg == QMessageBox.Cancel:
            self.esp.abort()
            self.esp.continue_ok()
            self.abort()

    def stop_thread(self):
        self.esp_thread.wait(2000)
        self.esp_thread.exit()

    def accept(self):
        self.stop_thread()
        self.done(QDialog.Accepted)

    def abort(self):
        self.sb.showMessage('Aborting...', 0)
        QApplication.processEvents()
        self.esp.abort()
        self.stop_thread()
        self.reject()

    def error(self, e):
        self.exception = e
        self.abort()

    def closeEvent(self, e):
        self.stop_thread()
Пример #43
0
class ExampleApp(QtWidgets.QWidget, mouseEvent.Ui_Form):
    def __init__(self):
        # Это здесь нужно для доступа к переменным, методам
        # и т.д. в файле design.py
        super().__init__()
        self.stopEvent = threading.Event()
        self.check = True
        self.thread = QThread()
        self.thread.start()
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setupUi(self)  # Это нужно для инициализации нашего дизайна
        self.robot = xmlrpc.client.ServerProxy('http://%s:%d' %
                                               (IP_ROBOT, CONTROL_PORT))
        self.spawer = Spawer(self.on_recive, self.stopEvent)
        self.spawer.start()

    def on_recive(self, img):
        pixmap = QtGui.QPixmap(img)
        self.label.setPixmap(pixmap)

    def closeEvent(self, event):
        reply = QtWidgets.QMessageBox.question(
            self, 'Message', "Are you sure to quit?",
            QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
            QtWidgets.QMessageBox.No)
        if reply == QtWidgets.QMessageBox.Yes:
            _ = self.robot.stopStreaming()
            self.stopEvent.wait()
            event.accept()
        else:
            event.ignore()

    def keyPressEvent(self, e):
        if self.check:
            if e.type() == QEvent.KeyPress:
                if e.text() == 'w':
                    _ = self.robot.comRecv('cam/down')
                    #self.check = False
                    e.accept()
                elif e.text() == 'd':
                    _ = self.robot.comRecv('cam/RIGHT')
                    #self.check = False
                    e.accept()
                elif e.text() == 'a':
                    _ = self.robot.comRecv('cam/LEFT')
                    #self.check = False
                    e.accept()
                elif e.text() == 's':
                    _ = self.robot.comRecv('cam/up')
                    #self.check = False
                    e.accept()
                elif e.key() == Qt.Key_Up:
                    _ = self.robot.comRecv('drive/ahead/200')
                    self.check = False
                    e.accept()
                elif e.key() == Qt.Key_Down:
                    _ = self.robot.comRecv('drive/backward/200')
                    self.check = False
                    e.accept()
                elif e.key() == Qt.Key_Left:
                    _ = self.robot.comRecv('drive/left/200')
                    self.check = False
                    e.accept()
                elif e.key() == Qt.Key_Right:
                    _ = self.robot.comRecv('drive/right/200')
                    self.check = False
                    e.accept()
        if e.key() == Qt.Key_Escape:
            self.close()

    def keyReleaseEvent(self, e):
        if e.type() == QEvent.KeyRelease:
            if e.key() in [Qt.Key_Up, Qt.Key_Down, Qt.Key_Left, Qt.Key_Right
                           ] and not e.isAutoRepeat():
                _ = self.robot.comRecv('drive/ahead/0')
                self.check = True
                e.accept()
Пример #44
0
class Ui_Window(QtWidgets.QDialog, UI.UICoordTransform.Ui_Dialog):
    def __init__(self, parent):
        super(Ui_Window, self).__init__(parent=parent)
        self.setupUi(self)

        font = QtGui.QFont()
        font.setFamily("微软雅黑")
        font.setPointSize(9)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Ok
                                          | QDialogButtonBox.Cancel)
        self.buttonBox.button(QDialogButtonBox.Ok).setFont(font)
        self.buttonBox.button(QDialogButtonBox.Ok).setText("确定")
        self.buttonBox.button(QDialogButtonBox.Cancel).setFont(font)
        self.buttonBox.button(QDialogButtonBox.Cancel).setText("取消")

        vlayout = QtWidgets.QVBoxLayout(self)
        vlayout.addWidget(self.splitter)
        vlayout.setContentsMargins(0, 0, 10, 10)
        self.splitter.setGeometry(0, 0, self.width(), self.height())

        self.splitter.setOrientation(Qt.Horizontal)
        self.splitter.setProperty("Stretch", SplitterState.collapsed)
        self.splitter.setProperty("Dock", Dock.right)
        self.splitter.setProperty("WidgetToHide", self.txt_log)
        self.splitter.setProperty("ExpandParentForm", True)

        self.splitter.setSizes([600, self.splitter.width() - 590])
        self.resize(self.splitter.width(), self.splitter.height())

        self.paras = {}  # 存储参数信息
        self.selIndex = QModelIndex()
        self.table_init()

        log.setLogViewer(parent=self, logViewer=self.txt_log)
        self.txt_log.setReadOnly(True)

        self.btn_addressFile.clicked.connect(self.open_addressFile)
        self.btn_addRow.clicked.connect(self.btn_addRow_clicked)
        self.btn_removeRow.clicked.connect(self.btn_removeBtn_clicked)
        self.btn_saveMetaFile.clicked.connect(self.btn_saveMetaFile_clicked)
        self.buttonBox.clicked.connect(self.buttonBox_clicked)
        self.splitter.splitterMoved.connect(self.splitterMoved)
        self.splitter.handle(1).handleClicked.connect(self.handleClicked)

        self.rbtn_file.clicked.connect(self.rbtn_toggled)
        self.rbtn_filedb.clicked.connect(self.rbtn_toggled)
        self.rbtn_table.clicked.connect(self.rbtn_toggled)

        self.thread = QThread(self)
        self.coordTransformThread = coordTransformWorker()
        self.coordTransformThread.moveToThread(self.thread)
        self.coordTransformThread.transform.connect(
            self.coordTransformThread.coordTransform)
        self.coordTransformThread.transform_tbl.connect(
            self.coordTransformThread.tableTransform)
        self.coordTransformThread.finished.connect(self.threadStop)

    def showEvent(self, a0: QtGui.QShowEvent) -> None:
        log.setLogViewer(parent=self, logViewer=self.txt_log)
        self.rbtn_file.click()
        # self.table_layout()

    def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
        self.table_layout()

    def threadStop(self):
        self.thread.quit()

    def splitterMoved(self):
        self.table_layout()

    def handleClicked(self):
        self.table_layout()

    def table_layout(self):
        if self.rbtn_table.isChecked():
            self.tbl_address.setColumnWidth(0, self.tbl_address.width() * 0.2)
            self.tbl_address.setColumnWidth(1, self.tbl_address.width() * 0.1)
            self.tbl_address.setColumnWidth(2, self.tbl_address.width() * 0.1)
            self.tbl_address.setColumnWidth(3, self.tbl_address.width() * 0.2)
            self.tbl_address.setColumnWidth(4, self.tbl_address.width() * 0.2)
            self.tbl_address.setColumnWidth(5, self.tbl_address.width() * 0.2)
        else:
            self.tbl_address.setColumnWidth(0, self.tbl_address.width() * 0.2)
            self.tbl_address.setColumnWidth(1, self.tbl_address.width() * 0.15)
            self.tbl_address.setColumnWidth(2, self.tbl_address.width() * 0.15)
            self.tbl_address.setColumnWidth(3, self.tbl_address.width() * 0.15)
            self.tbl_address.setColumnWidth(4, self.tbl_address.width() * 0.2)
            self.tbl_address.setColumnWidth(5, self.tbl_address.width() * 0.15)

    @Slot()
    def btn_addRow_clicked(self):
        try:
            save_srs_list = list(srs_dict.values())

            if self.rbtn_file.isChecked():
                fileNames, types = QFileDialog.getOpenFileNames(
                    self, "选择需要转换的图形文件", os.getcwd(),
                    "图形文件(*.shp *.geojson *.dwg);;ESRI Shapefile(*.shp);;GeoJson(*.geojson);;CAD drawing(*.dwg)"
                )
                if len(fileNames) == 0:
                    return

                for fileName in fileNames:
                    fileType = get_suffix(fileName)
                    if fileType == DataType.shapefile:
                        wks = workspaceFactory().get_factory(
                            DataType.shapefile)
                    elif fileType == DataType.geojson:
                        wks = workspaceFactory().get_factory(DataType.geojson)
                    elif fileType == DataType.cad_dwg:
                        wks = workspaceFactory().get_factory(DataType.cad_dwg)
                    else:
                        log.error("不识别的图形文件格式!", dialog=True)
                        return None

                    datasource = wks.openFromFile(fileName)

                    if datasource is not None:
                        layer_name = wks.getLayerNames()[0]
                        in_layer = datasource.GetLayer()
                        row = self.add_layer_to_row(in_layer, fileName,
                                                    layer_name)
                        # self.add_delegate_to_row(row, fileName, [layer_name], save_srs_list)
                        levelData = {
                            'layer_names': [layer_name],
                            'srs_list': save_srs_list
                        }
                        self.model.setLevelData(fileName, levelData)

                        datasource.Release()
                        datasource = None
                        in_layer = None
                    else:
                        layer_name, suffix = os.path.splitext(
                            os.path.basename(fileName))
                        row = self.add_layer_to_row(None, fileName, layer_name)
                        levelData = {
                            'layer_names': [layer_name],
                            'srs_list': save_srs_list
                        }
                        self.model.setLevelData(fileName, levelData)
                        # self.add_delegate_to_row(row, fileName, [layer_name], save_srs_list)

            elif self.rbtn_filedb.isChecked():
                fileName = QtWidgets.QFileDialog.getExistingDirectory(
                    self, "选择需要转换的GDB数据库", os.getcwd(),
                    QFileDialog.ShowDirsOnly)
                wks = workspaceFactory().get_factory(DataType.fileGDB)
                datasource = wks.openFromFile(fileName)

                if datasource is not None:
                    lst_names = wks.getLayerNames()
                    selected_names = None
                    if len(lst_names) > 1:
                        selected_names = nameListDialog().openListDialog(
                            "请选择要转换的图层", lst_names)
                    elif len(lst_names) == 1:
                        selected_names = [lst_names[0]]

                    rows = []
                    if selected_names is not None:
                        for selected_name in selected_names:
                            layer = wks.openLayer(selected_name)
                            row = self.add_layer_to_row(
                                layer, fileName, selected_name)
                            rows.append(row)

                        for row in rows:
                            levelData = {
                                'layer_names': lst_names,
                                'srs_list': save_srs_list
                            }
                            self.model.setLevelData(fileName, levelData)
                            # self.add_delegate_to_row(row, fileName, lst_names, save_srs_list)

            elif self.rbtn_table.isChecked():
                fileNames, types = QFileDialog.getOpenFileNames(
                    self, "选择需要转换的表格文件", os.getcwd(),
                    "表格文件(*.csv *.xlsx *.dbf);;csv文件(*.csv);;excel文件(*.xlsx);;dbf文件(*.dbf)"
                )
                if len(fileNames) == 0:
                    return
                for fileName in fileNames:
                    fileType = get_suffix(fileName)
                    row = self.add_table_to_row(fileName)

                    if fileType == DataType.xlsx:
                        # selected_sheet = []
                        # wb = load_workbook(fileName, read_only=True)
                        # wb.close()
                        # lst_names = wb.sheetnames
                        # if len(lst_names) > 1:
                        #     selected_sheet = nameListDialog().openListDialog(
                        #         "请选择工作表(sheet)", lst_names, QAbstractItemView.SingleSelection)
                        # elif len(lst_names) == 1:
                        #     selected_sheet = lst_names[0]
                        header, bheader = read_table_header(
                            fileName, fileType, None)
                        levelData = {
                            'is_header': bheader,
                            # 'sheet': selected_sheet[0],
                            'field_list': header,
                            'srs_list': save_srs_list
                        }
                    elif fileType == DataType.csv:
                        header, encoding, bheader = read_table_header(
                            fileName, fileType)
                        levelData = {
                            'is_header': bheader,
                            'encoding': encoding,
                            'field_list': header,
                            'srs_list': save_srs_list
                        }
                    elif fileType == DataType.dbf:
                        header = read_table_header(fileName, fileType)
                        levelData = {
                            'is_header': True,
                            'field_list': header,
                            'srs_list': save_srs_list
                        }
                    else:
                        log.error("不识别的图形文件格式!", dialog=True)
                        return None

                    field_delegate = xyfieldDelegate(self, [
                        None, {
                            'type': 'xy'
                        }, {
                            'type': 'xy'
                        }, {
                            'type': 'srs'
                        }, {
                            'type': 'srs'
                        }, {
                            'type': 'f',
                            'text': '请选择需要保存的文件'
                        }
                    ])
                    self.tbl_address.setItemDelegateForRow(row, field_delegate)
                    self.model.setLevelData(fileName, levelData)
        except:
            log.error(traceback.format_exc())

    def add_table_to_row(self, fileName):
        row = self.model.rowCount(QModelIndex())
        self.model.addEmptyRow(row, 1, 0)
        in_path_index = self.tbl_address.model().index(row, self.in_path_no)
        self.tbl_address.model().setData(in_path_index, fileName)

        return row

    def add_layer_to_row(self, in_layer, fileName, layer_name):
        if in_layer is not None:
            in_srs = in_layer.GetSpatialRef()
            if in_srs is not None:
                in_srs = osr.SpatialReference(in_srs.ExportToWkt())
                srs_epsg = in_srs.GetAttrValue("AUTHORITY", 1)
                srs_desc = srs_dict[int(srs_epsg)]
            else:
                srs_desc = None
        else:
            srs_desc = None

        row = self.model.rowCount(QModelIndex())
        self.model.addEmptyRow(row, 1, 0)
        in_path_index = self.tbl_address.model().index(row, self.in_path_no)
        in_layername_index = self.tbl_address.model().index(
            row, self.in_layername_no)
        in_srs_index = self.tbl_address.model().index(row, self.in_srs_no)

        self.tbl_address.model().setData(in_path_index, fileName)
        self.tbl_address.model().setData(in_layername_index, layer_name)
        self.tbl_address.model().setData(in_srs_index, srs_desc)

        return row

    @Slot()
    def open_addressFile(self):
        fileName, fileType = QtWidgets.QFileDialog.getOpenFileName(
            self, "选择坐标转换参数文件", os.getcwd(), "All Files(*)")
        self.txt_addressFile.setText(fileName)

        if fileName == "":
            return

        try:
            with open(fileName, 'r', encoding='utf-8') as f:
                self.paras = json.load(f)

            self.add_address_rows_from_paras()
        except:
            if self.model.rowCount(QModelIndex()) > 0:
                self.model.removeRows(
                    self.model.rowCount(QModelIndex()) - 1, 1, QModelIndex())
            log.error("读取参数文件失败!", dialog=True)

    def add_address_rows_from_paras(self):
        imps = self.paras['exports']
        for imp in imps:
            row = self.model.rowCount(QModelIndex())
            self.model.addEmptyRow(row, 1, 0)

            if self.rbtn_table.isChecked():
                fileType = get_suffix(imp['in_path'])
                header = imp['field_list']

                if fileType == DataType.csv:
                    header, encoding, bheader = read_table_header(
                        imp['in_path'], fileType)

                    self.model.setLevelData(
                        imp['in_path'], {
                            'is_header': bheader,
                            'encoding': encoding,
                            'field_list': header,
                            'srs_list': imp['srs_list']
                        })
                elif fileType == DataType.xlsx:
                    header, bheader = read_table_header(
                        imp['in_path'], fileType, None)
                    self.model.setLevelData(
                        imp['in_path'], {
                            'is_header': bheader,
                            'field_list': header,
                            'srs_list': imp['srs_list']
                        })
                elif fileType == DataType.dbf:
                    self.model.setLevelData(
                        imp['in_path'], {
                            'is_header': True,
                            'field_list': header,
                            'srs_list': imp['srs_list']
                        })

                x_field_index = self.tbl_address.model().index(row, 1)
                y_field_index = self.tbl_address.model().index(row, 2)
                in_srs_index = self.tbl_address.model().index(row, 3)
                out_srs_index = self.tbl_address.model().index(row, 4)
                x_field_delegate = self.tbl_address.itemDelegate(x_field_index)
                y_field_delegate = self.tbl_address.itemDelegate(y_field_index)
                in_srs_delegate = self.tbl_address.itemDelegate(in_srs_index)
                out_srs_delegate = self.tbl_address.itemDelegate(out_srs_index)

                if isinstance(x_field_delegate, xyfieldDelegate):
                    x_field_delegate.set_field_list(header)
                if isinstance(y_field_delegate, xyfieldDelegate):
                    y_field_delegate.set_field_list(header)
                if isinstance(in_srs_delegate, srsDelegate):
                    in_srs_delegate.set_srs_list(imp['srs_list'])
                if isinstance(out_srs_delegate, srsDelegate):
                    out_srs_delegate.set_srs_list(imp['srs_list'])

                field_delegate = xyfieldDelegate(self, [
                    None, {
                        'type': 'xy'
                    }, {
                        'type': 'xy'
                    }, {
                        'type': 'srs'
                    }, {
                        'type': 'srs'
                    }, {
                        'type': 'f',
                        'text': '请选择需要保存的文件'
                    }
                ])
                self.tbl_address.setItemDelegateForRow(row, field_delegate)

                self.tbl_address.model().setData(
                    self.tbl_address.model().index(row, 0), imp['in_path'])
                if imp['x_field'] in header:
                    self.tbl_address.model().setData(
                        self.tbl_address.model().index(row, 1), imp['x_field'])
                if imp['y_field'] in header:
                    self.tbl_address.model().setData(
                        self.tbl_address.model().index(row, 2), imp['y_field'])
                self.tbl_address.model().setData(
                    self.tbl_address.model().index(row, 3), imp['in_srs'])
                self.tbl_address.model().setData(
                    self.tbl_address.model().index(row, 4), imp['out_srs'])
                self.tbl_address.model().setData(
                    self.tbl_address.model().index(row, 5), imp['out_path'])
            else:
                in_srs_index = self.tbl_address.model().index(
                    row, self.in_srs_no)
                out_srs_index = self.tbl_address.model().index(
                    row, self.out_srs_no)
                in_srs_delegate = self.tbl_address.itemDelegate(in_srs_index)
                out_srs_delegate = self.tbl_address.itemDelegate(out_srs_index)

                if isinstance(in_srs_delegate, srsDelegate):
                    in_srs_delegate.set_srs_list(imp['srs_list'])
                if isinstance(out_srs_delegate, srsDelegate):
                    out_srs_delegate.set_srs_list(imp['srs_list'])

                self.model.setLevelData(imp['in_path'], {
                    'layer_names': imp['layer_names'],
                    'srs_list': imp['srs_list']
                })

                self.tbl_address.model().setData(
                    self.tbl_address.model().index(row, 0), imp['in_path'])
                self.tbl_address.model().setData(
                    self.tbl_address.model().index(row, 1), imp['in_layer'])
                self.tbl_address.model().setData(
                    self.tbl_address.model().index(row, 2), imp['in_srs'])
                self.tbl_address.model().setData(
                    self.tbl_address.model().index(row, 3), imp['out_srs'])
                self.tbl_address.model().setData(
                    self.tbl_address.model().index(row, 4), imp['out_path'])
                self.tbl_address.model().setData(
                    self.tbl_address.model().index(row, 5), imp['out_layer'])

    @Slot(QAbstractButton)
    def buttonBox_clicked(self, button: QAbstractButton):
        if button == self.buttonBox.button(QDialogButtonBox.Ok):
            if not self.check_paras():
                return

            self.thread.start()
            self.run_process()
        elif button == self.buttonBox.button(QDialogButtonBox.Cancel):
            self.close()

    def check_paras(self):
        rows = range(0, self.tbl_address.model().rowCount(QModelIndex()))

        if self.rbtn_table.isChecked():
            for row in rows:
                in_path_index = self.tbl_address.model().index(
                    row, 0, QModelIndex())
                x_field_index = self.tbl_address.model().index(
                    row, 1, QModelIndex())
                y_field_index = self.tbl_address.model().index(
                    row, 2, QModelIndex())
                in_srs_index = self.tbl_address.model().index(
                    row, 3, QModelIndex())
                out_srs_index = self.tbl_address.model().index(
                    row, 4, QModelIndex())
                out_path_index = self.tbl_address.model().index(
                    row, 5, QModelIndex())

                in_path = str(self.tbl_address.model().data(
                    in_path_index, Qt.DisplayRole)).strip()
                x_field = str(self.tbl_address.model().data(
                    x_field_index, Qt.DisplayRole)).strip()
                y_field = str(self.tbl_address.model().data(
                    y_field_index, Qt.DisplayRole)).strip()
                in_srs = str(self.tbl_address.model().data(
                    in_srs_index, Qt.DisplayRole)).strip()
                out_srs = str(self.tbl_address.model().data(
                    out_srs_index, Qt.DisplayRole)).strip()
                out_path = str(self.tbl_address.model().data(
                    out_path_index, Qt.DisplayRole)).strip()

                if in_path == "":
                    log.error('第{}行缺失必要参数"输入路径",请补全!'.format(row), dialog=True)
                    return False

                in_format = get_suffix(in_path)
                if in_format is None:
                    log.error('第{}行的输入数据格式不支持!目前只支持csv, excel和dbf'.format(row),
                              dialog=True)
                    return False

                if x_field == "":
                    log.error('第{}行缺失必要参数"x坐标",请补全!'.format(row))
                    return False

                if y_field == "":
                    log.error('第{}行缺失必要参数"y坐标",请补全!'.format(row))
                    return False

                if in_srs == "":
                    log.error('第{}行缺失必要参数"输入坐标系",请补全!'.format(row))
                    return False

                if out_srs == "":
                    log.error('第{}行缺失必要参数"输出坐标系",请补全!'.format(row))
                    return False

                if out_path == "":
                    in_filename, in_suffix = os.path.splitext(
                        os.path.basename(in_path))
                    out_file = self.default_outfile(in_path, in_format,
                                                    in_filename, out_srs)
                    if not os.path.exists("res"):
                        os.makedirs("res")

                    out_path = os.path.join(os.path.abspath("res"), out_file)

                if is_already_opened_in_write_mode(out_path):
                    out_path = launderName(out_path)
                self.tbl_address.model().setData(out_path_index, out_path)
                log.warning('第{}行参数"输出路径"缺失数据源,自动补全为默认值{}'.format(
                    row, out_path))
        else:
            for row in rows:
                in_path_index = self.tbl_address.model().index(
                    row, 0, QModelIndex())
                in_layername_index = self.tbl_address.model().index(
                    row, 1, QModelIndex())
                in_srs_index = self.tbl_address.model().index(
                    row, 2, QModelIndex())
                out_srs_index = self.tbl_address.model().index(
                    row, 3, QModelIndex())
                out_path_index = self.tbl_address.model().index(
                    row, 4, QModelIndex())
                out_layername_index = self.tbl_address.model().index(
                    row, 5, QModelIndex())

                in_path = str(self.tbl_address.model().data(
                    in_path_index, Qt.DisplayRole)).strip()
                in_layername = str(self.tbl_address.model().data(
                    in_layername_index, Qt.DisplayRole)).strip()
                in_srs = str(self.tbl_address.model().data(
                    in_srs_index, Qt.DisplayRole)).strip()
                out_srs = str(self.tbl_address.model().data(
                    out_srs_index, Qt.DisplayRole)).strip()
                out_path = str(self.tbl_address.model().data(
                    out_path_index, Qt.DisplayRole)).strip()
                out_layername = str(self.tbl_address.model().data(
                    out_layername_index, Qt.DisplayRole)).strip()

                if in_path == "":
                    log.error('第{}行缺失必要参数"输入路径",请补全!'.format(row), dialog=True)
                    return False

                in_format = get_suffix(in_path)
                if in_format is None:
                    log.error(
                        '第{}行的输入数据格式不支持!目前只支持shapefile, fileGDB, geojson和cad dwg'
                        .format(row),
                        dialog=True)
                    return False
                else:
                    in_wks = workspaceFactory().get_factory(in_format)
                    if in_wks is None:
                        log.error("缺失图形文件引擎!")
                        return False

                    if in_wks.driver is None:
                        log.error("缺失图形文件引擎{}!".format(in_wks.driverName))
                        return False

                if in_layername == "":
                    if in_format == DataType.fileGDB:
                        log.error('第{}行参数缺失必要参数"输入图层"!')
                        return False
                    else:
                        in_layername, suffix = os.path.splitext(
                            os.path.basename(in_path))
                        self.tbl_address.model().setData(
                            in_layername_index, in_layername)
                        log.warning('第{}行参数缺失参数"输入图层",已自动补全为{}'.format(
                            row, in_layername))

                if in_srs == "":
                    log.error('第{}行缺失必要参数"输入坐标系",请补全!'.format(row),
                              dialog=True)
                    return False

                if out_srs == "":
                    log.error('第{}行缺失必要参数"输出坐标系",请补全!'.format(row),
                              dialog=True)
                    return False

                if out_layername == "":
                    out_layername = in_layername + "_" + str(out_srs)
                    self.tbl_address.model().setData(out_layername_index,
                                                     out_layername)
                    log.warning('第{}行参数缺失参数"输出图层",自动补全为默认值{}'.format(
                        row, out_layername))

                self.autofill_outpath(row, in_path, out_path, in_layername,
                                      out_srs, in_format, out_path_index)

        return True

    def default_outfile(self, in_path, in_format, in_layername, out_srs):
        out_file = ""
        if in_format == DataType.fileGDB:
            in_filename, in_suffix = os.path.splitext(
                os.path.basename(in_path))
            out_file = "{}_converted.gdb".format(in_filename)
        elif in_format == DataType.geojson:
            out_file = "{}_{}_{}.geojson".format(in_layername, out_srs,
                                                 encodeCurrentTime())
        elif in_format == DataType.shapefile:
            out_file = "{}_{}_{}.shp".format(in_layername, out_srs,
                                             encodeCurrentTime())
        elif in_format == DataType.cad_dwg:
            out_file = "{}_{}_{}.dwg".format(in_layername, out_srs,
                                             encodeCurrentTime())
        elif in_format == DataType.csv or in_format == DataType.xlsx or in_format == DataType.dbf:
            out_file = "{}_{}_{}.csv".format(in_layername, out_srs,
                                             encodeCurrentTime())
        return out_file

    def autofill_outpath(self, row, in_path, out_path, in_layername, out_srs,
                         in_format, out_path_index):
        if out_path == "":
            out_file = self.default_outfile(in_path, in_format, in_layername,
                                            out_srs)

            if not os.path.exists("res"):
                os.makedirs("res")

            out_path = os.path.join(os.path.abspath("res"), out_file)
            self.tbl_address.model().setData(out_path_index, out_path)
            log.warning('第{}行参数缺失参数"输出路径",自动补全为默认值{}'.format(row, out_path))
        else:
            out_format = get_suffix(out_path)
            if out_format is None:
                out_file = self.default_outfile(in_path, in_format,
                                                in_layername, out_srs)

                out_path = os.path.join(out_path, out_file)
                self.tbl_address.model().setData(out_path_index, out_path)
                log.warning('第{}行参数"输出路径"缺失数据源,自动补全为默认值{}'.format(
                    row, out_path))

    def run_process(self):
        rows = range(0, self.tbl_address.model().rowCount(QModelIndex()))

        if self.rbtn_table.isChecked():
            for row in rows:
                in_path_index = self.tbl_address.model().index(
                    row, 0, QModelIndex())
                x_field_index = self.tbl_address.model().index(
                    row, 1, QModelIndex())
                y_field_index = self.tbl_address.model().index(
                    row, 2, QModelIndex())
                in_srs_index = self.tbl_address.model().index(
                    row, 3, QModelIndex())
                out_srs_index = self.tbl_address.model().index(
                    row, 4, QModelIndex())
                out_path_index = self.tbl_address.model().index(
                    row, 5, QModelIndex())

                in_path = str(self.tbl_address.model().data(
                    in_path_index, Qt.DisplayRole)).strip()
                x_field = str(self.tbl_address.model().data(
                    x_field_index, Qt.DisplayRole)).strip()
                y_field = str(self.tbl_address.model().data(
                    y_field_index, Qt.DisplayRole)).strip()
                in_srs = str(self.tbl_address.model().data(
                    in_srs_index, Qt.DisplayRole)).strip()
                out_srs = str(self.tbl_address.model().data(
                    out_srs_index, Qt.DisplayRole)).strip()
                out_path = str(self.tbl_address.model().data(
                    out_path_index, Qt.DisplayRole)).strip()

                in_srs = list(srs_dict.keys())[list(
                    srs_dict.values()).index(in_srs)]
                out_srs = list(srs_dict.keys())[list(
                    srs_dict.values()).index(out_srs)]
                x = self.model.levels[in_path]['field_list'].index(x_field)
                y = self.model.levels[in_path]['field_list'].index(y_field)

                # inencode = check_encoding(in_path)
                fileType = get_suffix(in_path)
                bheader = self.model.levels[in_path]['is_header']

                if fileType == DataType.csv:
                    inencode = self.model.levels[in_path]['encoding']

                    # UICore.coordTransform_table.coordTransform(in_path, inencode, bheader, x, y, in_srs, out_srs, out_path, "gbk")
                    self.coordTransformThread.transform_tbl.emit(
                        in_path, inencode, bheader, x, y, in_srs, out_srs,
                        out_path, "gb18030")
                else:
                    self.coordTransformThread.transform_tbl.emit(
                        in_path, "gbk", bheader, x, y, in_srs, out_srs,
                        out_path, "gb18030")
        else:
            for row in rows:
                in_path_index = self.tbl_address.model().index(
                    row, 0, QModelIndex())
                in_layername_index = self.tbl_address.model().index(
                    row, 1, QModelIndex())
                in_srs_index = self.tbl_address.model().index(
                    row, 2, QModelIndex())
                out_srs_index = self.tbl_address.model().index(
                    row, 3, QModelIndex())
                out_path_index = self.tbl_address.model().index(
                    row, 4, QModelIndex())
                out_layername_index = self.tbl_address.model().index(
                    row, 5, QModelIndex())

                in_path = str(self.tbl_address.model().data(
                    in_path_index, Qt.DisplayRole)).strip()
                in_layername = str(self.tbl_address.model().data(
                    in_layername_index, Qt.DisplayRole)).strip()
                in_srs = str(self.tbl_address.model().data(
                    in_srs_index, Qt.DisplayRole)).strip()
                out_srs = str(self.tbl_address.model().data(
                    out_srs_index, Qt.DisplayRole)).strip()
                out_path = str(self.tbl_address.model().data(
                    out_path_index, Qt.DisplayRole)).strip()
                out_layername = str(self.tbl_address.model().data(
                    out_layername_index, Qt.DisplayRole)).strip()

                in_srs = list(srs_dict.keys())[list(
                    srs_dict.values()).index(in_srs)]
                out_srs = list(srs_dict.keys())[list(
                    srs_dict.values()).index(out_srs)]

                self.coordTransformThread.transform.emit(
                    in_path, in_layername, in_srs, out_path, out_layername,
                    out_srs)

    @Slot()
    def btn_saveMetaFile_clicked(self):
        fileName, fileType = QFileDialog.getSaveFileName(
            self, "请选择保存的参数文件", os.getcwd(), "json file(*.json)")

        if fileName == "":
            return

        datas = self.tbl_address.model().datas
        levels = self.tbl_address.model().levelData()
        logicRows = range(0, len(datas))

        results = []

        if self.rbtn_table.isChecked():
            for logicRow in logicRows:
                key = datas[logicRow][0]
                row_data = {
                    'in_path': datas[logicRow][0],
                    'x_field': datas[logicRow][1],
                    'y_field': datas[logicRow][2],
                    'in_srs': datas[logicRow][3],
                    'out_srs': datas[logicRow][4],
                    'out_path': datas[logicRow][5],
                    'field_list': levels[key]['field_list'],
                    'srs_list': levels[key]['srs_list']
                }
                results.append(row_data)
        else:
            for logicRow in logicRows:
                key = datas[logicRow][0]
                row_data = {
                    'in_path': datas[logicRow][0],
                    'in_layer': datas[logicRow][1],
                    'in_srs': datas[logicRow][2],
                    'out_srs': datas[logicRow][3],
                    'out_path': datas[logicRow][4],
                    'out_layer': datas[logicRow][5],
                    'layer_names': levels[key]['layer_names'],
                    'srs_list': levels[key]['srs_list']
                }
                results.append(row_data)

        res = {'exports': results}

        try:
            if fileName != '':
                with open(fileName, 'w', encoding='UTF-8') as f:
                    json.dump(res, f, ensure_ascii=False)
        except:
            log.error("文件存储路径错误,无法保存!", parent=self, dialog=True)

    @Slot()
    def rbtn_toggled(self):
        if self.rbtn_table.isChecked():
            self.table_init_table_data()
        else:
            self.table_init_spatial_data()

    def table_init(self):
        self.tbl_address.setStyle(mTableStyle())

        self.tbl_address.horizontalHeader().setStretchLastSection(True)
        self.tbl_address.verticalHeader().setDefaultSectionSize(20)
        self.tbl_address.verticalHeader().setSectionResizeMode(
            QHeaderView.Fixed)  # 行高固定

        color = self.palette().color(QPalette.Button)
        self.tbl_address.horizontalHeader().setStyleSheet(
            "QHeaderView::section {{ background-color: {}}}".format(
                color.name()))
        self.tbl_address.verticalHeader().setStyleSheet(
            "QHeaderView::section {{ background-color: {}}}".format(
                color.name()))
        self.tbl_address.setStyleSheet(
            "QTableCornerButton::section {{ color: {}; border: 1px solid; border-color: {}}}"
            .format(color.name(), color.name()))

        self.tbl_address.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.tbl_address.setEditTriggers(QAbstractItemView.SelectedClicked
                                         | QAbstractItemView.DoubleClicked)
        # self.tbl_address.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.tbl_address.DragDropMode(QAbstractItemView.InternalMove)
        self.tbl_address.setSelectionBehavior(QAbstractItemView.SelectRows
                                              | QAbstractItemView.SelectItems)
        self.tbl_address.setDefaultDropAction(Qt.MoveAction)

        self.tbl_address.horizontalHeader().setSectionsMovable(False)
        self.tbl_address.setDragEnabled(True)
        self.tbl_address.setAcceptDrops(True)

        self.in_path_no = 0  # 输入路径字段的序号
        self.in_layername_no = 1  # 输入图层的序号
        self.out_layername_no = 5  # 输出图层的序号
        self.in_srs_no = 2
        self.out_srs_no = 3  # 输出坐标系的序号

    def table_init_spatial_data(self):
        self.model = TableModel()

        self.model.setHeaderData(0, Qt.Horizontal, "输入路径", Qt.DisplayRole)
        self.model.setHeaderData(1, Qt.Horizontal, "输入图层", Qt.DisplayRole)
        self.model.setHeaderData(2, Qt.Horizontal, "输入坐标系", Qt.DisplayRole)
        self.model.setHeaderData(3, Qt.Horizontal, "输出坐标系", Qt.DisplayRole)
        self.model.setHeaderData(4, Qt.Horizontal, "输出路径", Qt.DisplayRole)
        self.model.setHeaderData(5, Qt.Horizontal, "输出图层", Qt.DisplayRole)
        self.tbl_address.setModel(self.model)
        self.table_layout()

        layername_delegate = layernameDelegate(self, {'type': 'c'})
        self.tbl_address.setItemDelegateForColumn(1, layername_delegate)
        srs_delegate = srsDelegate(self, srs_dict.values())
        self.tbl_address.setItemDelegateForColumn(2, srs_delegate)
        self.tbl_address.setItemDelegateForColumn(3, srs_delegate)
        outputpath_delegate = outputPathDelegate(self, {'type': 'd'})
        self.tbl_address.setItemDelegateForColumn(4, outputpath_delegate)

    def table_init_table_data(self):
        self.model = TableModel()

        self.model.setHeaderData(0, Qt.Horizontal, "输入文件", Qt.DisplayRole)
        self.model.setHeaderData(1, Qt.Horizontal, "x坐标", Qt.DisplayRole)
        self.model.setHeaderData(2, Qt.Horizontal, "y坐标", Qt.DisplayRole)
        self.model.setHeaderData(3, Qt.Horizontal, "输入坐标系", Qt.DisplayRole)
        self.model.setHeaderData(4, Qt.Horizontal, "输出坐标系", Qt.DisplayRole)
        self.model.setHeaderData(5, Qt.Horizontal, "输出文件", Qt.DisplayRole)
        self.tbl_address.setModel(self.model)
        self.table_layout()

        # layername_delegate = layernameDelegate(self, {'type': 'c'})
        # self.tbl_address.setItemDelegateForColumn(1, layername_delegate)
        # srs_delegate = srsDelegate(self, srs_dict.values())
        # self.tbl_address.setItemDelegateForColumn(2, srs_delegate)
        # self.tbl_address.setItemDelegateForColumn(3, srs_delegate)
        # outputpath_delegate = outputPathDelegate(self, {'type': 'd'})
        # self.tbl_address.setItemDelegateForColumn(4, outputpath_delegate)

    @Slot()
    def btn_removeBtn_clicked(self):
        index_list = []
        selModel = self.tbl_address.selectionModel()
        if selModel is None:
            return
        if len(selModel.selectedRows()) < 1:
            return
        for model_index in selModel.selectedRows():
            index = QPersistentModelIndex(model_index)
            index_list.append(index)

        oldrow = index.row()
        for index in index_list:
            self.model.removeRows(index.row(), 1, 0)

        if self.model.rowCount(QModelIndex()) == 1:
            next_index = self.model.index(0, 0)
        else:
            next_index = self.model.index(oldrow, 0)
        # next_index = self.model.index(self.model.rowCount(QModelIndex()) - 1, 0)
        selModel.select(
            next_index,
            QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
        self.tbl_address.setFocus()

    def return_url_and_layername(self, logicRow):
        in_path_index = self.tbl_address.model().index(logicRow,
                                                       self.in_path_no)
        layername_index = self.tbl_address.model().index(
            logicRow, self.in_layername_no)
        url = self.tbl_address.model().data(in_path_index, Qt.DisplayRole)
        layername = self.tbl_address.model().data(layername_index,
                                                  Qt.DisplayRole)
        return in_path_index, layername_index, url, layername

    def update_outlayername(self, index: QModelIndex, text):
        if index.column() == self.out_srs_no:
            in_layername_index = self.tbl_address.model().index(
                index.row(), self.in_layername_no)
            out_srs_index = self.tbl_address.model().index(
                index.row(), self.out_srs_no)
            out_layername_index = self.tbl_address.model().index(
                index.row(), self.out_layername_no)
            layer_name = self.tbl_address.model().data(in_layername_index,
                                                       Qt.DisplayRole)
            # self.tbl_address.model().setData(out_srs_index, text)
            self.tbl_address.model().setData(out_layername_index,
                                             "{}_{}".format(layer_name, text))
Пример #45
0
class ESPMode(MicroPythonMode):
    """
    Represents the functionality required for running MicroPython on ESP8266
    """

    name = _("ESP MicroPython")
    short_name = "esp"
    description = _("Write MicroPython on ESP8266/ESP32 boards.")
    icon = "esp"
    fs = None

    # The below list defines the supported devices, however, many
    # devices are using the exact same FTDI USB-interface, with vendor
    # ID 0x403 without reporting their own VID/PID

    # In some instances we can recognize the device not on VID/PID,
    # but on manufacturer ID, that's what the third column is for.
    # These more specific device specifications, should be listed
    # before the generic FTDI VID/PID's
    valid_boards = [
        # VID  , PID,    Manufacturer string, Device name
        (0x1A86, 0x7523, None, "HL-340"),
        (0x10C4, 0xEA60, None, "CP210x"),
        (0x0403, 0x6001, "M5STACK Inc.", "M5Stack ESP32 device"),
        (0x0403, 0x6001, None, None),  # FT232/FT245 (XinaBox CW01, CW02)
        (0x0403, 0x6010, None, None),  # FT2232C/D/L/HL/Q (ESP-WROVER-KIT)
        (0x0403, 0x6011, None, None),  # FT4232
        (0x0403, 0x6014, None, None),  # FT232H
        (0x0403, 0x6015, None, None),  # FT X-Series (Sparkfun ESP32)
        (0x0403, 0x601C, None, None),  # FT4222H
    ]

    def actions(self):
        """
        Return an ordered list of actions provided by this module. An action
        is a name (also used to identify the icon) , description, and handler.
        """
        buttons = [
            {
                "name": "run",
                "display_name": _("Run"),
                "description": _(
                    "Run your code directly on the ESP8266/ESP32"
                    " via the REPL."
                ),
                "handler": self.run,
                "shortcut": "F5",
            },
            {
                "name": "files",
                "display_name": _("Files"),
                "description": _("Access the file system on ESP8266/ESP32."),
                "handler": self.toggle_files,
                "shortcut": "F4",
            },
            {
                "name": "repl",
                "display_name": _("REPL"),
                "description": _(
                    "Use the REPL to live-code on the " "ESP8266/ESP32."
                ),
                "handler": self.toggle_repl,
                "shortcut": "Ctrl+Shift+I",
            },
        ]
        if CHARTS:
            buttons.append(
                {
                    "name": "plotter",
                    "display_name": _("Plotter"),
                    "description": _("Plot incoming REPL data."),
                    "handler": self.toggle_plotter,
                    "shortcut": "CTRL+Shift+P",
                }
            )
        return buttons

    def api(self):
        """
        Return a list of API specifications to be used by auto-suggest and call
        tips.
        """
        return SHARED_APIS + ESP_APIS

    def toggle_repl(self, event):
        if self.fs is None:
            if self.repl:
                # Remove REPL
                super().toggle_repl(event)
                self.set_buttons(files=True)
            elif not (self.repl):
                # Add REPL
                super().toggle_repl(event)
                if self.repl:
                    self.set_buttons(files=False)
        else:
            message = _("REPL and file system cannot work at the same time.")
            information = _(
                "The REPL and file system both use the same USB "
                "serial connection. Only one can be active "
                "at any time. Toggle the file system off and "
                "try again."
            )
            self.view.show_message(message, information)

    def toggle_plotter(self, event):
        """
        Check for the existence of the file pane before toggling plotter.
        """
        if self.fs is None:
            super().toggle_plotter(event)
            if self.plotter:
                self.set_buttons(files=False)
            elif not (self.repl or self.plotter):
                self.set_buttons(files=True)
        else:
            message = _(
                "The plotter and file system cannot work at the same " "time."
            )
            information = _(
                "The plotter and file system both use the same "
                "USB serial connection. Only one can be active "
                "at any time. Toggle the file system off and "
                "try again."
            )
            self.view.show_message(message, information)

    def run(self):
        """
        Takes the currently active tab, compiles the Python script therein into
        a hex file and flashes it all onto the connected device.
        """
        """
        if self.repl:
            message = _("Flashing cannot be performed at the same time as the "
                        "REPL is active.")
            information = _("File transfers use the same "
                            "USB serial connection as the REPL. Toggle the "
                            "REPL off and try again.")
            self.view.show_message(message, information)
            return
        """
        logger.info("Running script.")
        # Grab the Python script.
        tab = self.view.current_tab
        if tab is None:
            # There is no active text editor.
            message = _("Cannot run anything without any active editor tabs.")
            information = _(
                "Running transfers the content of the current tab"
                " onto the device. It seems like you don't have "
                " any tabs open."
            )
            self.view.show_message(message, information)
            return
        python_script = tab.text().split("\n")
        if not self.repl:
            self.toggle_repl(None)
        if self.repl and self.connection:
            self.connection.send_commands(python_script)

    def toggle_files(self, event):
        """
        Check for the existence of the REPL or plotter before toggling the file
        system navigator for the MicroPython device on or off.
        """
        if self.repl:
            message = _(
                "File system cannot work at the same time as the "
                "REPL or plotter."
            )
            information = _(
                "The file system and the REPL and plotter "
                "use the same USB serial connection. Toggle the "
                "REPL and plotter off and try again."
            )
            self.view.show_message(message, information)
        else:
            if self.fs is None:
                self.add_fs()
                if self.fs:
                    logger.info("Toggle filesystem on.")
                    self.set_buttons(run=False, repl=False, plotter=False)
            else:
                self.remove_fs()
                logger.info("Toggle filesystem off.")
                self.set_buttons(run=True, repl=True, plotter=True)

    def add_fs(self):
        """
        Add the file system navigator to the UI.
        """

        # Find serial port the ESP8266/ESP32 is connected to
        device = self.editor.current_device

        # Check for MicroPython device
        if not device:
            message = _("Could not find an attached ESP8266/ESP32.")
            information = _(
                "Please make sure the device is plugged "
                "into this computer.\n\nThe device must "
                "have MicroPython flashed onto it before "
                "the file system will work.\n\n"
                "Finally, press the device's reset button "
                "and wait a few seconds before trying "
                "again."
            )
            self.view.show_message(message, information)
            return
        self.file_manager_thread = QThread(self)
        self.file_manager = FileManager(device.port)
        self.file_manager.moveToThread(self.file_manager_thread)
        self.file_manager_thread.started.connect(self.file_manager.on_start)

        # Show directory of the current file in the left pane, if any,
        # otherwise show the default workspace_dir
        if self.view.current_tab and self.view.current_tab.path:
            path = os.path.dirname(os.path.abspath(self.view.current_tab.path))
        else:
            path = self.workspace_dir()
        self.fs = self.view.add_filesystem(
            path, self.file_manager, _("ESP board")
        )
        self.fs.set_message.connect(self.editor.show_status_message)
        self.fs.set_warning.connect(self.view.show_message)
        self.file_manager_thread.start()

    def remove_fs(self):
        """
        Remove the file system navigator from the UI.
        """
        self.view.remove_filesystem()
        self.file_manager = None
        self.file_manager_thread = None
        self.fs = None

    def on_data_flood(self):
        """
        Ensure the Files button is active before the REPL is killed off when
        a data flood of the plotter is detected.
        """
        self.set_buttons(files=True)
        super().on_data_flood()

    def deactivate(self):
        """
        Invoked whenever the mode is deactivated.
        """
        super().deactivate()
        if self.fs:
            self.remove_fs()

    def device_changed(self, new_device):
        """
        Invoked when the user changes device.
        """
        super().device_changed(new_device)
        if self.fs:
            self.remove_fs()
            self.add_fs()
Пример #46
0
 def start(self):
     """Ensure thread is stopped, and start it
     """
     self.stop()
     self._exit = False
     QThread.start(self)
Пример #47
0
class ApiJobQueue(QObject):
    """
    ApiJobQueue is the queue manager of two FIFO priority queues that process jobs of type ApiJob.


    The queue manager starts the queues when a new auth token is provided to ensure jobs are able to
    make their requests. It stops the queues whenever a MetadataSyncJob, which runs in a continuous
    loop outside of the queue manager, encounters an ApiInaccessibleError and forces a logout
    from the Controller.
    """

    # Signal that is emitted after a queue is paused.
    paused = pyqtSignal()

    def __init__(self, api_client: API, session_maker: scoped_session) -> None:
        super().__init__(None)

        self.main_thread = QThread()
        self.download_file_thread = QThread()

        self.main_queue = RunnableQueue(api_client, session_maker)
        self.download_file_queue = RunnableQueue(api_client, session_maker)

        self.main_queue.moveToThread(self.main_thread)
        self.download_file_queue.moveToThread(self.download_file_thread)

        self.main_thread.started.connect(self.main_queue.process)
        self.download_file_thread.started.connect(self.download_file_queue.process)

        self.main_queue.paused.connect(self.on_main_queue_paused)
        self.download_file_queue.paused.connect(self.on_file_download_queue_paused)

    def start(self, api_client: API) -> None:
        """
        Start the queues whenever a new api token is provided.
        """
        self.main_queue.api_client = api_client
        self.download_file_queue.api_client = api_client

        if not self.main_thread.isRunning():
            self.main_thread.start()
            logger.debug("Started main queue")

        if not self.download_file_thread.isRunning():
            self.download_file_thread.start()
            logger.debug("Started file download queue")

    def stop(self) -> None:
        """
        Stop the queues.
        """
        if self.main_thread.isRunning():
            self.main_thread.quit()
            logger.debug("Stopped main queue")

        if self.download_file_thread.isRunning():
            self.download_file_thread.quit()
            logger.debug("Stopped file download queue")

    @pyqtSlot()
    def on_main_queue_paused(self) -> None:
        """
        Emit the paused signal if the main queue has been paused.
        """
        logger.debug("Paused main queue")
        self.paused.emit()

    @pyqtSlot()
    def on_file_download_queue_paused(self) -> None:
        """
        Emit the paused signal if the file download queue has been paused.
        """
        logger.debug("Paused file download queue")
        self.paused.emit()

    def resume_queues(self) -> None:
        """
        Emit the resume signal to the queues if they are running.
        """
        if self.main_thread.isRunning():
            logger.debug("Resuming main queue")
            self.main_queue.resume.emit()
        if self.download_file_thread.isRunning():
            logger.debug("Resuming download queue")
            self.download_file_queue.resume.emit()

    @pyqtSlot(object)
    def enqueue(self, job: ApiJob) -> None:
        """
        Enqueue the supplied job if the queues are running.
        """
        if not self.main_thread.isRunning() or not self.download_file_thread.isRunning():
            logger.debug("Not adding job before queues have been started.")
            return

        if isinstance(job, FileDownloadJob):
            self.download_file_queue.add_job(job)
        else:
            self.main_queue.add_job(job)
Пример #48
0
class MainWindow(QWidget):
    def __init__(self, parent, masternode_list, imgDir):
        super(QWidget, self).__init__(parent)
        self.parent = parent
        self.imgDir = imgDir
        self.runInThread = ThreadFuns.runInThread
        ###-- Masternode list
        self.masternode_list = masternode_list
        ###-- Create clients and statuses
        self.hwdevice = None
        self.hwStatus = 0
        self.hwStatusMess = "Not Connected"
        self.rpcClient = None
        self.rpcConnected = False
        self.rpcStatusMess = "Not Connected"
        self.isBlockchainSynced = False

        ###-- Load icons & images
        self.loadIcons()
        ###-- Create main layout
        self.layout = QVBoxLayout()
        self.header = GuiHeader(self)
        self.initConsole()
        self.layout.addWidget(self.header)
        ###-- Create RPC Whatchdog
        self.rpc_watchdogThread = QThread()
        self.myRpcWd = RpcWatchdog(self)
        self.myRpcWd.moveToThread(self.rpc_watchdogThread)
        self.rpc_watchdogThread.started.connect(self.myRpcWd.run)

        ###-- Create Queues and redirect stdout and stderr
        self.queue = Queue()
        self.queue2 = Queue()
        sys.stdout = WriteStream(self.queue)
        sys.stderr = WriteStream(self.queue2)

        ###-- Init last logs
        logFile = open(log_File, 'w+')
        timestamp = strftime('%Y-%m-%d %H:%M:%S', gmtime(now()))
        log_line = '<b style="color: blue">{}</b><br>'.format(
            'STARTING SPMT at ' + timestamp)
        logFile.write(log_line)
        logFile.close()

        ###-- Create the thread to update console log for stdout
        self.consoleLogThread = QThread()
        self.myWSReceiver = WriteStreamReceiver(self.queue)
        self.myWSReceiver.mysignal.connect(self.append_to_console)
        self.myWSReceiver.moveToThread(self.consoleLogThread)
        self.consoleLogThread.started.connect(self.myWSReceiver.run)
        self.consoleLogThread.start()
        printDbg("Console Log thread started")
        ###-- Create the thread to update console log for stderr
        self.consoleLogThread2 = QThread()
        self.myWSReceiver2 = WriteStreamReceiver(self.queue2)
        self.myWSReceiver2.mysignal.connect(self.append_to_console)
        self.myWSReceiver2.moveToThread(self.consoleLogThread2)
        self.consoleLogThread2.started.connect(self.myWSReceiver2.run)
        self.consoleLogThread2.start()
        printDbg("Console Log thread 2 started")

        ###-- Initialize tabs
        self.tabs = QTabWidget()
        self.t_main = TabMain(self)
        self.t_mnconf = TabMNConf(self)
        self.t_rewards = TabRewards(self)
        self.t_governance = TabGovernance(self)

        ###-- Add tabs
        self.tabs.addTab(self.tabMain, "Masternode Control")
        #self.tabs.addTab(self.tabMNConf, "MN Configuration")
        self.tabs.addTab(self.tabRewards, "Transfer Rewards")
        self.tabs.addTab(self.tabGovernance, "Governance")
        ###-- Connect change action
        self.tabs.currentChanged.connect(lambda: self.onTabChange())
        ###-- Draw Tabs
        self.splitter = QSplitter(Qt.Vertical)
        ###-- Add tabs and console to Layout
        self.splitter.addWidget(self.tabs)
        self.splitter.addWidget(self.console)
        self.splitter.setStretchFactor(0, 0)
        self.splitter.setStretchFactor(1, 1)
        self.splitter.setSizes(self.parent.cache.get("splitter_sizes"))
        self.layout.addWidget(self.splitter)

        ###-- Set Layout
        self.setLayout(self.layout)
        ###-- Let's go
        self.mnode_to_change = None
        printOK("Hello! Welcome to " + parent.title)

        ###-- Hide console if it was previously hidden
        if self.parent.cache.get("console_hidden"):
            self.onToggleConsole()

        ##-- Check version
        self.onCheckVersion()

        ##-- init Api Client
        self.apiClient = ApiClient()

    @pyqtSlot(str)
    def append_to_console(self, text):
        self.consoleArea.moveCursor(QTextCursor.End)
        self.consoleArea.insertHtml(text)

    def initConsole(self):
        self.console = QGroupBox()
        self.console.setTitle("Console Log")
        layout = QVBoxLayout()
        self.btn_consoleToggle = QPushButton('Hide')
        self.btn_consoleToggle.setToolTip('Show/Hide console')
        self.btn_consoleToggle.clicked.connect(lambda: self.onToggleConsole())
        consoleHeader = QHBoxLayout()
        consoleHeader.addWidget(self.btn_consoleToggle)
        self.consoleSaveButton = QPushButton('Save')
        self.consoleSaveButton.clicked.connect(lambda: self.onSaveConsole())
        consoleHeader.addWidget(self.consoleSaveButton)
        self.btn_consoleClean = QPushButton('Clean')
        self.btn_consoleClean.setToolTip('Clean console log area')
        self.btn_consoleClean.clicked.connect(lambda: self.onCleanConsole())
        consoleHeader.addWidget(self.btn_consoleClean)
        consoleHeader.addStretch(1)
        self.versionLabel = QLabel("--")
        self.versionLabel.setOpenExternalLinks(True)
        consoleHeader.addWidget(self.versionLabel)
        self.btn_checkVersion = QPushButton("Check SPMT version")
        self.btn_checkVersion.setToolTip("Check latest stable release of SPMT")
        self.btn_checkVersion.clicked.connect(lambda: self.onCheckVersion())
        consoleHeader.addWidget(self.btn_checkVersion)
        layout.addLayout(consoleHeader)
        self.consoleArea = QTextEdit()
        almostBlack = QColor(40, 40, 40)
        palette = QPalette()
        palette.setColor(QPalette.Base, almostBlack)
        green = QColor(0, 255, 0)
        palette.setColor(QPalette.Text, green)
        self.consoleArea.setPalette(palette)
        layout.addWidget(self.consoleArea)
        self.console.setLayout(layout)

    def isMasternodeInList(self, mn_alias):
        return (mn_alias in [x['name'] for x in self.masternode_list])

    def loadIcons(self):
        # Load Icons
        self.ledPurpleH_icon = QPixmap(
            os.path.join(self.imgDir, 'icon_purpleLedH.png')).scaledToHeight(
                17, Qt.SmoothTransformation)
        self.ledGrayH_icon = QPixmap(
            os.path.join(self.imgDir, 'icon_grayLedH.png')).scaledToHeight(
                17, Qt.SmoothTransformation)
        self.ledHalfPurpleH_icon = QPixmap(
            os.path.join(self.imgDir,
                         'icon_halfPurpleLedH.png')).scaledToHeight(
                             17, Qt.SmoothTransformation)
        self.ledRedV_icon = QPixmap(
            os.path.join(self.imgDir, 'icon_redLedV.png')).scaledToHeight(
                17, Qt.SmoothTransformation)
        self.ledGrayV_icon = QPixmap(
            os.path.join(self.imgDir, 'icon_grayLedV.png')).scaledToHeight(
                17, Qt.SmoothTransformation)
        self.ledGreenV_icon = QPixmap(
            os.path.join(self.imgDir, 'icon_greenLedV.png')).scaledToHeight(
                17, Qt.SmoothTransformation)

    def loadMNConf(self, fileName):
        hot_masternodes = loadMNConfFile(fileName)
        if hot_masternodes == None:
            messText = "Unable to load data from file '%s'" % fileName
            self.myPopUp2(QMessageBox.Warning, "SPMT - warning", messText)
        else:
            # Append new masternodes to list
            new_masternodes = []
            skip_masternodes = []
            for x in hot_masternodes:
                if not self.isMasternodeInList(x['name']):
                    self.masternode_list.append(x)
                    new_masternodes.append(x)
                else:
                    skip_masternodes.append(x)

            # Show new list
            for new_masternode in new_masternodes:
                name = new_masternode['name']
                self.tabMain.insert_mn_list(name,
                                            new_masternode['ip'],
                                            new_masternode['port'],
                                            None,
                                            isHardware=False)
                self.tabMain.btn_remove[name].clicked.connect(
                    lambda: self.t_main.onRemoveMN())

            # print number of nodes added
            new_nodes = len(new_masternodes)
            final_message = ""
            if new_nodes == 0:
                final_message = "No External Masternode "
            elif new_nodes == 1:
                final_message = "1 External Masternode "
            else:
                final_message = "%d External Masternodes " % new_nodes
            final_message += "added to the list. "
            if new_nodes > 0:
                final_message += str([x['name']
                                      for x in new_masternodes]) + ".  "
            if len(skip_masternodes) > 0:
                final_message += "Following entries skipped due to duplicate names:"
                final_message += str([x['name']
                                      for x in skip_masternodes]) + ".  "
            printDbg(final_message)

            if new_nodes > 0:
                # update files
                printDbg("saving MN configuration file")
                writeToFile(self.masternode_list, masternodes_File)
                printDbg("saved")
                # Clear voting masternodes configuration and update cache
                self.t_governance.clear()

    def myPopUp(self,
                messType,
                messTitle,
                messText,
                defaultButton=QMessageBox.No):
        mess = QMessageBox(messType,
                           messTitle,
                           messText,
                           defaultButton,
                           parent=self)
        mess.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        mess.setDefaultButton(defaultButton)
        return mess.exec_()

    def myPopUp2(self,
                 messType,
                 messTitle,
                 messText,
                 singleButton=QMessageBox.Ok):
        mess = QMessageBox(messType,
                           messTitle,
                           messText,
                           singleButton,
                           parent=self)
        mess.setStandardButtons(singleButton | singleButton)
        return mess.exec_()

    @pyqtSlot()
    def onCheckHw(self):
        printDbg("Checking for HW device...")
        self.updateHWstatus(None)
        self.showHWstatus()

    @pyqtSlot()
    def onCheckRpc(self):
        printDbg("Checking RPC server...")
        self.runInThread(self.updateRPCstatus, (), self.showRPCstatus)

    @pyqtSlot()
    def onCheckVersion(self):
        printDbg("Checking SPMT version...")
        self.versionLabel.setText("--")
        self.runInThread(self.checkVersion, (), self.updateVersion)

    def checkVersion(self, ctrl):
        local_version = self.parent.version['number'].split('.')
        remote_version = getRemoteSPMTversion().split('.')

        if (remote_version[0] > local_version[0]) or \
        (remote_version[0] == local_version[0] and remote_version[1] > local_version[1]) or \
        (remote_version[0] == local_version[0] and remote_version[1] == local_version[1] and remote_version[2] > local_version[2]):
            self.versionMess = '<b style="color:red">New Version Available:</b> %s.%s.%s  ' % (
                remote_version[0], remote_version[1], remote_version[2])
            self.versionMess += '(<a href="https://github.com/PIVX-Project/PIVX-SPMT/releases/">download</a>)'
        else:
            self.versionMess = "You have the latest version of SPMT"

    def updateVersion(self):
        if self.versionMess is not None:
            self.versionLabel.setText(self.versionMess)

    @pyqtSlot()
    def onCleanConsole(self):
        self.consoleArea.clear()

    @pyqtSlot()
    def onSaveConsole(self):
        timestamp = strftime('%Y-%m-%d_%H-%M-%S', gmtime(now()))
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        fileName, _ = QFileDialog.getSaveFileName(
            self,
            "Save Logs to file",
            "SPMT_Logs_%s.txt" % timestamp,
            "All Files (*);; Text Files (*.txt)",
            options=options)
        try:
            if fileName:
                printOK("Saving logs to %s" % fileName)
                log_file = open(fileName, 'w+')
                log_text = self.consoleArea.toPlainText()
                log_file.write(log_text)
                log_file.close()

        except Exception as e:
            err_msg = "error writing Log file"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)

    @pyqtSlot()
    def onTabChange(self):
        # reload (and re-sort)masternode list in tabs
        if self.tabs.currentWidget() == self.tabRewards:
            # reload last used address
            self.tabRewards.destinationLine.setText(
                self.parent.cache.get("lastAddress"))
            # get new order
            mnOrder = {}
            mnList = self.tabMain.myList
            for i in range(mnList.count()):
                mnName = mnList.itemWidget(mnList.item(i)).alias
                mnOrder[mnName] = i
            self.parent.cache['mnList_order'] = mnOrder
            # Sort masternode list (by alias if no previous order set)
            if self.parent.cache.get('mnList_order') != {}:
                self.masternode_list.sort(key=self.parent.extract_order)
            self.t_rewards.loadMnSelect()
            self.t_rewards.selectedRewards = None

        # reload proposal and voting masternode list
        if self.tabs.currentWidget() == self.tabGovernance:
            self.t_governance.onRefreshProposals()
            self.t_governance.updateSelectedMNlabel()

    @pyqtSlot()
    def onToggleConsole(self):
        if self.btn_consoleToggle.text() == 'Hide':
            self.btn_consoleToggle.setText('Show')
            self.consoleArea.hide()
            self.console.setMinimumHeight(70)
            self.console.setMaximumHeight(70)
        else:
            self.console.setMinimumHeight(70)
            self.console.setMaximumHeight(starting_height)
            self.btn_consoleToggle.setText('Hide')
            self.consoleArea.show()

    def showHWstatus(self):
        self.updateHWleds()
        self.myPopUp2(QMessageBox.Information, 'SPMT - hw check',
                      "%s" % self.hwStatusMess, QMessageBox.Ok)

    def showRPCstatus(self):
        self.updateRPCled()
        self.myPopUp2(QMessageBox.Information, 'SPMT - rpc check',
                      "%s" % self.rpcStatusMess, QMessageBox.Ok)

    def updateHWleds(self):
        if self.hwStatus == 1:
            self.header.hwLed.setPixmap(self.ledHalfPurpleH_icon)
        elif self.hwStatus == 2:
            self.header.hwLed.setPixmap(self.ledPurpleH_icon)
        else:
            self.header.hwLed.setPixmap(self.ledGrayH_icon)
        self.header.hwLed.setToolTip(self.hwStatusMess)

    def updateHWstatus(self, ctrl):
        if self.hwdevice is not None:
            if hasattr(self.hwdevice, 'dongle'):
                self.hwdevice.dongle.close()

        self.hwdevice = HWdevice()

        statusCode, statusMess = self.hwdevice.getStatus()
        printDbg("mess: %s" % statusMess)
        if statusCode != 2:
            # If is not connected try again
            try:
                if hasattr(self.hwdevice, 'dongle'):
                    self.hwdevice.dongle.close()
                self.hwdevice = HWdevice()
                self.hwdevice.initDevice()
                statusCode, statusMess = self.hwdevice.getStatus()

            except Exception as e:
                err_msg = "error in checkHw"
                printException(getCallerName(), getFunctionName(), err_msg,
                               e.args)

        self.hwStatus = statusCode
        self.hwStatusMess = statusMess

        # if all is good connect the signals
        if statusCode == 2:
            self.hwdevice.sigTxdone.connect(self.t_rewards.FinishSend)
            self.hwdevice.sigTxabort.connect(self.t_rewards.onCancel)
            self.hwdevice.tx_progress.connect(
                self.t_rewards.updateProgressPercent)

    def updateLastBlockLabel(self):
        text = '--'
        if self.rpcLastBlock == 1:
            text = "Loading block index..."
        elif self.rpcLastBlock > 0 and self.rpcConnected:
            text = str(self.rpcLastBlock)
            text += " ("
            if not self.isBlockchainSynced:
                text += "Synchronizing"
            else:
                text += "Synced"
            text += ")"

        self.header.lastBlockLabel.setText(text)

    def updateRPCled(self):
        if self.rpcConnected:
            self.header.rpcLed.setPixmap(self.ledPurpleH_icon)
        else:
            if self.rpcLastBlock == 1:
                self.header.rpcLed.setPixmap(self.ledHalfPurpleH_icon)
            else:
                self.header.rpcLed.setPixmap(self.ledGrayH_icon)

        self.header.rpcLed.setToolTip(self.rpcStatusMess)
        self.updateLastBlockLabel()

    def updateRPCstatus(self, ctrl):
        if self.rpcClient is None:
            self.rpcClient = RpcClient()

        status, statusMess, lastBlock = self.rpcClient.getStatus()

        self.rpcConnected = status
        self.rpcLastBlock = lastBlock
        self.rpcStatusMess = statusMess
        self.isBlockchainSynced = self.rpcClient.isBlockchainSynced()

        # If is not connected try again
        if not status:
            self.rpcClient = RpcClient()
Пример #49
0
class Ui_MainWindow(object):

    def __init__(self, parent = None):
        self.parent = parent
        self.pressEnterTwiceLineEdit = False
        self.uiDialog = Ui_Dialog(self) # từ dialog
        self.word_buttons = []
        # tham chiếu parent để truyền qua lại dữ liệu
        #self.uiDialog.setupUi(self.window) # kế thừa từ QDialog
            # na ná như bên lớp applycation trong learnenglish.py
        self.translated_word = ''
        self.scraping = Scraping(0)    
        self.st2_waiting = False
        self.st2_status = True
        
    def newKeyPressEvent(self, event):
        if event.key() == Qt.Key_Q:
            self.google_trans(self.lineEdit.text(), 'e2v')
            self.getImageForWord(self.lineEdit.text())            

        if event.key() == Qt.Key_A:
            self.google_trans(self.lineEdit.text(), 'v2e')
            self.getImageForWord(self.lineEdit.text())

        if event.key() == Qt.Key_W and self.toolButton_trans2.isEnabled():
            self.google_trans(self.parent.GetLang('ENG'), 'e2v')
            self.getImageForWord(self.parent.GetLang('ENG'))
        
        if event.key() == Qt.Key_S:
            if self.pressEnterTwiceLineEdit or self.parent.listen:
                self.parent.TextToSpeech(self.parent.GetLang('ENG'))

        if event.key() == Qt.Key_D and self.parent.s2t and not self.st2_waiting:
            self.speak()


    def OpenVideo(self):
        self.uiVideo = VideoShow(self)
        self.uiVideo.show()

    def OpenCongrat(self):
        self.congratwindow = Congrat_Window(self)
        self.congratwindow.show()

    def OpenWindow(self, lang):
        def open():
            if self.parent.listen:
                self.parent.TextToSpeech(self.parent.GetLang('ENG'))
            else:
                if lang == 'VIE':
                    self.uiDialog.lineEdit.setText(self.parent.GetLang('VIE'))
                    self.uiDialog.lang_selected = 'VIE'
                else:
                    self.uiDialog.lineEdit.setText(self.parent.GetLang('ENG'))
                    self.uiDialog.lang_selected = 'ENG'

                    # gán text của lable vào text của textEdit để chỉnh sửa !!!
                # Giữ label hiện tại để thay đổi trong dialog
                self.uiDialog.show()
                # sau khi khởi tạo đối tượng QMainWindow() là một dialog, ta sẽ show nó 
        return open
            # dùng hàm open lồng trong OpenWindow để mục đích cuối cùng là trả về địa chỉ hàm
                # hợp lệ cho event
            # Nếu không dùng hàm lồng nhau trả về địa chỉ hàm nó sẽ ngầm hiểu label là kiểu bool và ko có thuộc tính text !!!
    

    def addWordButtonToLayoutEng(self, text):
        text = re.sub(r"[^\w\-']", ' ', text).strip()
        text = re.sub(r'\s+', ' ', text)
        layout = self.hz_vocabulary_eng
        # del widgets from hz_vocabulary_eng
        self.parent.deleteWidgetsInLayout(layout)
        self.word_buttons = []

        # add widgets to hz_vocabulary_eng
        list_word = text.split(' ')
        for i, word in enumerate(list_word):
            word_button = QtWidgets.QPushButton(self.centralwidget)
            word_button.setObjectName('word_button%s'%i)
            # fix callable objects in loop
            slot = functools.partial(self.translate, word)
            word_button.clicked.connect(slot)
            self.word_buttons.append(word_button)
            word_button.setText(word)
            font = QtGui.QFont()
            font.setPointSize(12)
            font.setFamily("Time News Roman")
            word_button.setFont(font)
            layout.addWidget(word_button)

    def google_trans(self, sentence, mode):
        self.translated_word = ''
        if sentence.replace(' ', '') == '':
            return 
        trsl, spelling, word_type_and_content, hint = self.scraping.google_translate(sentence, mode)
        self.text_browser.clear()
        self.text_browser.show()
        self.text_browser.append(self.parent.setStyleTextHTML(trsl, color= '#005500', size = '14'))
        if hint:
            self.text_browser.append(self.parent.setStyleTextHTML(hint, color= '#ff0000', size = '10'))
        if spelling:
            self.text_browser.append(self.parent.setStyleTextHTML(spelling, color= '#ffaa00', size = '10'))
        if word_type_and_content[0] and word_type_and_content[1]:
            viet_word = True
            for line in word_type_and_content[1]:
                if line in word_type_and_content[0]:
                    self.text_browser.append(self.parent.setStyleTextHTML(line, color= '#005500', size = '12', weight= '500'))
                elif viet_word:
                    self.text_browser.append(self.parent.setStyleTextHTML(line, color= '#5500ff', size = '10', style= 'italic'))
                    viet_word = False
                else:
                    self.text_browser.append(self.parent.setStyleTextHTML(line, color= '#000000', size = '10'))
                    viet_word = True
        self.text_browser.moveCursor(QtGui.QTextCursor.Start)

    def speak(self):
        self.lineEdit.setText('')
        self.lineEdit.setPlaceholderText('Speech to text >>> Listening ... <<<')
        self.scraping.speech_to_text(self)
        
    def translate(self, word):
        # show text_browser and hz_image_eng
        if self.translated_word == word:
            self.getImageForWord(word)
            return

        self.translated_word = word
        self.text_browser.show()
        self.parent.EnableWidgetsInLayout(self.hz_image_eng, True)
        self.getImageForWord(word)
        self.text_browser.clear()
        word = re.sub(r"[^-'\w]", ' ', word).lower()
        word = word.replace('_', '-')
        if word == '':
            return
        page = requests.get("https://dict.laban.vn/find?type=1&query=%s"%(word))
        soup = BeautifulSoup(page.content, 'html.parser')
        spelling = soup.find('h2', {'class':'fl'}).text
        content = soup.find('div',{'id':'content_selectable', 'class':'content'})
        if content == None:
            return
        word_type = content.find_all('div', {'class':'bg-grey bold font-large m-top20'})
        word_type = [wt.find('span').text for wt in word_type]
        content = [w.replace('\xa0', ' ') for w in content.text.split('\n')][1:len(content)-1]
        self.text_browser.append(self.parent.setStyleTextHTML(word, color= '#005500', size = '17'))
        self.text_browser.append(self.parent.setStyleTextHTML(spelling))
        for i, wt in enumerate(word_type):
            self.text_browser.append(self.parent.addTagScroll(wt, 'href', '#', i, 8, 'underline', '0000ff'))
        text = ''
        count = 0
        for line in content:
            if line in word_type:
                self.text_browser.append(self.parent.addTagScroll('\n' + line, 'name', '', count, 14, 'none', 'ffaa00'))
                count+=1
            else:
                temp = word[:int(len(word)/2)]
                temp = word if len(temp)<=2 else temp
                if line.lower().find(temp)!=-1:
                    text = self.parent.setStyleTextHTML(line, color= '#aa55ff', style='italic', weight = '600')
                else:
                    text = self.parent.setStyleTextHTML(line)
                self.text_browser.append(text)

        self.text_browser.moveCursor(QtGui.QTextCursor.Start)

    def getImageForWord(self, word):
        try:
            self.parent.deleteWidgetsInLayout(self.hz_image_eng)
            page = requests.get("https://vn.images.search.yahoo.com/search/images?fr=sfp&p=" + word + "&fr2=p%3As%2Cv%3Ai&.bcrumb=uKIyJ5aGvwE&save=0")
            soup = BeautifulSoup(page.content, 'html.parser')
            images = soup.find_all('img')
            images = [img.attrs['src'] for img in images if 'src' in img.attrs]
            if len(images)<4:
                max = len(images)
            else:
                max = 4

            for _ in range(max):
                i = random.randint(0,len(images)-1)
                data = urllib.request.urlopen(images[i]).read()
                image = QtGui.QImage()
                image.loadFromData(data)
                lbl = QtWidgets.QLabel(self.centralwidget)
                lbl.setPixmap(QtGui.QPixmap(image).scaled(200,200))
                #lbl.setFixedSize(QtCore.QSize(200,200))
                self.hz_image_eng.addWidget(lbl)
                del images[i]
        except:
            pass


    def show(self, result):
        self.toolButton_3.setEnabled(True)
        self.toolButton_4.setEnabled(True)
        self.toolButton_trans2.setEnabled(True)

        self.pressEnterTwiceLineEdit = True


        # so khớp 2 kết quả
        # sửa sai lần hai cho các từ giống như như 's ~ is, 're ~ are ...
        if self.parent.checkSentenceEncore:
            s1 = self.parent.ConvertAcronyms(result)
            s2 = self.parent.ConvertAcronyms(self.lineEdit.text())
            s1 = self.parent.FillCharectInSentence(s1)
            s2 = self.parent.FillCharectInSentence(s2)
        else:
            s1 = self.parent.FillCharectInSentence(result)
            s2 = self.parent.FillCharectInSentence(self.lineEdit.text())
        if s1 == s2:
            self.parent.BellRing(1)
            if self.numTrueSentence >= 0:
                self.progressBar.setProperty('value', self.progressBar.value() + 5)
            self.parent.DictDB.update_score(self.parent.DictDB.get_score(result) + 1, result)
            self.numTrueSentence+=1
            self.progressBar.setFormat('%s/20'%(str(self.numTrueSentence)))

            self.addWordButtonToLayoutEng(result)
            if self.numTrueSentence == 20:
                self.OpenCongrat()
                self.parent.BellRing(3)
        else:
            if not self.parent.checkSentenceEncore:
                self.parent.checkSentenceEncore = True
                self.show(result)
                return
            self.parent.BellRing(2)
            if not self.parent.s2t:
                self.progressBar.setProperty('value', self.progressBar.value() - 5)
                score = self.parent.DictDB.get_score(result)
                score = score - 1 if score >-5 else score
                self.parent.DictDB.update_score(score, result)
                self.numTrueSentence-=1
            self.progressBar.setFormat('%s/20'%(str(self.numTrueSentence)))
            s1 = self.lineEdit.text().split(' ')
            s2 = self.parent.FillCharectInSentence(result).split(' ')
            pos = [-1,-1]
            if len(s1) <= len(s2):
                pos = self.parent.FindPosErrorWords(s1, s2)
            else:
                pos = self.parent.FindPosErrorWords(s2, s1)
            self.addWordButtonToLayoutEng(result)
            self.parent.PaintColorWordButtons(self.word_buttons, pos[0], pos[1])
            self.parent.checkSentenceEncore = False

    def showResult(self):
        # Không show result nếu chưa nhập gì cả hoặc chỉ toàn là khoảng trắng
        string = self.lineEdit.text().replace(' ', '')

        if not self.pressEnterTwiceLineEdit: # lần enter thứ nhất
            if string == '': # chuỗi rỗng
                QtWidgets.QMessageBox.information(None, 'WARNING', 'Please type corectly english translation !!!')
            else:
                if self.parent.listen:
                    self.label.setText(self.parent.GetLang('VIE'))
                    self.parent.SetButtonIcon(self.toolButton, 'edit')
                    self.parent.listen = False
                    self.toolButton_3.show()
                result = self.parent.GetLang('ENG')
                # Thread(target= self.show(result)).start()
                # Thread(target= self.parent.TextToSpeech(result)).start()
                self.show(result)
                self.parent.TextToSpeech(result)
        else: # lần enter thứ 2
                if self.parent.s2t:
                    self.lineEdit.setPlaceholderText('Type english translation')
                    self.parent.s2t = False
                self.pressEnterTwiceLineEdit = False
                self.lineEdit.setText('')
                self.parent.EnableWidgetsInLayout(self.hz_image_eng, False)
                self.text_browser.hide()
                self.parent.LoadEngSentence()
                if self.st2_status:
                    self.parallel_s2t_threading()
                # self.label_2.setStyleSheet('color: black;')    

    def parallel_s2t_threading(self):
        # self.worker = Worker()
        self.thread = QThread(self.parent)
        self.thread.started.connect(self.speech2text_shot) # <--new line, make sure work starts.
        self.thread.start()

    def speech2text_shot(self):
        if np.random.randint(3) == 0 and self.parent.firstStart:
            self.lineEdit.setReadOnly(True)
            self.parent.s2t = True
            self.speak()
        else:
            self.lineEdit.setReadOnly(False)

    def DeleteCouple(self, MainWindow):
        def delete():
            reply = QtWidgets.QMessageBox.question(MainWindow, 'Message', "Do you want to delete this couple?", QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
            if reply == QtWidgets.QMessageBox.Yes:
                self.parent.DictDB.deleteById(self.parent.rowid)     
                self.parent.LoadEngSentence()                 
        return delete

    def setStatusS2T(self):
        icon = QtGui.QIcon()
        if self.st2_status:
            icon.addPixmap(QtGui.QPixmap("assets/speech_to_text_off.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
            self.toolButton_6.setIcon(icon)
            self.st2_status = False
            self.lineEdit.setReadOnly(False)
        else:
            icon.addPixmap(QtGui.QPixmap("assets/speech_to_text.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
            self.toolButton_6.setIcon(icon)
            self.st2_status = True
            if self.parent.s2t:
                self.lineEdit.setReadOnly(True)

    def setupUi(self, MainWindow):
        MainWindow.keyPressEvent = self.newKeyPressEvent      
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(603, 318)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")

        # tạo layout chứa label và button
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName('horizontalLayout')

        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName('horizontalLayout_2')

        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_3.setObjectName('horizontalLayout_3')

        self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_4.setObjectName('horizontalLayout_3')

        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setObjectName('verticalLayout_2')

        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setWordWrap(True)
        self.horizontalLayout.addWidget(self.label)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.verticalLayout.addLayout(self.horizontalLayout_2)
        self.verticalLayout.addLayout(self.horizontalLayout_3)
        self.verticalLayout.addLayout(self.horizontalLayout_4)

        self.text_browser = QtWidgets.QTextBrowser(self.centralwidget)
        self.text_browser.hide()
        self.verticalLayout.addWidget(self.text_browser)
        # self.text_browser.setText('ok baby\nyou are the one for me\n')
        # self.text_browser.setHtml("""My image :<br /><p style="text-align:center;"><img src="assets/enter.png" height="100" width="100"/>
        #                             <img src="assets/speak.png" height="100" width="100"/>
        #                             """)
        # self.text_browser.setFixedHeight(250)


        fontLabel1 = QtGui.QFont()
        fontLabel1.setPointSize(14)
        fontLabel1.setBold(True)

        fontLabel2 = QtGui.QFont()
        fontLabel2.setPointSize(14)
        fontLabel2.setBold(True)

        fontLineEdit = QtGui.QFont()
        fontLineEdit.setFamily("Times New Roman")
        fontLineEdit.setPointSize(16)
        fontLineEdit.setBold(True)
        fontLineEdit.setItalic(False)
        fontLineEdit.setWeight(75)

        self.label.setFont(fontLabel1)
        self.label.setObjectName("label")
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("assets/edit.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)

        self.toolButton = QtWidgets.QToolButton(self.centralwidget)
        self.toolButton.setIcon(icon)
        self.toolButton.setIconSize(QtCore.QSize(20,20))
        self.toolButton.setObjectName("toolButton")
        self.horizontalLayout.addWidget(self.toolButton)

        # Truyền tham số vào hàm sự kiện !!!
        # Vì sự kiện cần một địa chỉ hàm ko phải là hàm thực thi
        # nên sẽ dùng hàm lồng nhau 
        self.toolButton.clicked.connect(self.OpenWindow('VIE'))

        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        # self.lineEdit.keyPressEvent = self.newKeyPressEvent
        # self.lineEdit.keyReleaseEvent = self.newKeyReleaseEvent

        self.horizontalLayout_2.addWidget(self.lineEdit)

        self.lineEdit.setFont(fontLineEdit)
        self.lineEdit.setAutoFillBackground(False)
        self.lineEdit.setText('')
        self.lineEdit.setDragEnabled(False)
        self.lineEdit.setClearButtonEnabled(False)
        self.lineEdit.setStyleSheet("color: green;")

        self.lineEdit.setObjectName("lineEdit")
        # ẩn chữ trong lineEdit
        self.lineEdit.setPlaceholderText('Type english translation')

        # Event lineEdit
        self.lineEdit.returnPressed.connect(self.showResult)

        # Hiện con trỏ văn bản khi mở app 
        self.lineEdit.setFocus(True)
        icon2 = QtGui.QIcon()
        icon2.addPixmap(QtGui.QPixmap("assets/enter.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)

        self.toolButton_2 = QtWidgets.QToolButton(self.centralwidget)
        self.toolButton_2.setIcon(icon2)
        self.toolButton_2.setIconSize(QtCore.QSize(20,20))
        self.toolButton_2.setObjectName("toolButton_2")
        icon_trans = QtGui.QIcon()
        icon_trans.addPixmap(QtGui.QPixmap("assets/google_translate_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)

        self.toolButton_trans = QtWidgets.QToolButton(self.centralwidget)
        self.toolButton_trans.setIcon(icon_trans)
        self.toolButton_trans.setIconSize(QtCore.QSize(20,20))
        self.toolButton_trans.setObjectName('toolButton_trans')
        self.toolButton_trans.clicked.connect(lambda: self.google_trans(self.lineEdit.text(), 'e2v'))
        
        self.verLayA = QtWidgets.QVBoxLayout()
        self.verLayA.setObjectName('verLayA')
        self.verLayA.addWidget(self.toolButton_2)
        self.verLayA.addWidget(self.toolButton_trans)

        self.horizontalLayout_2.addLayout(self.verLayA)

        #Event toolButton_2
        self.toolButton_2.clicked.connect(self.showResult)


        # ProgressBar
        self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
        self.progressBar.setProperty("value", 0)
        self.progressBar.setObjectName("progressBar")

        # set text progressbar
        self.numTrueSentence = 0
        self.progressBar.setTextVisible(True)
        self.progressBar.setFormat('0/20')
        

        icon3 = QtGui.QIcon()
        icon3.addPixmap(QtGui.QPixmap("assets/speak.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        
        self.toolButton_3 = QtWidgets.QToolButton(self.centralwidget)
        self.toolButton_3.setIcon(icon3)
        self.toolButton_3.setIconSize(QtCore.QSize(20,20))
        self.toolButton_3.setObjectName("toolButton_3")

        self.verticalLayout_2.addWidget(self.toolButton_3)

        icon4 = QtGui.QIcon()
        icon4.addPixmap(QtGui.QPixmap("assets/edit.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.toolButton_4 = QtWidgets.QToolButton(self.centralwidget)
        self.toolButton_4.setIcon(icon4)
        self.toolButton_4.setIconSize(QtCore.QSize(20,20))
        self.toolButton_4.setObjectName('toolButton_4')
        self.toolButton_4.clicked.connect(self.OpenWindow('ENG'))


        self.toolButton_trans2 = QtWidgets.QToolButton(self.centralwidget)
        self.toolButton_trans2.setIcon(icon_trans)
        self.toolButton_trans2.setIconSize(QtCore.QSize(20,20))
        self.toolButton_trans2.setObjectName('toolButton_trans2')
        self.toolButton_trans2.clicked.connect(lambda: self.google_trans(self.parent.GetLang('ENG'), 'e2v'))

        self.verticalLayout_2.addWidget(self.toolButton_4)
        self.verticalLayout_2.addWidget(self.toolButton_trans2)
        self.verticalLayout_2.setAlignment(Qt.AlignRight)

        self.hz_vocabulary_eng = QtWidgets.QHBoxLayout()
        self.hz_vocabulary_eng.setObjectName('horizontalLayout_eng')
        self.hz_vocabulary_eng.setAlignment(Qt.AlignLeft)

        self.horizontalLayout_3.addLayout(self.hz_vocabulary_eng)
        self.horizontalLayout_3.addLayout(self.verticalLayout_2)

        icon5 = QtGui.QIcon()
        icon5.addPixmap(QtGui.QPixmap("assets/delete.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.toolButton_5 = QtWidgets.QToolButton(self.centralwidget)
        self.toolButton_5.setIcon(icon5)
        self.toolButton_5.setIconSize(QtCore.QSize(16,16))
        self.toolButton_5.setObjectName('toolButton_5')
        self.toolButton_5.clicked.connect(self.DeleteCouple(MainWindow))

        icon6 = QtGui.QIcon()
        icon6.addPixmap(QtGui.QPixmap("assets/speech_to_text.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.toolButton_6 = QtWidgets.QToolButton(self.centralwidget)
        self.toolButton_6.setIcon(icon6)
        self.toolButton_6.setIconSize(QtCore.QSize(16,16))
        self.toolButton_6.setObjectName('toolButton_6')
        self.toolButton_6.clicked.connect(self.setStatusS2T)
        self.horizontalLayout_4.addWidget(self.toolButton_5)
        self.horizontalLayout_4.addWidget(self.toolButton_6)
        self.horizontalLayout_4.setAlignment(Qt.AlignLeft)

        self.hz_image_eng = QtWidgets.QHBoxLayout()
        self.hz_image_eng.setObjectName('horizontalLayoutImageEng')
        self.verticalLayout.addLayout(self.hz_image_eng)
        self.verticalLayout.addWidget(self.progressBar)

        self.toolButton_3.clicked.connect(lambda: self.parent.TextToSpeech(self.parent.GetLang('ENG')))

        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 603, 21))
        self.menubar.setObjectName("menubar")
        self.menuFile = QtWidgets.QMenu(self.menubar)
        self.menuFile.setObjectName("menuFile")
        MainWindow.setMenuBar(self.menubar)
        self.actionReload = QtWidgets.QAction(MainWindow)
        self.actionReload.setObjectName("actionReload")
        self.actionSave_sentence_source = QtWidgets.QAction(MainWindow)
        self.actionSave_sentence_source.setObjectName("actionSave_sentence_source")
        self.menuFile.addAction(self.actionReload)

        # event actionReload
        self.actionReload.triggered.connect(self.parent.Reload)

        self.menuFile.addAction(self.actionSave_sentence_source)
        self.menubar.addAction(self.menuFile.menuAction())

        # Menu video
        self.actionVideo = QtWidgets.QAction(MainWindow)
        self.actionVideo.setObjectName('actionVideo')
        self.menubar.addAction(self.actionVideo)
        self.actionVideo.triggered.connect(self.OpenVideo)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "STUDY ENGLISH"))
        self.label.setText(_translate("MainWindow", "Vietnamese Sentence"))
        self.toolButton.setText(_translate("MainWindow", "Edit Sentence"))
        self.toolButton_2.setText(_translate("MainWindow", "Check"))
        self.toolButton_3.setText(_translate("MainWindow", "Edit result"))
        self.menuFile.setTitle(_translate("MainWindow", "Option"))
        self.actionReload.setText(_translate("MainWindow", "Reload"))
        self.actionSave_sentence_source.setText(_translate("MainWindow", "Save sentence source"))
        self.actionVideo.setText(_translate("MainWindow", "Learn with Video"))
Пример #50
0
class UserWindow(QMainWindow):
    def __init__(self, login, password, addr, port, parent=None):
        super().__init__(parent)
        self.login = login
        self.password = password
        self.user = client.User(login=login,
                                password=password,
                                addr=addr,
                                port=port)
        self.user.connect()

        self.w = uic.loadUi('GUI\client.ui', self)
        self.w.setWindowTitle(self.login)
        # создаём рессивер для перехвата
        self.listener = GuiReciever(self.user.socket, self.user.request_queue)
        # создаём поток
        self.listener.gotData.connect(self.update_chat)
        self.th = QThread()
        # ????
        self.listener.moveToThread(self.th)
        # связываем поток с рессивером при приеме данных
        self.th.started.connect(self.listener.poll)
        # запускаем поток
        self.th.start()
        self.initUT()

    def set_contacts(self):
        self.w.listWidgetContacts.clear()
        for contact in self.contacts:
            self.w.listWidgetContacts.addItem(str(contact))

    @pyqtSlot(str)
    def update_chat(self, data):
        ''' Отображение сообщения в истории
        '''
        try:
            msg = data
            self.w.listWidgetMsg.addItem(msg)
        except Exception as e:
            print(e)

    def refresh_contacts(self):
        self.contacts = self.user.get_contacts()[1]
        self.set_contacts()

    def add_contact(self):
        contact = self.w.textEditAddContact.toPlainText()
        answer = self.user.add_contact(contact)
        if answer['response'] == 201:
            self.refresh_contacts()
        self.print_info(answer)

    def print_info(self, msg):
        alert = msg['alert']
        time_point = msg['time']
        t = time_point[11:]
        text = f'[{t} сервер] {alert}'
        self.w.info.setText(text)

    def open_chat(self):
        try:
            self.wc = Chat(socket=self.user.socket,
                           login=self.login,
                           user=self.user)
        except Exception as e:
            print(e)

    def del_contact(self):
        contact = self.w.textEditAddContact.toPlainText()
        answer = self.user.del_contact(contact)
        if answer['response'] == 203:
            self.refresh_contacts()
        self.print_info(answer)

    def set_item(self):
        name = self.w.listWidgetContacts.currentItem().text()
        self.w.textEditAddContact.setText(name)

    def initUT(self):
        self.refresh_contacts()  # получаем контакты
        self.w.pushButtonAddContact.clicked.connect(self.add_contact)
        self.w.pushButtonOpenChat.clicked.connect(self.open_chat)
        self.w.pushButtonDelContact.clicked.connect(self.del_contact)
        self.w.listWidgetContacts.itemClicked.connect(self.set_item)
        self.show()
Пример #51
0
class MainWindow(QWidget):
    """
    Main window for uploading Template and Variables files.
    """
    def __init__(self):
        super(MainWindow, self).__init__()

        self.automation_thread = None
        self.threads = []

        self.setWindowTitle('AutoReport')
        self.setWindowIcon(
            QIcon(os.path.join(sys.path[1], 'assets\\small_logo.ico')))
        self.setMaximumSize(600, 400)

        self.TemplateDrop = FileDrop(self, '.docx')
        self.TemplateDrop.clicked.connect(partial(self.upload, '.docx'))

        self.VarsDrop = FileDrop(self, '.xls')
        self.VarsDrop.clicked.connect(partial(self.upload, '.xls'))

        vbox = QVBoxLayout()
        hbox = QHBoxLayout()

        hbox.addWidget(self.TemplateDrop, 0, Qt.AlignHCenter)
        hbox.addWidget(self.VarsDrop, 0, Qt.AlignHCenter)
        vbox.addLayout(hbox)

        self.submitBtn = QPushButton('Submit')
        self.submitBtn.clicked.connect(self.submit)

        vbox.addWidget(self.submitBtn)

        self.setLayout(vbox)
        self.setAcceptDrops(True)

    def upload(self, which_file):
        """
        Handles the upload of a file from a file-upload dialog. This function is ran after a user clicks 'Open'
        after selecting a template or vars file.

        Parameters
        ----------
        which_file : str
            Which file (Template or Variables) is being uploaded.
        """
        if which_file == '.xls':
            file_path = QFileDialog().getOpenFileName(filter='*.xls')[0]
            icon_name = os.path.join(sys.path[1], 'assets\\excel_icon.png')
            listbox = self.VarsDrop
        else:
            file_path = QFileDialog().getOpenFileName(filter='*.docx')[0]
            icon_name = os.path.join(sys.path[1], 'assets\\word_icon.png')
            listbox = self.TemplateDrop

        if not file_path:
            listbox.setDefaultContents()
        else:
            file_name = file_path.split('/')[-1]
            listbox.cur_file = file_path
            listbox.clear()
            item = QListWidgetItem(QIcon(icon_name), file_name)
            item.setFont(QFont(None, 12))
            item.setSizeHint(QSize(256, 256))
            item.setTextAlignment(Qt.AlignCenter)
            listbox.addItem(item)

    def submit(self):
        """
        Submit the inputted template and vars file to the automation thread.
        This code is ran upon press of the submit button.
        """
        if not self.TemplateDrop.cur_file or not self.VarsDrop.cur_file:
            return

        self.dialog = ProgressDialog()
        self.automation_thread = QThread()
        self.threads.append(self.automation_thread)
        automation = Automate()
        automation.moveToThread(self.automation_thread)
        automation.guiUpdater.moveToThread(self.automation_thread)
        automation.guiUpdater.logging_signal.connect(self.log)

        # When the thread starts, the run method of the Automation object will be ran with the template and vars files
        self.automation_thread.started.connect(
            partial(automation.run, self.TemplateDrop.cur_file,
                    self.VarsDrop.cur_file))

        self.automation_thread.start()
        self.dialog.exec()

    @pyqtSlot(int, str, int)
    def log(self, level, message, progress=None):
        """
        Add a log message to the progress dialog box.

        Parameters
        ----------
        level : int
            The level (i.e. logging level, info, warning, or critical) of the message.
        message : str
            The contents of the message.
        progress : int, optional
            The progress level from 0 to 100 of the message.
        """
        if progress is not None:
            self.dialog.progressBar.setValue(progress)

        if level in {logging.CRITICAL, logging.ERROR}:
            self.dialog.setWindowTitle('Error!')

        log = QListWidgetItem(message)
        color = {
            logging.WARNING: QColor(255, 204, 0),  # Yellow
            logging.ERROR: QColor(166, 68, 82),  # Red
            logging.CRITICAL: QColor(166, 68, 82)  # Red
        }.get(
            level
        )  # If level is not Warning, Error, or Critical, there will be a default (white) background.

        if color:
            log.setBackground(color)

        self.dialog.logsList.addItem(log)
        self.dialog.logsList.scrollToBottom()
Пример #52
0
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent, Qt.Window)
        self.setObjectName('main_window')
        self.params = PARAMS
        self.load_config()
        self.menubar = self.menuBar()
        self.root = QTabWidget(self)
        self.configurator = LoggerConfigurator(self.root)
        self.console = LoggerConsole(self.root)
        self.worker = None
        self.worker_thread = None
        self.init_ui()
        self.update_start_button()

    def init_ui(self):
        # Setting window geometry
        self.setWindowTitle('JIRA work logger')
        self.setWindowIcon(QIcon('gui/misc/clock-icon.ico'))

        # Setting menu bar
        app_menu = self.menubar.addMenu('Help')
        exit_action = QAction('Exit', self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.triggered.connect(qApp.quit)
        app_menu.addAction(exit_action)

        # Setting root frame
        self.root.addTab(self.configurator, 'Logger Setup')
        self.root.addTab(self.console, 'Logger Output')
        self.setCentralWidget(self.root)

    def setup_worker_thread(self):
        self.worker = LogWorker(self.params)
        self.worker_thread = QThread()
        self.worker.moveToThread(self.worker_thread)

        # Assign signals to slots
        self.worker.msg.connect(self.console.print_msg)
        self.worker.warn.connect(self.console.print_warn)
        self.worker.err.connect(self.console.print_err)
        self.worker_thread.started.connect(self.worker.execute_logging)
        self.worker_thread.finished.connect(self.stop_worker_thread)

    def execute_autologging(self):
        get_main_window().findChild(
            QWidget, 'main_buttons',
            Qt.FindChildrenRecursively).start_btn.setDisabled(True)
        self.read_params()
        self.setup_worker_thread()
        self.root.setCurrentIndex(1)
        qApp.processEvents()
        self.worker_thread.start()

    def stop_worker_thread(self):
        self.console.print_msg('Worker thread has been stopped')
        self.worker_thread.deleteLater()
        get_main_window().findChild(
            QWidget, 'main_buttons',
            Qt.FindChildrenRecursively).start_btn.setEnabled(True)
        qApp.processEvents()

    def update_start_button(self):
        self.read_params()
        start_btn = self.findChild(QWidget, 'main_buttons',
                                   Qt.FindChildrenRecursively).start_btn

        if not [param for param in MANDATORY_PARAMS if not self.params[param]]:
            if True in list(self.params['tasks_filter'].values()):
                start_btn.setEnabled(True)
                return

        start_btn.setDisabled(True)

    def read_params(self):
        """Reading params from widgets across Configurator"""
        # JIRA settings
        jira_widget = self.findChild(QWidget, 'jira_settings',
                                     Qt.FindChildrenRecursively)
        self.params['jira_host'] = jira_widget.host_ln.text()
        self.params['jira_user'] = jira_widget.user_ln.text()
        self.params['jira_pass'] = jira_widget.pass_ln.text()

        # Tasks filter settings
        tasks_filter_widget = self.findChild(QWidget, 'tasks_filter',
                                             Qt.FindChildrenRecursively)
        self.params['tasks_filter'][
            'user_assignee'] = tasks_filter_widget.is_assignee.isChecked()
        self.params['tasks_filter'][
            'user_validator'] = tasks_filter_widget.is_validator.isChecked()
        self.params['tasks_filter'][
            'user_creator'] = tasks_filter_widget.is_creator.isChecked()

        # Working days settings
        days_widget = self.findChild(QWidget, 'days_config',
                                     Qt.FindChildrenRecursively)
        self.params['work_days'] = days_widget.weekdays
        self.params['target_hrs'] = days_widget.target_hrs.value()
        self.params['daily_tasks'] = tasks_string_to_dict(
            days_widget.daily_tasks.text())
        self.params['tasks_comment'] = days_widget.tasks_comment.text()
        self.params['daily_only'] = days_widget.daily_only.isChecked()
        self.params['ignore_tasks'] = tasks_string_to_list(
            days_widget.ignore_tasks.text())

        # Date settings
        date_widget = self.findChild(QWidget, 'dates_selector',
                                     Qt.FindChildrenRecursively)
        self.params['from_date'] = date_widget.from_cal.selectedDate(
        ).toString(Qt.ISODate)
        self.params['to_date'] = date_widget.to_cal.selectedDate().toString(
            Qt.ISODate)

    def load_config(self):
        config_path = Path(CONFIG_FILE)

        if config_path.exists():
            self.params.update(
                yaml.load(config_path.read_text(), Loader=yaml.FullLoader))
            return
Пример #53
0
class FlashingDialog(QDialog):
    def __init__(self, parent):
        super().__init__()
        self.setWindowTitle("Flashing...")
        esptool.sw.read_start.connect(self.read_start)
        esptool.sw.read_progress.connect(self.read_progress)
        esptool.sw.read_finished.connect(self.read_finished)
        esptool.sw.erase_start.connect(self.erase_start)
        esptool.sw.erase_finished.connect(self.erase_finished)
        esptool.sw.write_start.connect(self.write_start)
        esptool.sw.write_progress.connect(self.write_progress)
        esptool.sw.write_finished.connect(self.write_finished)
        self.setFixedWidth(400)
        self.nrBinFile = QNetworkRequest()
        self.parent = parent
        vl = VLayout(10, 10)
        self.setLayout(vl)
        self.bin_data = b""
        self.error_msg = None
        self.progress_task = QProgressBar()
        self.progress_task.setFixedHeight(45)
        self.task = QLabel()
        self.erase_timer = QTimer()
        self.erase_timer.setSingleShot(False)
        self.erase_timer.timeout.connect(self.erase_progress)
        self.btns = QDialogButtonBox(QDialogButtonBox.Abort)
        self.dlgText = QLabel(
            "Press the Boot button for a few seconds to start the flashing process"
        )
        vl.addWidgets([self.dlgText, self.task, self.progress_task, self.btns])
        self.btns.rejected.connect(self.abort)
        # process starts
        self.bin_file = parent.bin_file
        self.run_esptool()

    def updateBinProgress(self, recv, total):
        self.progress_task.setValue(recv // total * 100)

    def read_start(self):
        self.progress_task.setValue(0)
        self.task.setText("Saving image backup...")

    def read_progress(self, value):
        self.progress_task.setValue(value)

    def read_finished(self):
        self.progress_task.setValue(100)
        self.task.setText("Writing done.")

    def erase_start(self):
        self.btns.setEnabled(False)
        self.progress_task.setValue(0)
        self.task.setText("Erasing flash... (this may take a while)")
        self.erase_timer.start(1000)

    def erase_progress(self):
        self.progress_task.setValue(self.progress_task.value() + 5)

    def erase_finished(self):
        self.progress_task.setValue(100)
        self.task.setText("Erasing done.")
        self.erase_timer.stop()
        self.btns.setEnabled(True)

    def write_start(self):
        self.dlgText.setText("Flashing in progress...")
        self.progress_task.setValue(0)
        self.task.setText("Writing image...")

    def write_progress(self, value):
        self.progress_task.setValue(value)

    def write_finished(self):
        self.progress_task.setValue(100)
        self.task.setText("Writing done.")
        self.accept()

    def run_esptool(self):
        self.espthread = QThread()
        self.espworker = ESPWorker(self.parent.cbxPort.currentData(),
                                   self.bin_file)
        self.espworker.port_error.connect(self.error)
        self.espworker.moveToThread(self.espthread)
        self.espthread.started.connect(self.espworker.execute)
        self.espthread.start()

    def abort(self):
        self.espworker.stop()
        self.espthread.quit()
        self.espthread.wait(2000)
        self.reject()

    def error(self, e):
        self.error_msg = e
        self.reject()

    def accept(self):
        self.espworker.stop()
        self.espthread.quit()
        self.espthread.wait(2000)
        self.done(QDialog.Accepted)
Пример #54
0
class ObserverState(QObject):
    """
    Handles Observer state (current trip info, current haul info, Gear Type, etc)
    and related database interactions
    """

    modelChanged = pyqtSignal()
    currentHaulChanged = pyqtSignal(QVariant)
    currentTripChanged = pyqtSignal(QVariant)
    tripsChanged = pyqtSignal(QVariant)
    haulsChanged = pyqtSignal(QVariant)
    setsChanged = pyqtSignal(QVariant)
    catchesChanged = pyqtSignal(QVariant)
    trawlMaxDepthFathomsChanged = pyqtSignal(QVariant)
    trawlMaxBasketWeightLbsChanged = pyqtSignal(QVariant)
    trawlConfirmBasketWeightLbsChanged = pyqtSignal(QVariant)
    isGearTypeTrawlChanged = pyqtSignal(bool)
    currentObserverChanged = pyqtSignal(QVariant)
    driveLettersChanged = pyqtSignal(QVariant)
    lastBackupTimeChanged = pyqtSignal()
    backupStatusChanged = pyqtSignal(bool,
                                     QVariant,
                                     arguments=["success", "message"])
    unusedSignal = pyqtSignal(
        name='unusedSignal'
    )  # For properties without a signal; use to avoid QML warning.

    currentCatchCatChanged = pyqtSignal(QVariant)
    catchCatNameChanged = pyqtSignal(str)
    speciesNameChanged = pyqtSignal(str)
    commentsChanged = pyqtSignal(str)
    wm5WeightChanged = pyqtSignal(
        int, QVariant,
        arguments=["catchId", "new_wt"])  # tell CC page to update tableView

    # If we are in these appstates, always save comments to Trips.notes
    # (instead of haul level: FishingActivities.notes)
    trip_comment_states = ('home_state', 'start_trawl_state',
                           'end_trawl_state', 'start_fg_state', 'end_fg_state',
                           'hauls_state', 'sets_state', 'trip_errors_state',
                           'logbook_state', 'backup_state',
                           'select_trip_state')

    def __init__(self, db):
        super().__init__()
        self._logger = logging.getLogger(__name__)

        self._db = db

        # These data will also get saved into (new) Trips
        cs_query = Settings.select().where(Settings.parameter == 'catch_share')
        if not cs_query.exists():
            Settings.create(parameter='catch_share', value='TRUE')
        self._catchshare = cs_query.get()

        gt_query = Settings.select().where(Settings.parameter == 'gear_type')
        if not gt_query.exists():
            Settings.create(parameter='gear_type', value='TRUE')
        self._geartype_trawl_default = gt_query.get()

        self._users = ObserverUsers()

        self._trips = ObserverTrip()
        self._hauls = Hauls(db=db)
        self._sets = Sets(db=db)
        self._catches = ObserverCatches(db=db)

        self._current_cc = None

        self._current_cc_name = ""  # for display
        self._current_spec_name = ""  # for display

        fr_query = Settings.select().where(Settings.parameter == 'first_run')
        if not fr_query.exists():
            Settings.create(parameter='first_run', value='TRUE')
        self._firstrun = fr_query.get()

        cu_query = Settings.select().where(
            Settings.parameter == 'current_user')
        if not cu_query.exists():
            Settings.create(parameter='current_user')
        self._current_user = cu_query.get()

        cu_query = Settings.select().where(
            Settings.parameter == 'current_user_id')
        if not cu_query.exists():
            Settings.create(parameter='current_user_id')
        self._current_user_id = cu_query.get()

        # Type max depth as integer
        self._trawl_max_depth_fathoms = int(
            self.getset_setting(
                'trawl_max_depth_fathoms',
                DefaultDefaultSettings.trawl_max_depth_fathoms))
        self.trawlMaxDepthFathomsChanged.emit(self._trawl_max_depth_fathoms)

        # Confirmation-required basket weight, typed as integer
        self._trawl_confirm_basket_weight_lbs = int(
            self.getset_setting(
                'trawl_confirm_basket_weight_lbs',
                DefaultDefaultSettings.trawl_confirm_basket_weight_lbs))
        self.trawlConfirmBasketWeightLbsChanged.emit(
            self._trawl_confirm_basket_weight_lbs)

        # Max basket weight as integer, typed as integer
        self._trawl_max_basket_weight_lbs = int(
            self.getset_setting(
                'trawl_max_basket_weight_lbs',
                DefaultDefaultSettings.trawl_max_basket_weight_lbs))
        self.trawlMaxBasketWeightLbsChanged.emit(
            self._trawl_max_basket_weight_lbs)

        # Minimum and maximum degrees latitude as integer.
        # No emits necessary: this value will not change during a run.
        self._trawl_min_latitude_degrees = int(
            self.getset_setting(
                'trawl_minimum_latitude_degrees',
                DefaultDefaultSettings.trawl_minimum_latitude_degrees))
        self._trawl_max_latitude_degrees = int(
            self.getset_setting(
                'trawl_maximum_latitude_degrees',
                DefaultDefaultSettings.trawl_maximum_latitude_degrees))

        # DB Backup Thread
        self._backup_thread = QThread()
        self._backup_worker = None

        self._comments_all = ''
        self._comments_trip = ''
        self._comments_haul = dict()
        self._db_formatted_comments_trip = ''
        self._db_formatted_comments_haul = dict()

        self.currentTripId = ObserverDBUtil.db_load_setting(
            'trip_number')  # Current Trip ID if set
        self.update_comments()

        self._catches.retainedCatchWeightChanged.connect(
            self.update_wm5_catch_weights
        )  # ret. catch changes --> WM5 updates
        self._hauls.otcWeightChanged.connect(
            self.update_wm5_catch_weights)  # otc changes --> WM5 updates

    @staticmethod
    def getset_setting(parm_name, default_val):
        """ Get parm_name setting from SETTINGS table.
            If setting not in SETTINGS, use default_val.
            Side-effect: also add default_val to SETTINGS table.
        """
        fr_query = Settings.select().where(Settings.parameter == parm_name)
        if not fr_query.exists():
            Settings.create(parameter=parm_name, value=default_val)
        return fr_query.get().value

    @pyqtSlot()
    def reset(self):
        # self._logger.warn('RESETTING STATE - TODO')

        self._current_cc = None

        self._current_cc_name = ""  # for display
        self._current_spec_name = ""  # for display

    @staticmethod
    def pad_trip_id(id_value):
        """
        Returns padded string from int
        :param id_value: int ID
        :return:
        """
        return '{0:05d}'.format(id_value)

    @pyqtProperty(
        str, notify=unusedSignal
    )  # Specify notify to avoid "depends on non-NOTIFYable properties" warning
    def optecsVersion(self):
        return optecs_version

    @pyqtProperty(
        str, notify=unusedSignal
    )  # Specify notify to avoid "depends on non-NOTIFYable properties" warning
    def dbVersion(self):
        return ObserverDBUtil.get_setting('database_revision', 'unknown')

    @pyqtProperty(
        int, notify=unusedSignal
    )  # Specify notify to avoid "depends on non-NOTIFYable properties" warning
    def displayDecimalPlaces(self):
        """
        :return: Number of floating point decimal places to use in display text boxes.
        """
        return display_decimal_places

    @pyqtProperty(bool)
    def firstRun(self):
        return self._firstrun.value.lower() == 'true'

    @firstRun.setter
    def firstRun(self, value):
        self._firstrun.value = 'TRUE' if value else 'FALSE'
        self._firstrun.save()

    @pyqtProperty(QVariant, notify=currentObserverChanged)
    def currentObserver(self):
        return self._current_user.value

    @pyqtProperty(QVariant, notify=trawlMaxDepthFathomsChanged)
    def trawlMaxDepthFathoms(self):
        return self._trawl_max_depth_fathoms

    @pyqtProperty(QVariant, notify=trawlMaxBasketWeightLbsChanged)
    def trawlMaxBasketWeightLbs(self):
        """ Used in Catch Counts/Weights.
            A basket can't weigh more than this amount.
        """
        return self._trawl_max_basket_weight_lbs

    @pyqtProperty(QVariant, notify=trawlConfirmBasketWeightLbsChanged)
    def trawlConfirmBasketWeightLbs(self):
        """ Used in Catch Counts/Weights.
            A basket that weighs no more than trawlMaxBasketWeightLbs
            yet weighs more than this amount should prompt a confirmation window.
        """
        return self._trawl_confirm_basket_weight_lbs

    @pyqtProperty(QVariant, notify=unusedSignal)
    def trawlMinLatitudeDegrees(self):
        return self._trawl_min_latitude_degrees

    @pyqtProperty(QVariant, notify=unusedSignal)
    def trawlMaxLatitudeDegrees(self):
        return self._trawl_max_latitude_degrees

    @currentObserver.setter
    def currentObserver(self, value):
        """
        Set username for convenience, ties to appstate.users.currentUserName
        @param value: username
        @return:
        """
        self._logger.info('Current observer set to {0}'.format(value))
        self._current_user.value = value
        self._current_user.save()
        self._current_user_id.value = ObserverUsers.get_user_id(value)
        self._current_user_id.save()

    @pyqtProperty(bool)
    def defaultCatchShare(self):
        return self._catchshare.value.lower() == 'true'

    @defaultCatchShare.setter
    def defaultCatchShare(self, value):
        self._catchshare.value = 'TRUE' if value else 'FALSE'
        self._catchshare.save()

    @pyqtProperty(bool, notify=isGearTypeTrawlChanged)
    def isGearTypeTrawl(self):
        return self._geartype_trawl_default.value.lower() == 'true'

    @isGearTypeTrawl.setter
    def isGearTypeTrawl(self, value):
        self._geartype_trawl_default.value = 'TRUE' if value else 'FALSE'
        self._geartype_trawl_default.save()
        self.isGearTypeTrawlChanged.emit(value)

    @pyqtProperty(bool, notify=isGearTypeTrawlChanged)
    def isFixedGear(self):
        return self._geartype_trawl_default.value.lower() != 'true'

    @pyqtProperty(QVariant, notify=haulsChanged)
    def hauls(self):
        return self._hauls

    @pyqtProperty(QVariant, notify=setsChanged)
    def sets(self):
        return self._sets

    @pyqtProperty(str, notify=tripsChanged)
    def currentTripId(self):
        return self._trips.tripId

    @pyqtProperty(QVariant, notify=currentObserverChanged)
    def users(self):
        return self._users

    @currentTripId.setter
    def currentTripId(self, trip_id):
        """
        Set Trip ID (currently same as db pk)
        @param trip_id: DB PK ID
        """
        if trip_id is None:
            self._logger.info('Current trip ID is not set.')
            return
        # this is also being set in ObserverTrips.tripId.setter, but fails if in debriefer mode (user mismatch).
        # adding here to ensure trip_number param is definitely set when selecting trip
        # TODO: consolidate logic for setting trip_number here, or rework ObserverTrip.tripId setter
        ObserverDBUtil.db_save_setting('trip_number', trip_id)
        self._logger.debug(
            f"setting SETTINGS parameter trip_number to {trip_id}")
        trip_id = int(trip_id)  # ensure integer key
        self._trips.tripId = trip_id
        self._hauls.reset()
        self._trips.load_current_trip(trip_id)
        if self.isFixedGear:
            self._sets.load_sets(trip_id=trip_id)
        else:
            self._hauls.load_hauls(trip_id=trip_id)

    @pyqtSlot(str, result='QVariant')
    def create_trip(self, vessel_name):
        """
        Create trip, looks up vessel_name by ID
        @param vessel_name: vessel name as seen in DB
        @return: new trip
        """
        if self.currentObserver is None or vessel_name is None:
            self._logger.error(
                'No Observer/ Vessel Selected, abort create trip')
            return None
        vessel_id = self.get_vessel_id(vessel_name)
        observer_id = self.users.currentUserID
        program_id = self.users.currentProgramID
        self._logger.info(
            'Creating trip with observer ID {}, vessel ID {} TODO PROGRAM ID'.
            format(observer_id, vessel_id))
        newtrip = self._trips.create_trip(vessel_id, observer_id, program_id)
        self.modelChanged.emit()
        return newtrip

    @pyqtSlot(str, result='QVariant')
    def create_catch(self, catch_category_id):
        """
        Create catch
        @param catch_category_id:
        @return: new catch model (dict)
        """
        if catch_category_id is None:
            self._logger.error(
                'Bad catch category ID passed to create_catch, abort.')
            return None

        current_haul_set_id = self.sets.current_set.fishing_activity if self.isFixedGear \
            else self.hauls.current_haul.fishing_activity
        if current_haul_set_id is None:
            self._logger.error('No current haul/set ID, abort create_catch')
            return None

        try:
            catch_category_id = int(catch_category_id)
        except ValueError as ve:
            self._logger.error(
                f'catch_category_id "{catch_category_id}" is not int, aborting create_catch ({ve}).'
            )
            return None

        self._logger.info(
            'Creating catch with catch category ID {}, haul id {}'.format(
                catch_category_id, current_haul_set_id))
        newcatch_model = self._catches.create_catch(
            catch_category_id=catch_category_id,
            fishing_activity_pk=current_haul_set_id)
        self.modelChanged.emit()
        return newcatch_model

    @pyqtSlot()
    def end_trip(self):
        self._trips.end_trip()
        self.hauls.reset()
        self.sets.reset()
        self.modelChanged.emit()

    @pyqtProperty(QVariant, notify=catchesChanged)
    def catches(self):
        return self._catches

    @pyqtProperty(QVariant, notify=modelChanged)
    def TripsModel(self):
        return self._trips.TripsModel

    @pyqtProperty(QVariant, notify=tripsChanged)
    def trips(self):
        return self._trips

    #  Properties for display (in header, etc)
    @pyqtProperty(str, notify=catchCatNameChanged)
    def catchCatName(self):
        return self._current_cc_name

    @catchCatName.setter
    def catchCatName(self, value):
        self._current_cc_name = value
        self.catchCatNameChanged.emit(value)

    @pyqtProperty(str, notify=speciesNameChanged)
    def speciesName(self):
        return self._current_spec_name

    @speciesName.setter
    def speciesName(self, value):
        self._current_spec_name = value
        self.speciesNameChanged.emit(value)

    @pyqtProperty(str, notify=commentsChanged)
    def comments(self):
        return self._comments_all

    @pyqtProperty(str, notify=commentsChanged)
    def db_formatted_comments(self):
        return self._db_formatted_comments_trip

    @pyqtSlot(name='updateComments')
    def update_comments(self):
        if not self.currentTripId or not self.currentObserver:
            # self._logger.info(f'No trip, skip saving comments: {self.currentTripId}')
            return

        trip_id = int(self.currentTripId)
        self._logger.info(f'Updating comments for TRIP ID {trip_id}')
        self._comments_all = ''
        self._comments_trip = ''
        self._comments_haul = dict()  # { haul_id: "comment" }
        self._db_formatted_comments_trip = ''
        self._db_formatted_comments_haul = dict()
        comments_q = Comment.select().where(Comment.trip == trip_id).order_by(
            Comment.comment_id)
        if not comments_q.count():
            self._logger.debug(f'No comments found for trip: {trip_id}')
            return
        for comment in comments_q:
            # Removed starting dash comment delimiter to adhere to IFQ TRIP_CHECK convention that
            # notes start with an alphanumeric. But continuing to use trailing dash separator.
            new_comment_string = f'{comment.username} ({comment.appstateinfo}) ' \
                                  f'{comment.comment_date} ---\n{comment.comment}\n\n'
            self._comments_all += new_comment_string

            try:
                # Only want first half of appstateinfo, that variable now also holds the page title text
                if comment.fishing_activity is None or comment.appstateinfo.split(
                        "::")[0] in self.trip_comment_states:
                    self._comments_trip += new_comment_string

                    self._db_formatted_comments_trip += self._db_format_one_comment(
                        comment)
                else:
                    haul_id = comment.fishing_activity.fishing_activity
                    if haul_id not in self._comments_haul.keys():
                        self._comments_haul[haul_id] = new_comment_string
                        self._db_formatted_comments_haul[
                            haul_id] = self._db_format_one_comment(comment)
                    else:  # append
                        self._comments_haul[haul_id] += new_comment_string
                        self._db_formatted_comments_haul[
                            haul_id] += self._db_format_one_comment(comment)
            except Exception as e:  # Handle load of bad previous comment weirdness
                self._logger.error(e)

        # now save to NOTES for Trip
        try:
            trip_q = Trips.get(Trips.trip == trip_id)
            trip_q.notes = ObserverDBUtil.escape_linefeeds(
                self._db_formatted_comments_trip)
            trip_q.save()
            self._logger.info(
                f"Wrote {len(trip_q.notes)} characters to Trips.notes.")
            for haul_id in self._comments_haul.keys():
                haul_q = FishingActivities.get(
                    (FishingActivities.trip == trip_id)
                    & (FishingActivities.fishing_activity == haul_id))
                haul_q.notes = ObserverDBUtil.escape_linefeeds(
                    self._db_formatted_comments_haul[haul_id])
                haul_q.save()
                self._logger.info(
                    f"Wrote {len(haul_q.notes)} characters to FishingActivities.notes."
                )

        except Trips.DoesNotExist as e:
            self._logger.warning(f'Cannot save comment, {e}')
        except FishingActivities.DoesNotExist as e:
            self._logger.warning(f'Cannot save comment, {e}')

        self.commentsChanged.emit(self._comments_trip)
        # logging.debug('Comments now {}'.format(self._comments))

    @staticmethod
    def strip_db_comment(comment):
        remove_text = ['\r', '\n']
        replace_text = [(',', '|')]
        for r in remove_text:
            comment = comment.replace(r, '')
        for r in replace_text:
            comment = comment.replace(r[0], r[1])
        return comment

    def _db_format_one_comment(self, comment):
        db_format_comment = self.strip_db_comment(comment.comment)
        # Removed starting dash comment delimiter to adhere to IFQ TRIP_CHECK convention that
        # notes start with an alphanumeric. But continuing to use trailing dash separators (but only 4).
        # also need some smarts for the deleting the first half of the app state string if it's not
        # blank

        newappstate = comment.appstateinfo if len(comment.appstateinfo.split("::")) < 2 else \
                      comment.appstateinfo.split("::", maxsplit=1)[1]

        db_formatted_comment = f'{comment.username} ' \
                               f'({newappstate}) {comment.comment_date} ' \
                               f': {db_format_comment} -- '
        return db_formatted_comment

    def _get_size_of_existing_comments(self):
        """ How many characters are currently in Trips.note for this trip? Ignores haul-level comments"""
        if not self.currentTripId or not self.currentObserver:
            self._logger.warning(
                f'Invalid trip or observer to save comments: {self.currentTripId}'
            )
            return 0
        else:
            trip_id = int(self.currentTripId)
            self._logger.info(
                f'Calculating size of existing comments for TRIP ID {trip_id}')

        db_formatted_comments = ''
        # Ignore haul-level comments
        comments_q = Comment. \
            select(). \
            where((Comment.trip == trip_id) & (Comment.fishing_activity.is_null(True))). \
            order_by(Comment.comment_id)
        if not comments_q.count():
            self._logger.info(f'No comments found for trip: {trip_id}')
            return 0

        for comment in comments_q:
            db_formatted_comments += self._db_format_one_comment(comment)

        return len(db_formatted_comments)

    @pyqtProperty(
        int, notify=unusedSignal
    )  # Specify notify to avoid "depends on non-NOTIFYable properties" warning
    def maxTextSizeOfObserverComments(self):
        return max_text_size_observer_comments

    @pyqtSlot(str, str, result=int, name='getFreeCommentSpaceAfterProposedAdd')
    def get_free_observer_comment_space_after_proposed_add(
            self, proposed_comment, proposed_appstate):

        total_size_after, _ = self.get_free_comment_space_after_proposed_add(
            proposed_comment, proposed_appstate,
            max_text_size_observer_comments)

        return total_size_after

    def get_free_comment_space_after_proposed_add(self, proposed_comment,
                                                  proposed_appstate,
                                                  max_text_allowed):

        size_before = self._get_size_of_existing_comments()

        # Instantiate a new model instance in memory, but don't add to database (no create, no save)
        newcomment = Comment(username=self.currentObserver,
                             comment_date=ObserverDBUtil.get_arrow_datestr(),
                             appstateinfo=proposed_appstate,
                             comment=proposed_comment,
                             trip=self.currentTripId)

        size_of_new_comment = 0 if newcomment is None else len(
            self._db_format_one_comment(newcomment))
        size_after = size_before + size_of_new_comment

        return max_text_allowed - size_after, size_of_new_comment

    def update_wm5_catch_weights(self):
        """
        Go to DB directly and find catches w. WM5.
        Update catch_weight with OTC-RET., then EMIT to signal QML
        func lives here so it can interact with both _hauls and _catches signals

        NOTE: catch_weight cant be negative in DB, so if negative set to null / None
        :return: None
        """
        if self.isFixedGear:  # wm5 doesn't exist w. FG
            return

        wm5_catches = Catches.select(Catches.catch, Catches.catch_num).where(
            (Catches.fishing_activity == self._hauls.currentHaulDBId)
            & (Catches.catch_weight_method == '5')).execute()
        new_wt = self._hauls.getData(
            'observer_total_catch') - self._hauls.retainedHaulWeight
        new_wt = new_wt if new_wt > 0 else None  # wt can't be negative in DB, set to None/Null
        for c in wm5_catches:  # there shouldn't be more than one, but just in case
            Catches.update(catch_weight=new_wt).where(
                (Catches.catch == c.catch)).execute()

            logging.info(
                f"CatchNum {c.catch_num} (ID: {c.catch}) WM5 weight updated to {new_wt}"
            )
            self.wm5WeightChanged.emit(
                c.catch, new_wt)  # tell CC QML page to update too

    @pyqtSlot(str, str, str, name='upsertComment')
    def upsert_comment(self, comment_prefix, comment, appstate):
        """
        Use for comment update/insert oustide of Comment dialog box.
        Find comment with prefix, if exists, replace, else insert
        :param comment_prefix: str, e.g. CollectionMethod=
        :param comment: str, e.g. string after prefix
        :param appstate: str, e.g. state of app + title of current screen
        :return: None
        """
        new_comment_date = ObserverDBUtil.get_arrow_datestr()
        new_comment = f"{comment_prefix}{comment}"

        # try to get comment model and update
        try:
            c = Comment.get(Comment.comment.contains(comment_prefix),
                            Comment.trip == self.currentTripId)
            Comment.update(comment=new_comment,
                           comment_date=new_comment_date,
                           username=self.currentObserver).where(
                               Comment.comment_id == c.comment_id).execute()

        except ValueError:  # trip id is not defined yet
            return

        # if existing comment not found, create a new one
        except Comment.DoesNotExist:
            Comment.create(comment=new_comment,
                           comment_date=new_comment_date,
                           username=self.currentObserver,
                           trip=self.currentTripId,
                           appstateinfo=appstate)

        # parse comments to trips/fishing_activities
        self.update_comments()

    @pyqtSlot(str, str, name='addComment')
    def add_comment(self, comment, appstate):
        """
        Adds date, username, and comment to Comments
        :return:
        """
        if not self.currentTripId:
            self._logger.error(
                'No trip selected, comment NOT saved: {}'.format(comment))
            return
        self._logger.info(
            f'Adding comment "{comment}" to current trip {self.currentTripId}')
        # TODO Add to trips, not Comment
        if self.isFixedGear:
            haul_db_id = self.sets.currentSetDBId if self.sets else None
        else:
            haul_db_id = self.hauls.currentHaulDBId if self.hauls else None

        newcomment = Comment.create(
            username=self.currentObserver,
            comment_date=ObserverDBUtil.get_arrow_datestr(),
            appstateinfo=appstate,
            comment=comment,
            trip=self.currentTripId,
            fishing_activity=haul_db_id)
        newcomment.save()
        self.update_comments()

    @pyqtSlot(str, result=int)
    def get_vessel_id(self, vessel_name):
        """
        Check for vessel name
        @param vessel_name: vessel to check for in DB
        @return: Returns ID if vessel is in the DB, otherwise 0
        """
        try:
            vessel_name = vessel_name.lower()
            try:
                vessel = Vessels.get(
                    fn.Lower(Vessels.vessel_name) == vessel_name)
                logging.info('ID {} found for vessel {}'.format(
                    vessel.vessel, vessel_name))
                return vessel.vessel
            except DoesNotExist:
                logging.warning('Vessel not found: {}'.format(vessel_name))
                return 0
        except ValueError:
            logging.warning('Invalid vessel name {}'.format(vessel_name))
            return 0

    @pyqtSlot(name='raiseException')
    def raise_exception(self):
        """
        QML code can call this to raise an exception in order to test the unhandled exception handler.
        Should be only called by a developer or product proxy (Newport Team).
        Should be triggered only by an obscure keystroke sequence in the UI
        (currently 10 mouse clicks in a row on the label (not text box) of Visual OTC of the Haul Details screen).
        :return:
        """
        msgStart = "Exception intentionally raised for testing. "
        msgEnd = "End of intentionally long exception msg"
        filler = "abcdefghijklmnopqrstuvwxyz " * 200
        raise OptecsTestException(msgStart + filler + msgEnd)

    @pyqtProperty(bool, notify=unusedSignal)
    def isTestMode(self):
        """
        if DB is pointing at IFQADMIN or IFQDEV, True else False (production!)
        @return: True if Test mode
        """
        mode = ObserverDBUtil.get_setting('optecs_mode')
        return False if mode == 'ifq' else True

    @pyqtProperty(bool, notify=unusedSignal)
    def isTrainingMode(self):
        """
        @return: True if training mode
        """
        mode = ObserverDBUtil.get_setting('training')
        return True if mode == 'TRUE' else False

    @pyqtProperty(QVariant, notify=unusedSignal)
    def optecsMode(self):
        """
        @return: optecs mode, e.g. ifqadmin
        """
        return ObserverDBUtil.get_setting('optecs_mode', 'ifqadmin').upper()

    @pyqtSlot(str, name='backupToPath', result=str)
    def backup_db_to_path(self, path):
        try:
            if not self._backup_thread.isRunning():
                self._backup_worker = BackupDBWorker(dest_path=path)
                self._backup_worker.moveToThread(self._backup_thread)
                self._backup_worker.backupStatus.connect(
                    self._backup_status_received)
                self._backup_thread.started.connect(self._backup_worker.run)
                self._backup_thread.start()
            return f'Backup started to\n{path}...'
        except Exception as e:
            return textwrap.fill(f'FAIL:\nCould not back up DB:\n{e}', 60)

    def _backup_status_received(self, success, message):
        """
        Method to catch the backup results
        @param success: True/False if succeeded
        @param message: Description of status
        @return:
        """
        if success:
            self._update_backup_time()
        self.backupStatusChanged.emit(success, message)
        self._backup_thread.quit()

    @pyqtProperty(QVariant, notify=lastBackupTimeChanged)
    def lastBackupTime(self):
        """
        Returns string representation of last DB backup.
        @return: string value
        """
        try:
            last_sync = Settings.get(Settings.parameter == 'last_backup_time')
            last_time = arrow.get(last_sync.value)
            return last_time.humanize()
        except Settings.DoesNotExist:
            return 'Never'

    def _update_backup_time(self):
        """
        Set most recent backup time to now.
        @return:
        """
        try:
            last_sync = Settings.get(Settings.parameter == 'last_backup_time')
            last_sync.value = arrow.now()
            last_sync.save()
        except Settings.DoesNotExist:
            new_setting = Settings.create(parameter='last_backup_time',
                                          value=arrow.now())
            new_setting.save()
        self.lastBackupTimeChanged.emit()

    @pyqtSlot(name='updateDriveLetters')
    def update_drive_letters(self):
        """
        Send new drive letters signal
        @return:
        """
        self.driveLettersChanged.emit(
            ObserverDBUtil.get_external_drive_letters())

    @pyqtProperty(QVariant, notify=driveLettersChanged)
    def driveLetters(self):
        return ObserverDBUtil.get_external_drive_letters()
Пример #55
0
class TERuleQueryTab(SEToolsWidget, QScrollArea):

    """A Type Enforcement rule query."""

    def __init__(self, parent, policy, perm_map):
        super(TERuleQueryTab, self).__init__(parent)
        self.log = logging.getLogger(__name__)
        self.policy = policy
        self.query = TERuleQuery(policy)
        self.setupUi()

    def __del__(self):
        self.thread.quit()
        self.thread.wait(5000)
        logging.getLogger("setools.terulequery").removeHandler(self.handler)

    def setupUi(self):
        self.load_ui("terulequery.ui")

        # set up source/target autocompletion
        typeattr_completion_list = [str(t) for t in self.policy.types()]
        typeattr_completion_list.extend(str(a) for a in self.policy.typeattributes())
        typeattr_completer_model = QStringListModel(self)
        typeattr_completer_model.setStringList(sorted(typeattr_completion_list))
        self.typeattr_completion = QCompleter()
        self.typeattr_completion.setModel(typeattr_completer_model)
        self.source.setCompleter(self.typeattr_completion)
        self.target.setCompleter(self.typeattr_completion)

        # set up default autocompletion
        type_completion_list = [str(t) for t in self.policy.types()]
        type_completer_model = QStringListModel(self)
        type_completer_model.setStringList(sorted(type_completion_list))
        self.type_completion = QCompleter()
        self.type_completion.setModel(type_completer_model)
        self.default_type.setCompleter(self.type_completion)

        # setup indications of errors on source/target/default
        self.orig_palette = self.source.palette()
        self.error_palette = self.source.palette()
        self.error_palette.setColor(QPalette.Base, Qt.red)
        self.clear_source_error()
        self.clear_target_error()
        self.clear_default_error()
        self.clear_xperm_error()

        # populate class list
        self.class_model = SEToolsListModel(self)
        self.class_model.item_list = sorted(self.policy.classes())
        self.tclass.setModel(self.class_model)

        # populate perm list
        self.perms_model = PermListModel(self, self.policy)
        self.perms.setModel(self.perms_model)

        # populate bool list
        self.bool_model = SEToolsListModel(self)
        self.bool_model.item_list = sorted(self.policy.bools())
        self.bool_criteria.setModel(self.bool_model)

        # set up results
        self.table_results_model = TERuleTableModel(self)
        self.sort_proxy = QSortFilterProxyModel(self)
        self.sort_proxy.setSourceModel(self.table_results_model)
        self.table_results.setModel(self.sort_proxy)
        self.table_results.sortByColumn(0, Qt.AscendingOrder)

        # set up processing thread
        self.thread = QThread()
        self.worker = QueryResultsUpdater(self.query, self.table_results_model)
        self.worker.moveToThread(self.thread)
        self.worker.raw_line.connect(self.raw_results.appendPlainText)
        self.worker.finished.connect(self.update_complete)
        self.worker.finished.connect(self.thread.quit)
        self.thread.started.connect(self.worker.update)

        # create a "busy, please wait" dialog
        self.busy = QProgressDialog(self)
        self.busy.setModal(True)
        self.busy.setRange(0, 0)
        self.busy.setMinimumDuration(0)
        self.busy.canceled.connect(self.thread.requestInterruption)
        self.busy.reset()

        # update busy dialog from query INFO logs
        self.handler = LogHandlerToSignal()
        self.handler.message.connect(self.busy.setLabelText)
        logging.getLogger("setools.terulequery").addHandler(self.handler)

        # Ensure settings are consistent with the initial .ui state
        self.set_source_regex(self.source_regex.isChecked())
        self.set_target_regex(self.target_regex.isChecked())
        self.set_default_regex(self.default_regex.isChecked())
        self.toggle_xperm_criteria()
        self.criteria_frame.setHidden(not self.criteria_expander.isChecked())
        self.notes.setHidden(not self.notes_expander.isChecked())

        # connect signals
        self.buttonBox.clicked.connect(self.run)
        self.allowxperm.toggled.connect(self.toggle_xperm_criteria)
        self.auditallowxperm.toggled.connect(self.toggle_xperm_criteria)
        self.neverallowxperm.toggled.connect(self.toggle_xperm_criteria)
        self.dontauditxperm.toggled.connect(self.toggle_xperm_criteria)
        self.clear_ruletypes.clicked.connect(self.clear_all_ruletypes)
        self.all_ruletypes.clicked.connect(self.set_all_ruletypes)
        self.source.textEdited.connect(self.clear_source_error)
        self.source.editingFinished.connect(self.set_source)
        self.source_regex.toggled.connect(self.set_source_regex)
        self.target.textEdited.connect(self.clear_target_error)
        self.target.editingFinished.connect(self.set_target)
        self.target_regex.toggled.connect(self.set_target_regex)
        self.tclass.selectionModel().selectionChanged.connect(self.set_tclass)
        self.invert_class.clicked.connect(self.invert_tclass_selection)
        self.perms.selectionModel().selectionChanged.connect(self.set_perms)
        self.invert_perms.clicked.connect(self.invert_perms_selection)
        self.xperms.textEdited.connect(self.clear_xperm_error)
        self.xperms.editingFinished.connect(self.set_xperm)
        self.default_type.textEdited.connect(self.clear_default_error)
        self.default_type.editingFinished.connect(self.set_default_type)
        self.default_regex.toggled.connect(self.set_default_regex)
        self.bool_criteria.selectionModel().selectionChanged.connect(self.set_bools)

    #
    # Ruletype criteria
    #

    def _set_ruletypes(self, value):
        self.allow.setChecked(value)
        self.allowxperm.setChecked(value)
        self.auditallow.setChecked(value)
        self.auditallowxperm.setChecked(value)
        self.neverallow.setChecked(value)
        self.neverallowxperm.setChecked(value)
        self.dontaudit.setChecked(value)
        self.dontauditxperm.setChecked(value)
        self.type_transition.setChecked(value)
        self.type_member.setChecked(value)
        self.type_change.setChecked(value)

    def set_all_ruletypes(self):
        self._set_ruletypes(True)

    def clear_all_ruletypes(self):
        self._set_ruletypes(False)

    #
    # Source criteria
    #

    def clear_source_error(self):
        self.source.setToolTip("Match the source type/attribute of the rule.")
        self.source.setPalette(self.orig_palette)

    def set_source(self):
        try:
            self.query.source = self.source.text()
        except Exception as ex:
            self.log.error("Source type/attribute error: {0}".format(ex))
            self.source.setToolTip("Error: " + str(ex))
            self.source.setPalette(self.error_palette)

    def set_source_regex(self, state):
        self.log.debug("Setting source_regex {0}".format(state))
        self.query.source_regex = state
        self.clear_source_error()
        self.set_source()

    #
    # Target criteria
    #

    def clear_target_error(self):
        self.target.setToolTip("Match the target type/attribute of the rule.")
        self.target.setPalette(self.orig_palette)

    def set_target(self):
        try:
            self.query.target = self.target.text()
        except Exception as ex:
            self.log.error("Target type/attribute error: {0}".format(ex))
            self.target.setToolTip("Error: " + str(ex))
            self.target.setPalette(self.error_palette)

    def set_target_regex(self, state):
        self.log.debug("Setting target_regex {0}".format(state))
        self.query.target_regex = state
        self.clear_target_error()
        self.set_target()

    #
    # Class criteria
    #

    def set_tclass(self):
        selected_classes = []
        for index in self.tclass.selectionModel().selectedIndexes():
            selected_classes.append(self.class_model.data(index, Qt.UserRole))

        self.query.tclass = selected_classes
        self.perms_model.set_classes(selected_classes)

    def invert_tclass_selection(self):
        invert_list_selection(self.tclass.selectionModel())

    #
    # Permissions criteria
    #

    def set_perms(self):
        selected_perms = []
        for index in self.perms.selectionModel().selectedIndexes():
            selected_perms.append(self.perms_model.data(index, Qt.UserRole))

        self.query.perms = selected_perms

    def invert_perms_selection(self):
        invert_list_selection(self.perms.selectionModel())

    #
    # Extended permission criteria
    #
    def toggle_xperm_criteria(self):
        mode = any((self.allowxperm.isChecked(),
                    self.auditallowxperm.isChecked(),
                    self.neverallowxperm.isChecked(),
                    self.dontauditxperm.isChecked()))

        self.xperms.setEnabled(mode)
        self.xperms_equal.setEnabled(mode)

    def clear_xperm_error(self):
        self.xperms.setToolTip("Match the extended permissions of the rule. Comma-separated "
                               "permissions or ranges of permissions.")
        self.xperms.setPalette(self.orig_palette)

    def set_xperm(self):
        xperms = []
        try:
            text = self.xperms.text()

            if text:
                for item in self.xperms.text().split(","):
                    rng = item.split("-")
                    if len(rng) == 2:
                        xperms.append((int(rng[0], base=16), int(rng[1], base=16)))
                    elif len(rng) == 1:
                        xperms.append((int(rng[0], base=16), int(rng[0], base=16)))
                    else:
                        raise ValueError("Enter an extended permission or extended permission "
                                         "range, e.g. 0x5411 or 0x8800-0x88ff.")

                self.query.xperms = xperms
            else:
                self.query.xperms = None

        except Exception as ex:
            self.log.error("Extended permissions error: {0}".format(ex))
            self.xperms.setToolTip("Error: " + str(ex))
            self.xperms.setPalette(self.error_palette)

    #
    # Default criteria
    #

    def clear_default_error(self):
        self.default_type.setToolTip("Match the default type the rule.")
        self.default_type.setPalette(self.orig_palette)

    def set_default_type(self):
        self.query.default_regex = self.default_regex.isChecked()

        try:
            self.query.default = self.default_type.text()
        except Exception as ex:
            self.log.error("Default type error: {0}".format(ex))
            self.default_type.setToolTip("Error: " + str(ex))
            self.default_type.setPalette(self.error_palette)

    def set_default_regex(self, state):
        self.log.debug("Setting default_regex {0}".format(state))
        self.query.default_regex = state
        self.clear_default_error()
        self.set_default_type()

    #
    # Boolean criteria
    #

    def set_bools(self):
        selected_bools = []
        for index in self.bool_criteria.selectionModel().selectedIndexes():
            selected_bools.append(self.bool_model.data(index, Qt.UserRole))

        self.query.boolean = selected_bools

    #
    # Save/Load tab
    #
    def save(self):
        """Return a dictionary of settings."""
        settings = {}
        save_checkboxes(self, settings, ["criteria_expander", "notes_expander",
                                         "allow", "allowxperm",
                                         "auditallow", "auditallowxperm",
                                         "neverallow", "neverallowxperm",
                                         "dontaudit", "dontauditxperm",
                                         "type_transition", "type_change", "type_member",
                                         "source_indirect", "source_regex",
                                         "target_indirect", "target_regex",
                                         "perms_subset",
                                         "xperms_equal",
                                         "default_regex",
                                         "bools_equal"])
        save_lineedits(self, settings, ["source", "target", "xperms", "default_type"])
        save_listviews(self, settings, ["tclass", "perms", "bool_criteria"])
        save_textedits(self, settings, ["notes"])
        return settings

    def load(self, settings):
        load_checkboxes(self, settings, ["allow", "allowxperm",
                                         "auditallow", "auditallowxperm",
                                         "neverallow", "neverallowxperm",
                                         "dontaudit", "dontauditxperm",
                                         "type_transition", "type_change", "type_member",
                                         "criteria_expander", "notes_expander",
                                         "source_indirect", "source_regex",
                                         "target_indirect", "target_regex",
                                         "perms_subset",
                                         "xperms_equal",
                                         "default_regex",
                                         "bools_equal"])
        load_lineedits(self, settings, ["source", "target", "xperms", "default_type"])
        load_listviews(self, settings, ["tclass", "perms", "bool_criteria"])
        load_textedits(self, settings, ["notes"])

    #
    # Results runner
    #

    def run(self, button):
        # right now there is only one button.
        rule_types = []
        max_results = 0

        if self.allow.isChecked():
            rule_types.append("allow")
            max_results += self.policy.allow_count
        if self.allowxperm.isChecked():
            rule_types.append("allowxperm")
            max_results += self.policy.allowxperm_count
        if self.auditallow.isChecked():
            rule_types.append("auditallow")
            max_results += self.policy.auditallow_count
        if self.auditallowxperm.isChecked():
            rule_types.append("auditallowxperm")
            max_results += self.policy.auditallowxperm_count
        if self.neverallow.isChecked():
            rule_types.append("neverallow")
            max_results += self.policy.neverallow_count
        if self.neverallowxperm.isChecked():
            rule_types.append("neverallowxperm")
            max_results += self.policy.neverallowxperm_count
        if self.dontaudit.isChecked():
            rule_types.append("dontaudit")
            max_results += self.policy.dontaudit_count
        if self.dontauditxperm.isChecked():
            rule_types.append("dontauditxperm")
            max_results += self.policy.dontauditxperm_count
        if self.type_transition.isChecked():
            rule_types.append("type_transition")
            max_results += self.policy.type_transition_count
        if self.type_member.isChecked():
            rule_types.append("type_member")
            max_results += self.policy.type_member_count
        if self.type_change.isChecked():
            rule_types.append("type_change")
            max_results += self.policy.type_change_count

        self.query.ruletype = rule_types
        self.query.source_indirect = self.source_indirect.isChecked()
        self.query.target_indirect = self.target_indirect.isChecked()
        self.query.perms_subset = self.perms_subset.isChecked()
        self.query.boolean_equal = self.bools_equal.isChecked()

        # if query is broad, show warning.
        if not any((self.query.source, self.query.target, self.query.tclass, self.query.perms,
                    self.query.xperms, self.query.default, self.query.boolean)) \
                and max_results > 1000:

            reply = QMessageBox.question(
                self, "Continue?",
                "This is a broad query, estimated to return {0} results.  Continue?".
                format(max_results), QMessageBox.Yes | QMessageBox.No)

            if reply == QMessageBox.No:
                return

        # start processing
        self.busy.setLabelText("Processing query...")
        self.busy.show()
        self.raw_results.clear()
        self.thread.start()

    def update_complete(self, count):
        self.log.info("{0} type enforcement rule(s) found.".format(count))

        # update sizes/location of result displays
        if not self.busy.wasCanceled():
            self.busy.setLabelText("Resizing the result table's columns; GUI may be unresponsive")
            self.busy.repaint()
            self.table_results.resizeColumnsToContents()
            # If the permissions column width is too long, pull back
            # to a reasonable size
            header = self.table_results.horizontalHeader()
            if header.sectionSize(4) > 400:
                header.resizeSection(4, 400)

        if not self.busy.wasCanceled():
            self.busy.setLabelText("Resizing the result table's rows; GUI may be unresponsive")
            self.busy.repaint()
            self.table_results.resizeRowsToContents()

        if not self.busy.wasCanceled():
            self.busy.setLabelText("Moving the raw result to top; GUI may be unresponsive")
            self.busy.repaint()
            self.raw_results.moveCursor(QTextCursor.Start)

        self.busy.reset()
Пример #56
0
class MLSRuleQueryTab(SEToolsWidget, QScrollArea):
    """An MLS rule query."""
    def __init__(self, parent, policy, perm_map):
        super(MLSRuleQueryTab, self).__init__(parent)
        self.log = logging.getLogger(__name__)
        self.policy = policy
        self.query = MLSRuleQuery(policy)
        self.setupUi()

    def __del__(self):
        self.thread.quit()
        self.thread.wait(5000)
        logging.getLogger("setools.mlsrulequery").removeHandler(self.handler)

    def setupUi(self):
        self.load_ui("mlsrulequery.ui")

        # set up source/target autocompletion
        typeattr_completion_list = [str(t) for t in self.policy.types()]
        typeattr_completion_list.extend(
            str(a) for a in self.policy.typeattributes())
        typeattr_completer_model = QStringListModel(self)
        typeattr_completer_model.setStringList(
            sorted(typeattr_completion_list))
        self.typeattr_completion = QCompleter()
        self.typeattr_completion.setModel(typeattr_completer_model)
        self.source.setCompleter(self.typeattr_completion)
        self.target.setCompleter(self.typeattr_completion)

        # setup indications of errors on source/target/default
        self.orig_palette = self.source.palette()
        self.error_palette = self.source.palette()
        self.error_palette.setColor(QPalette.Base, Qt.red)
        self.clear_source_error()
        self.clear_target_error()
        self.clear_default_error()

        # populate class list
        self.class_model = SEToolsListModel(self)
        self.class_model.item_list = sorted(self.policy.classes())
        self.tclass.setModel(self.class_model)

        # set up results
        self.table_results_model = MLSRuleTableModel(self)
        self.sort_proxy = QSortFilterProxyModel(self)
        self.sort_proxy.setSourceModel(self.table_results_model)
        self.table_results.setModel(self.sort_proxy)
        self.table_results.sortByColumn(1, Qt.AscendingOrder)

        # set up processing thread
        self.thread = QThread()
        self.worker = QueryResultsUpdater(self.query, self.table_results_model)
        self.worker.moveToThread(self.thread)
        self.worker.raw_line.connect(self.raw_results.appendPlainText)
        self.worker.finished.connect(self.update_complete)
        self.worker.finished.connect(self.thread.quit)
        self.thread.started.connect(self.worker.update)

        # create a "busy, please wait" dialog
        self.busy = QProgressDialog(self)
        self.busy.setModal(True)
        self.busy.setRange(0, 0)
        self.busy.setMinimumDuration(0)
        self.busy.canceled.connect(self.thread.requestInterruption)
        self.busy.reset()

        # update busy dialog from query INFO logs
        self.handler = LogHandlerToSignal()
        self.handler.message.connect(self.busy.setLabelText)
        logging.getLogger("setools.mlsrulequery").addHandler(self.handler)

        # Ensure settings are consistent with the initial .ui state
        self.set_source_regex(self.source_regex.isChecked())
        self.set_target_regex(self.target_regex.isChecked())
        self.criteria_frame.setHidden(not self.criteria_expander.isChecked())
        self.notes.setHidden(not self.notes_expander.isChecked())

        # connect signals
        self.buttonBox.clicked.connect(self.run)
        self.clear_ruletypes.clicked.connect(self.clear_all_ruletypes)
        self.all_ruletypes.clicked.connect(self.set_all_ruletypes)
        self.source.textEdited.connect(self.clear_source_error)
        self.source.editingFinished.connect(self.set_source)
        self.source_regex.toggled.connect(self.set_source_regex)
        self.target.textEdited.connect(self.clear_target_error)
        self.target.editingFinished.connect(self.set_target)
        self.target_regex.toggled.connect(self.set_target_regex)
        self.tclass.selectionModel().selectionChanged.connect(self.set_tclass)
        self.invert_class.clicked.connect(self.invert_tclass_selection)
        self.default_range.textEdited.connect(self.clear_default_error)
        self.default_range.editingFinished.connect(self.set_default_range)

    #
    # Ruletype criteria
    #

    def _set_ruletypes(self, value):
        self.range_transition.setChecked(value)

    def set_all_ruletypes(self):
        self._set_ruletypes(True)

    def clear_all_ruletypes(self):
        self._set_ruletypes(False)

    #
    # Source criteria
    #

    def clear_source_error(self):
        self.source.setToolTip("Match the source type/attribute of the rule.")
        self.source.setPalette(self.orig_palette)

    def set_source(self):
        try:
            self.query.source = self.source.text()
        except Exception as ex:
            self.log.error("Source type/attribute error: {0}".format(ex))
            self.source.setToolTip("Error: {0}".format(ex))
            self.source.setPalette(self.error_palette)

    def set_source_regex(self, state):
        self.log.debug("Setting source_regex {0}".format(state))
        self.query.source_regex = state
        self.clear_source_error()
        self.set_source()

    #
    # Target criteria
    #

    def clear_target_error(self):
        self.target.setToolTip("Match the target type/attribute of the rule.")
        self.target.setPalette(self.orig_palette)

    def set_target(self):
        try:
            self.query.target = self.target.text()
        except Exception as ex:
            self.log.error("Target type/attribute error: {0}".format(ex))
            self.target.setToolTip("Error: {0}".format(ex))
            self.target.setPalette(self.error_palette)

    def set_target_regex(self, state):
        self.log.debug("Setting target_regex {0}".format(state))
        self.query.target_regex = state
        self.clear_target_error()
        self.set_target()

    #
    # Class criteria
    #

    def set_tclass(self):
        selected_classes = []
        for index in self.tclass.selectionModel().selectedIndexes():
            selected_classes.append(self.class_model.data(index, Qt.UserRole))

        self.query.tclass = selected_classes

    def invert_tclass_selection(self):
        invert_list_selection(self.tclass.selectionModel())

    #
    # Default criteria
    #

    def clear_default_error(self):
        self.default_range.setToolTip("Match the default type the rule.")
        self.default_range.setPalette(self.orig_palette)

    def set_default_range(self):
        try:
            self.query.default = self.default_range.text()
        except Exception as ex:
            self.log.error("Default range error: {0}".format(ex))
            self.default_range.setToolTip("Error: {0}".format(ex))
            self.default_range.setPalette(self.error_palette)

    #
    # Results runner
    #

    def run(self, button):
        # right now there is only one button.

        self.query.ruletype = ['range_transition']
        self.query.source_indirect = self.source_indirect.isChecked()
        self.query.target_indirect = self.target_indirect.isChecked()

        # start processing
        self.busy.setLabelText("Processing query...")
        self.busy.show()
        self.raw_results.clear()
        self.thread.start()

    def update_complete(self, count):
        self.log.info("{0} MLS rule(s) found.".format(count))

        # update sizes/location of result displays
        if not self.busy.wasCanceled():
            self.busy.setLabelText(
                "Resizing the result table's columns; GUI may be unresponsive")
            self.busy.repaint()
            self.table_results.resizeColumnsToContents()

        if not self.busy.wasCanceled():
            self.busy.setLabelText(
                "Resizing the result table's rows; GUI may be unresponsive")
            self.busy.repaint()
            self.table_results.resizeRowsToContents()

        if not self.busy.wasCanceled():
            self.busy.setLabelText(
                "Moving the raw result to top; GUI may be unresponsive")
            self.busy.repaint()
            self.raw_results.moveCursor(QTextCursor.Start)

        self.busy.reset()
Пример #57
0
 def start(self):
     QThread.start(self)
     StenoEngine.start(self)
Пример #58
0
class MainWindow(QMainWindow, Ui_MainWindow):
    """QT main window"""

    def __init__(self, *args, obj=None, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        # Logging
        self.log = logging.getLogger("SpecChecker Main")
        self.log.setLevel("NOTSET")
        self.log.info("Starting Main Application")

        # Configuration
        self.config = configparser.ConfigParser()
        self.load_configuration()
        self.load_logging_configuration()

        # GUI Code
        self.setupUi(self)
        self.progressBar.setValue(0)
        self.btnStart.pressed.connect(self.runAllTests)
        self.btnExit.pressed.connect(self.doExit)
        self.actionExit.triggered.connect(self.doExit)
        self.actionAbout.triggered.connect(self.showAbout)
        self.statusText = ""

        # Specifications Code
        self.specs = SpecRecord()
        self.audioInfo = {}
        self.list_gpus = []
        self.cpuInfo = {}
        self.networkInfo = {}
        self.hard_drive_list = []
        self.locationInfo = {}
        self.memoryInfo = {}
        self.systemInfo = {}
        self.webcamList = []

        self.timer = QTimer()
        self.speed_check_timeout = (1000 * 1)

    def finished(self, spec_record):
        self.btnStart.setEnabled(True)
        self.btnStart.setDisabled(False)
        self.btnStart.setText("Start")
        self.specs = spec_record

        # Setup email submission from configuration
        self.specs.email.client_name = self.txtName.text()
        self.specs.email.client_email_address = self.txtEmail.text()
        self.specs.email.fields = json.loads(self.config.get("email_submission", "fields"))
        self.specs.email.email_provider = self.config.get("email_submission", "email_provider")
        self.specs.email.send_address = self.config.get("email_submission", "send_address")
        self.specs.email.subject = self.config.get("email_submission", "subject")
        self.specs.email.template = self.config.get("email_submission", "template")
        self.specs.email.autoresponse = self.config.get("email_submission", "autoresponse")
        self.specs.email.cc_addresses = self.config.get("email_submission", "cc_addresses")
        self.specs.email.webhook = self.config.get("email_submission", "webhook")
        self.specs.email.url = self.config.get("email_submission", "api_url")
        self.log.error(f"url: {self.specs.email.url}")
        self.specs.email.submit(data=self.specs,
                                email_provider=self.specs.email.email_provider,
                                send_address=self.specs.email.send_address,
                                subject=self.specs.email.subject,
                                template=self.specs.email.template,
                                autoresponse=self.specs.email.autoresponse,
                                cc_addresses=self.specs.email.cc_addresses,
                                webhook=self.specs.email.webhook,
                                url=self.specs.email.url,
                                client_name=self.specs.email.client_name,
                                client_email_address=self.specs.email.client_email_address)
        # self.specs.write_to_file()

    def runAllTests(self):
        self.thread = QThread()
        self.worker = MainTestWorker(obj=self)
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.test)
        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.worker.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)
        self.worker.progress.connect(self.reportProgress)
        # self.worker.progress.connect(self.speed_progress)
        self.thread.start()
        self.timer.start(self.speed_check_timeout)
        self.timer.timeout.connect(self.check_stage)
        self.btnStart.setDisabled(True)
        self.btnStart.setEnabled(False)
        self.btnStart.setText("Running Tests...")
        self.worker.finished.connect(self.finished)
        self.thread.finished.connect(
            lambda: self.updateStatus("\n\n All Tests Complete!")
        )

    def check_stage(self):
        global speed_stage
        if speed_stage == 1:
            self.reportProgress(60, "dot")
        if speed_stage == 2:
            self.reportProgress(70, "dot")
        if speed_stage == 3:
            self.reportProgress(80, "dot")
        if speed_stage == 4:
            self.reportProgress(90, "dot")
        if speed_stage == 5:
            self.progressBar.setValue(100)
        if speed_stage <= 0 or speed_stage >= 6:
            self.reportProgress(-99, "dot")

    def reportProgress(self, percentage, module_name=None, first_pass=True):
        if not first_pass:
            self.updateStatus("Complete\n")
        if module_name is not None and module_name != "" and module_name != "dot":
            self.updateStatus(f"Scanning {module_name}..........")
        if module_name == "dot":
            self.updateStatus(".")

        if 100 >= percentage >= 0 and isinstance(percentage, int):
            self.progressBar.setValue(percentage)

    def showAbout(self):
        """Show the about box"""
        dlg = AboutBox()
        dlg.exec_()

    def doExit(self):
        """Exit the program cleanly"""
        self.log.info("Closing Application")
        self.close()

    def updateStatus(self, text):
        """Update the status text in the main text area of the app"""
        self.statusText = self.statusText + text
        self.txtStatus.setPlainText(self.statusText)

    def clearStatus(self):
        """Clear the status text in the main text area of the app"""
        self.statusText = ""
        self.txtStatus.setPlainText("")

    def load_configuration(self, filename='config.ini'):
        """Check if a configuration file exists. If it does, load it
        If not, load the default"""
        if path.exists(filename):
            self.config.read(filename)
            self.log.info(f"Loaded Configuration: {filename}")
        else:
            self.load_default_configuration()
            self.log.info("Configuration File Does Not Exist. Loading Defaults.")

    def load_default_configuration(self):
        """Load built in default configuration"""
        default_config = """
        [general]
        # Comment out lines that are unnecessary with hash
        save to file = False
        debug = True
        # Debug Levels: CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET (NOTSET turns off logging)
        debug level = INFO

        [email_submission]
        enabled = False
        # Not Yet Implemented
        
        [cpu]
        enabled = True
         
        [gpu]
        enabled = True
         
        [harddrive]
        enabled = True
        
        [location]
        enabled = True
        # options: ipinfo, 
        provider = ipinfo
        
        [memory]
        enabled = True
        
        [network]
        enabled = True
        
        [sound]
        enabled = True
        
        [speedtest]
        enabled = True
        # options: fast, 
        provider = fast
        
        [system]
        enabled = True
        """
        self.config.read_string(default_config)
        self.log.info("Loaded Default Configuration")

    def load_logging_configuration(self):
        """todo Sets Application Wide Log Level From Configuration"""
        log_flag = bool(self.config.get('general', 'debug'))
        log_level = self.config.get('general', 'debug level')
        if log_flag is False:
            log_level = "NOTSET"
Пример #59
0
class CommonQueryTab(AnalysisTab):

    """Common browser and query tab."""

    def __init__(self, parent, policy, perm_map):
        super(CommonQueryTab, self).__init__(parent)
        self.log = logging.getLogger(__name__)
        self.policy = policy
        self.query = CommonQuery(policy)
        self.setupUi()

    def __del__(self):
        self.thread.quit()
        self.thread.wait(5000)
        logging.getLogger("setools.commonquery").removeHandler(self.handler)

    def setupUi(self):
        self.load_ui("commonquery.ui")

        # populate commons list
        self.common_model = SEToolsListModel(self)
        self.common_model.item_list = sorted(c for c in self.policy.commons())
        self.commons.setModel(self.common_model)

        # populate perm list
        self.perms_model = SEToolsListModel(self)
        perms = set()
        for com in self.policy.commons():
            perms.update(com.perms)
        self.perms_model.item_list = sorted(perms)
        self.perms.setModel(self.perms_model)

        # set up results
        self.table_results_model = CommonTableModel(self)
        self.sort_proxy = QSortFilterProxyModel(self)
        self.sort_proxy.setSourceModel(self.table_results_model)
        self.table_results.setModel(self.sort_proxy)
        self.table_results.sortByColumn(0, Qt.AscendingOrder)

        # setup indications of errors
        self.errors = set()
        self.orig_palette = self.name.palette()
        self.error_palette = self.name.palette()
        self.error_palette.setColor(QPalette.Base, Qt.red)
        self.clear_name_error()

        # set up processing thread
        self.thread = QThread()
        self.worker = QueryResultsUpdater(self.query, self.table_results_model)
        self.worker.moveToThread(self.thread)
        self.worker.raw_line.connect(self.raw_results.appendPlainText)
        self.worker.finished.connect(self.update_complete)
        self.worker.finished.connect(self.thread.quit)
        self.thread.started.connect(self.worker.update)

        # create a "busy, please wait" dialog
        self.busy = QProgressDialog(self)
        self.busy.setModal(True)
        self.busy.setRange(0, 0)
        self.busy.setMinimumDuration(0)
        self.busy.canceled.connect(self.thread.requestInterruption)
        self.busy.reset()

        # update busy dialog from query INFO logs
        self.handler = LogHandlerToSignal()
        self.handler.message.connect(self.busy.setLabelText)
        logging.getLogger("setools.commonquery").addHandler(self.handler)

        # Ensure settings are consistent with the initial .ui state
        self.set_name_regex(self.name_regex.isChecked())
        self.notes.setHidden(not self.notes_expander.isChecked())

        # connect signals
        self.commons.doubleClicked.connect(self.get_detail)
        self.commons.get_detail.triggered.connect(self.get_detail)
        self.name.textEdited.connect(self.clear_name_error)
        self.name.editingFinished.connect(self.set_name)
        self.name_regex.toggled.connect(self.set_name_regex)
        self.perms.selectionModel().selectionChanged.connect(self.set_perms)
        self.invert_perms.clicked.connect(self.invert_perms_selection)
        self.buttonBox.clicked.connect(self.run)

    #
    # Class browser
    #
    def get_detail(self):
        # .ui is set for single item selection.
        index = self.commons.selectedIndexes()[0]
        item = self.common_model.data(index, Qt.UserRole)

        self.log.debug("Generating detail window for {0}".format(item))
        common_detail(self, item)

    #
    # Name criteria
    #
    def clear_name_error(self):
        self.clear_criteria_error(self.name, "Match the common name.")

    def set_name(self):
        try:
            self.query.name = self.name.text()
        except Exception as ex:
            self.log.error("Common name error: {0}".format(ex))
            self.set_criteria_error(self.name, ex)

    def set_name_regex(self, state):
        self.log.debug("Setting name_regex {0}".format(state))
        self.query.name_regex = state
        self.clear_name_error()
        self.set_name()

    #
    # Permissions criteria
    #
    def set_perms(self):
        selected_perms = []
        for index in self.perms.selectionModel().selectedIndexes():
            selected_perms.append(self.perms_model.data(index, Qt.UserRole))

        self.query.perms = selected_perms

    def invert_perms_selection(self):
        invert_list_selection(self.perms.selectionModel())

    #
    # Save/Load tab
    #
    def save(self):
        """Return a dictionary of settings."""
        if self.errors:
            raise TabFieldError("Field(s) are in error: {0}".
                                format(" ".join(o.objectName() for o in self.errors)))

        settings = {}
        save_checkboxes(self, settings, ["criteria_expander", "notes_expander", "name_regex",
                                         "perms_equal"])
        save_lineedits(self, settings, ["name"])
        save_listviews(self, settings, ["perms"])
        save_textedits(self, settings, ["notes"])
        return settings

    def load(self, settings):
        load_checkboxes(self, settings, ["criteria_expander", "notes_expander", "name_regex",
                                         "perms_equal"])
        load_lineedits(self, settings, ["name"])
        load_listviews(self, settings, ["perms"])
        load_textedits(self, settings, ["notes"])

    #
    # Results runner
    #
    def run(self, button):
        # right now there is only one button.
        self.query.perms_equal = self.perms_equal.isChecked()

        # start processing
        self.busy.setLabelText("Processing query...")
        self.busy.show()
        self.raw_results.clear()
        self.thread.start()

    def update_complete(self, count):
        self.log.info("{0} common(s) found.".format(count))

        # update sizes/location of result displays
        if not self.busy.wasCanceled():
            self.busy.setLabelText("Resizing the result table's columns; GUI may be unresponsive")
            self.busy.repaint()
            self.table_results.resizeColumnsToContents()
            # If the permissions column width is too long, pull back
            # to a reasonable size
            header = self.table_results.horizontalHeader()
            if header.sectionSize(1) > 400:
                header.resizeSection(1, 400)

        if not self.busy.wasCanceled():
            self.busy.setLabelText("Resizing the result table's rows; GUI may be unresponsive")
            self.busy.repaint()
            self.table_results.resizeRowsToContents()

        if not self.busy.wasCanceled():
            self.busy.setLabelText("Moving the raw result to top; GUI may be unresponsive")
            self.busy.repaint()
            self.raw_results.moveCursor(QTextCursor.Start)

        self.busy.reset()
Пример #60
0
class BatchExtractQWidget(QWidget):
    def __init__(self, mainwindow: QMainWindow):
        super().__init__()

        self.mainwindow = mainwindow
        self.thread = None
        self.init_ui()

    def init_ui(self):
        '''
        添加简历文件标签

        添加简历文件展示框

        添加选择文件按钮

        添加保存路径标签
        添加保存路径展示框
        添加修改保存路径窗口
        '''
        #添加组件并绑定事件
        srcresume_label = QLabel('简历文件')

        self.srcresume_edit = QTextEdit()
        self.srcresume_edit.setReadOnly(True)

        resume_choose_button = QPushButton('选择简历文件')
        resume_choose_button.clicked.connect(self._choose_extract_file)

        save_label = QLabel('处理好的简历文件保存路径:')
        self.save_edit = QLineEdit()
        self.save_edit.setReadOnly(True)

        change_save_button = QPushButton('选择保存路径')
        change_save_button.clicked.connect(self._choose_save_dic)

        batch_extract_button = QPushButton('   批量抽取    ')
        batch_extract_button.clicked.connect(self._batch_extract)

        #设置布局
        hbox = QHBoxLayout()
        hbox.addStretch(10)
        hbox.addWidget(resume_choose_button)

        hbox1 = QHBoxLayout()
        hbox1.addWidget(save_label)
        hbox1.addWidget(self.save_edit)
        hbox1.addWidget(change_save_button)

        hbox2 = QHBoxLayout()
        hbox2.addStretch(10)
        hbox2.addWidget(batch_extract_button)

        vbox = QVBoxLayout()
        vbox.addWidget(srcresume_label)
        vbox.addWidget(self.srcresume_edit)
        vbox.addLayout(hbox)
        vbox.addLayout(hbox1)
        vbox.addLayout(hbox2)

        self.setLayout(vbox)

        #设置主窗体属性
        self.mainwindow.setMinimumSize(700, 700)
        self.mainwindow.resize(700, 700)
        self.mainwindow.setWindowTitle('处理批量简历界面')
        self.mainwindow.center()
        #显示组件
        self.show()

    #选择要抽取的文件
    def _choose_extract_file(self):
        #选择文件
        file_names = QFileDialog.getOpenFileNames(self, 'Open Files',
                                                  config.TESTDATA_DIC)
        #处理文件名
        file_name_list = [file_name for file_name in file_names[0]]
        text = '\n'.join(file_name_list)
        #显示到srcresume_edit上面
        self.srcresume_edit.setText(text)

    #选择要保存的文件路径
    def _choose_save_dic(self):
        #选择文件夹
        dic_name = QFileDialog.getExistingDirectory(self, 'Change Save Dic',
                                                    config.RESULTDATA_DIC)
        #显示到save_edit上面
        self.save_edit.setText(str(dic_name))

    #批量抽取简历信息
    def _batch_extract(self):
        if self.thread != None and self.thread.isRunning():
            QMessageBox.about(self, 'message', '正在处理简历中,请稍等!')
            return

        file_names = str(self.srcresume_edit.toPlainText())
        save_dic = str(self.save_edit.text())

        if file_names == '' and save_dic == '':  #都没有内容
            QMessageBox.about(self, 'message', '请选择要处理的简历文件和保存路径!')

        elif file_names == '':
            QMessageBox.about(self, 'message', '请选择处理的简历文件!')

        elif save_dic == '':
            QMessageBox.about(self, 'message', '请选择保存路径!')

        elif file_names != '' and save_dic != '':
            #将所有文件发送给服务器端
            self.thread = QThread()
            self.object_client = ClientObject(
                resume_filepath_list=file_names.split('\n'), save_dic=save_dic)

            self.object_client.batch_extract_finished.connect(
                self._batch_extract_finish)
            self.object_client.moveToThread(self.thread)

            self.thread.started.connect(self.object_client.batch_extract)
            self.thread.start()
            QMessageBox.about(self, 'message', '正在处理简历中,请稍等!')
            pass

    #显示弹出框信息
    def _batch_extract_finish(self):
        QMessageBox.about(self, 'message', '处理批量简历完成!')
        self.thread.quit()

    pass  #BatchExtractQWidget class end