예제 #1
0
class Example(QObject):
    def __init__(self, parent=None):
        super(self.__class__, self).__init__(parent)

        self.gui = Window()

        self.worker = Worker()  # 백그라운드에서 돌아갈 인스턴스 소환
        self.worker_thread = QThread()  # 따로 돌아갈 thread를 하나 생성
        self.worker.moveToThread(self.worker_thread)  # worker를 만들어둔 쓰레드에 넣어줍니다
        self.worker_thread.start()  # 쓰레드를 실행합니다.

        self._connectSignals()  # 시그널을 연결하기 위한 함수를 호출
        self.gui.show()

    # 시그널을 연결하기 위한 func.
    def _connectSignals(self):
        # gui의 버튼을 클릭 시 연결설정
        self.gui.button_start.clicked.connect(self.worker.startWork)
        # worker에서 발생한 signal(sig_numbers) 의 연결 설정
        self.worker.sig_numbers.connect(self.gui.updateStatus)
        # cancel 버튼 연결 설정
        self.gui.button_cancel.clicked.connect(self.forceWorkerReset)

    # 쓰레드의 loop를 중단하고 다시 처음으로 대기 시키는 func.
    def forceWorkerReset(self):
        if self.worker_thread.isRunning():  # 쓰레드가 돌아가고 있다면
            self.worker_thread.terminate()  # 현재 돌아가는 thread를 중지시킨다
            self.worker_thread.wait()  # 새롭게 thread를 대기한 후
            self.worker_thread.start()  # 다시 처음부터 시작
class MainWin(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self,parent)
        self.window = uic.loadUi(main_path,self) # 메인창 만들기, 불러오기 self는 왜잇냐
        self.window.show()
        # 쓰레드 생성 및 연결
        self.worker = Worker() # back 에서 구동될 쓰레드
        self.threadObj = QThread() # 쓰레드 객체 생성
        self.worker.moveToThread(self.threadObj) # 가동할 쓰레드 - 쓰레드 객체를 연결
        self.connectionSignal()  # signal 연결 작업
        self.threadObj.start()  # 쓰레드 가동
    

    #쓰레드 중단 버튼을 누르면 호출된다. ?
    def connectionSignal(self):
        # 쓰레드 가동 버튼을 누르면 쓰레드가 시작.
        self.window.pushButton.clicked.connect(self.worker.start)
        # 쓰레드가 돌면서 이벤트를 발생하는데 받을 콜백함수 연결
        self.worker.signal_obj.connect(self.window.recvCnt)# 서로 다른 클래스 끼리 연결
        # 이벤트 이름이 없다.
    #쓰레드가 보내는 숫자값 받는 콜백함수

    #여기는 두개가 있다. signal이 (시작, 종료)
    @pyqtSlot(int)
    def recvCnt(self, cnt):
        self.window.progressBar.setValue(cnt)  # 받아서 화면을 바꿔준다.

    @pyqtSlot()
    def onThreadStop(self):# 이미 연결시켜놓음(디자이너에서)
        if self.threadObj.isRunning():
            self.threadObj.terminate()
예제 #3
0
class ModDownloaderWindow(QWidget):
    def __init__(self, file: CurseFile, curse: CurseAPI, instance, initmods):
        super().__init__()

        self.initmods = initmods

        self.setWindowTitle(translate("downloading.mod").format(file.name))

        self.layout = QVBoxLayout(self)

        self.progress = QProgressBar()
        self.layout.addWidget(self.progress)

        self.show()

        self.downloader = ModDownloaderThread(file, curse, instance)
        self.downloader.done.connect(self.download_done)
        self.downloader.update.connect(self.progress.setValue)

        self.download_thread = QThread()

        self.downloader.moveToThread(self.download_thread)

        self.download_thread.started.connect(self.downloader.download)
        self.download_thread.start()

    def download_done(self):
        self.downloader.terminate()
        self.download_thread.terminate()
        self.initmods()
        self.close()
        self.destroy()
예제 #4
0
파일: d.py 프로젝트: huxx-j/untitled
class Example(QObject):
    def __init__(self, parent=None):
        super(self.__class__, self).__init__(parent)

        self.gui = Window()
        self.worker = Worker()  # 백그라운드에서 돌아갈 인스턴스 소환
        self.worker_thread = QThread()  # 따로 돌아갈 thread를 하나 생성
        self.worker.moveToThread(self.worker_thread)  # worker를 만들어둔 쓰레드에 넣어줍니다
        self.worker_thread.start()  # 쓰레드를 실행합니다.

        self._connectSignals(
        )  # 시그널을 연결하기 위한 함수를 호출[출처] [Python3] PyQt5 | QThread-pyqtSignal/Slot Example|작성자 우리동네약사

        self.gui.show()

    def _connectSignals(self):
        # // gui 의 버튼을 클릭시 연결설정
        self.gui.button_start.clicked.connect(self.worker.startWork)
        # // worker에서 발생한 signal(sig_numbers)의 연결 설정
        self.worker.sig_numbers.connect(self.gui.updateStatus)
        # // cancel 버튼 연결 설정
        self.gui.button_cancel.clicked.connect(self.forceWorkerReset)

    # // 쓰레드의 loop를 중단하고 다시 처음으로 대기 시키는 func.
    def forceWorkerReset(self):
        if self.worker_thread.isRunning():
            self.worker_thread.terminate()
            self.worker_thread.wait()
            self.worker_thread.start()
예제 #5
0
 def mp_thread_callback(self, thread: QThread, ret):
     if isinstance(ret, Exception):
         print(ret)
         self.show_critical_error('Error contacting central market')
     thread.wait(2000)
     if thread.isRunning():
         thread.terminate()
     self.mp_threads.remove(thread)
예제 #6
0
class DownloadDialog(QDialog, Ui_DownloadDialog):
    def __init__(self, url, filePath, threadno, totalSize, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.setWindowTitle(os.path.split(filePath)[1])
        self.url = url
        self.filePath = filePath
        self.threadno = threadno
        self.totalSize = totalSize
        self.downloadTask = None
        self.workerThread = QThread(self)
        self.workerThread.started.connect(self.notifyStarted)
        self.workerThread.finished.connect(self.notifyFinished)
        self.mStopPushButton.clicked.connect(self.stopProcess)
        self.mCancelPushButton.clicked.connect(self.cancelProcess)
        self.toggleDownloadTask()

    @pyqtSlot(int)
    def setProgress(self, v):
        self.mProgressBar.setValue(v)
        if v == self.totalSize:
            self.workerThread.quit()
            self.workerThread.wait()

    def toggleDownloadTask(self):
        self.mProgressBar.setMaximum(self.totalSize)
        self.downloadTask = DownloadTask(self.url, self.filePath, self.totalSize, self.threadno)
        self.downloadTask.progressed.connect(self.setProgress)
        self.workerThread.started.connect(self.downloadTask.startDownload)
        self.workerThread.finished.connect(self.downloadTask.deleteLater)
        self.downloadTask.moveToThread(self.workerThread)
        self.workerThread.start()

    @pyqtSlot()
    def stopProcess(self):
        logger.info("stop task of downloading {} finished.".format(self.url))
        if self.workerThread.isRunning():
            self.workerThread.terminate()
            self.workerThread.wait()
            self.mStopPushButton.setText("Start")
        else:
            self.mStopPushButton.setText("Stop")

    @pyqtSlot()
    def cancelProcess(self):
        logger.info("cancel task of downloading {} finished.".format(self.url))
        if self.workerThread.isRunning():
            self.workerThread.terminate()
            self.workerThread.wait()

    @pyqtSlot()
    def notifyStarted(self):
        pass

    @pyqtSlot()
    def notifyFinished(self):
        logger.info("finished download {}".format(self.url))
예제 #7
0
class AppDialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(AppDialog, self).__init__(parent)
        self.setWindowTitle("PyQt5 -- QThread app")

        ui_file = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                               'qthread.ui')
        self.ui_widget = uic.loadUi(ui_file, self)
        self.ui_widget.buttonBox.accepted.connect(self.on_accept)
        self.ui_widget.buttonBox.rejected.connect(self.on_reject)

        self.ui_widget.pushButton_start.clicked.connect(self.on_click_start)
        self.ui_widget.pushButton_stop.clicked.connect(self.on_click_stop)

        self.ui_widget.progressBar.setValue(0)
        print("")

        self.thread_qt = QThread()

    def on_update_from_helper(self, tick):
        self.ui_widget.progressBar.setValue(tick)

    def on_result_from_helper(self, tick):
        self.ui_widget.progressBar.setValue(tick)
        print("Thread is completed")

    @pyqtSlot()
    def on_click_start(self):
        if self.thread_qt.isRunning():
            print("Thread is already running")
        else:
            print("Kicked off thread")
            self.helper_impl = helperThreadImpl(min=0, max=100)
            self.thread_qt.started.connect(self.helper_impl.start)
            self.helper_impl.sig_update.connect(self.on_update_from_helper)
            self.helper_impl.sig_result.connect(self.on_result_from_helper)
            self.helper_impl.moveToThread(self.thread_qt)
            self.thread_qt.start()

    @pyqtSlot()
    def on_click_stop(self):
        if self.thread_qt.isRunning():
            print("Ask thread to temrinate...")
            self.thread_qt.terminate()

            # It takes time, as per docs it depends on underlying OS
            self.thread_qt.wait()
            print("Thread is terminated")
        else:
            print("Thread is not active")

    def on_accept(self):
        print("on_accept")

    def on_reject(self):
        print("on_reject")
예제 #8
0
 def train_start(self, signal):
     print("Your computer's Ideal Thread Number :",
           QThread.idealThreadCount())
     if signal is 'start':
         self.thread = Worker(self.parent, 1)
         self.thread.start()  # run()
     else:
         QThread.terminate()
         self.thread.terminate()
         self.thread.quit()
class CameraSync(QObject):
    def __init__(self):
        super().__init__()
        self.shutdown = False
        self.items = dict()
        self.lock = threading.Lock()
        self.thread = QThread()
        self.moveToThread(self.thread)
        self.thread.started.connect(self.run)
        self.thread.start()

    def __del__(self):
        self.shutdown = True
        if not self.thread.wait(100):
            self.thread.terminate()
        self.thread = None

    def add(self, id, obj):
        with self.lock:
            self.items[id] = CameraStreamObject(id, obj)

    def remove(self, id):
        with self.lock:
            if id in self.items:
                obj = self.items.pop(id)
                obj.closeStream()

    def record(self, id):
        with self.lock:
            if id in self.items:
                self.items[id].rec = not self.items[id].rec

    def refresh(self, id):
        with self.lock:
            if id in self.items:
                self.items[id].refresh = True

    def run(self):
        global THREADING_SHUTDOWN, THREADING_EVENTS, THREADING_SUSPEND_TIME
        frames = dict()
        while not THREADING_SHUTDOWN and not self.shutdown:
            with self.lock:
                for id, obj in self.items.items():
                    frame = obj.render()
                    if frame:
                        frames[id] = frame

            # Send pixmap to ui thread, suspend otherwise, if nothing else to do!
            THREADING_EVENTS.dispatchPixmapEvent(frames.copy(
            )) if len(frames) > 0 else time.sleep(THREADING_SUSPEND_TIME)
            frames.clear()
        self.items.clear()
예제 #10
0
 def run(self):
     if self._status == 1:
         self.train_thread = QThread.currentThreadId()
         self.parent.train_widget.train_command()
     elif self._status == 2:
         self.parent.train_widget.show_tensorboard()
     elif self._status == 3:
         self.parent.train_widget.start_chrome()
     elif self._status == 4:
         self.parent.show_widget.view_loss()
     elif self._status == 5:
         self.parent.validation_widget.run_val()
     else:
         print('terminate')
         QThread.terminate()
예제 #11
0
class Gui(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):

        # Buttons:
        self.btn_start = QPushButton('Start')
        self.btn_start.resize(self.btn_start.sizeHint())
        self.btn_start.move(50, 50)
        self.btn_stop = QPushButton('Stop')
        self.btn_stop.resize(self.btn_stop.sizeHint())
        self.btn_stop.move(150, 50)

        # GUI title, size, etc...
        self.setGeometry(300, 300, 300, 220)
        self.setWindowTitle('ThreadTest')
        self.layout = QGridLayout()
        self.layout.addWidget(self.btn_start, 0, 0)
        self.layout.addWidget(self.btn_stop, 0, 50)
        self.setLayout(self.layout)

        # Thread:
        self.thread = QThread()
        self.worker = Worker()
        self.worker.moveToThread(self.thread)

        self.thread.started.connect(
            self.worker.do_work)  # when thread starts, start worker
        self.thread.finished.connect(
            self.worker.stop)  # when thread finishes, stop worker

        # Start Button action:
        self.btn_start.clicked.connect(self.thread.start)

        # Stop Button action:
        self.btn_stop.clicked.connect(self.stop_thread)

        self.show()

    # When stop_btn is clicked this runs. Terminates the worker and the thread.
    def stop_thread(self):
        print("It should stop printing numbers now and not crash")
        self.worker.disconnect()
        self.thread.terminate()
예제 #12
0
class MicrophoneListener(QObject):

    IN = pyqtSignal(object)
    OUT = pyqtSignal()

    @pyqtSlot(object)
    def receive_data(self, data):
        logger.info("{} received data of size {}".format(
            self, data.shape))

    def start(self):
        self._thread = QThread(self)
        self.moveToThread(self._thread)
        self._thread.start()
        self.IN.connect(self.receive_data)

    def stop(self):
        self._thread.terminate()
예제 #13
0
class PackDownloaderWindow(QWidget):
    def __init__(self, file: CurseFile, curse: CurseAPI, pack: CurseModpack):
        super().__init__()

        self.setWindowTitle(
            translate("downloading.pack").format(pack.project.title))

        self.layout = QVBoxLayout(self)

        self.label = QLabel()
        self.layout.addWidget(self.label)

        self.progress = QProgressBar()
        self.layout.addWidget(self.progress)

        self.prog2 = QProgressBar()
        self.layout.addWidget(self.prog2)

        self.show()

        self.downloader = PackDownloaderThread(file, curse, pack)
        self.downloader.done.connect(self.download_done)
        self.downloader.bar1.connect(self.progress.setValue)
        self.downloader.bar2.connect(self.prog2.setValue)
        self.downloader.setLabel.connect(self.label.setText)

        self.download_thread = QThread()

        self.downloader.moveToThread(self.download_thread)

        self.download_thread.started.connect(self.downloader.download)
        self.download_thread.start()

    def download_done(self):
        self.downloader.terminate()
        self.download_thread.terminate()
        self.close()
        self.destroy()
예제 #14
0
    def cmdFSUpdateMP_callback(self, thread: QThread, ret):
        model = self.enh_model
        frmMain = self.frmMain

        idx_NAME = self.HEADERS.index(HEADER_NAME)

        if isinstance(ret, Exception):
            print(ret)
            frmMain.sig_show_message.emit(frmMain.CRITICAL,
                                          'Error contacting central market')
        else:
            settings = model.settings
            item_store: ItemStore = settings[settings.P_ITEM_STORE]
            with QBlockSig(self):
                for i in range(self.rowCount()):
                    this_gear: Gear = self.cellWidget(i, idx_NAME).gear
                    self.fs_gear_set_costs(this_gear, item_store, i)
            frmMain.sig_show_message.emit(frmMain.REGULAR,
                                          'Fail stacking prices updated')
        thread.wait(2000)
        if thread.isRunning():
            thread.terminate()
        self.mp_threads.remove(thread)
예제 #15
0
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__()

        self.initUi()

    def initUi(self):
        pg.setConfigOption('background', 'w')  # before loading widget
        self.setupUi(self)
        self.setWindowTitle("GIAS Recorder")
        self.setWindowIcon(QtGui.QIcon(appctxt.get_resource('GIAS_icon.png')))
        self.StartButton.clicked.connect(self.start)
        self.StartButton.setIcon(QtGui.QIcon(appctxt.get_resource('Rec_button.png')))
        self.SettingsButton.clicked.connect(self.open_settings)
        self.StopButton.clicked.connect(self.stop)
        self.progressBar.setMaximum(32768)
        pg.setConfigOptions(antialias=True)
        self.rec_settings = Settings()
        self.BIPRange = 0
        self.srate = 500000
        self.CHUNKSZ = 1024  # int(self.srate/1000)
        self.plot_iter = 0
        self.setupThreads()
        self.open_settings()
        self.showMaximized()
        # app.aboutToQuit.connect(self.forceWorkerQuit)

    def open_settings(self):
        self.rec_settings.exec_()
        self.BIPRange = self.rec_settings.Range.currentIndex()
        self.srate = int(self.rec_settings.SampleRate.currentText())*10**3
        self.CHUNKSZ = 1024   # int(self.srate/1000)
        self.set_plot_props()
        self.forceWorkerReset()

    def set_plot_props(self):
        # TODO: cehck for vispy module

        # Time Plot Properties
        # self.win = self.graphicsView
        ############## test 1 PLotWidget ###############################################
        # self.win.plotItem.setRange(
        #     xRange=[0, self.CHUNKSZ], yRange=[-35000, 35000], padding=0,
        #     disableAutoRange=True)
        # self.win.setMouseEnabled(x=True, y=False)
        # self.win.showGrid(True, True, 0.5)
        # self.win.setLabel('bottom', 'Time', units='s')
        # self.win.setLabel('left', 'Amplitude', units='counts')
        ############## test 2 GraphicsLayoutWidget ####################################
        # self.win.clear()
        # self.data1 = np.zeros(2*self.CHUNKSZ)
        # self.plot_index = 0
        # self.p1 = self.win.addPlot(row=0, col=1)
        # self.p1.setDownsampling(mode='peak')
        # self.p1.setClipToView(True)
        # self.p1.setLimits(xMin=0, xMax=2*self.CHUNKSZ, yMin=-32768, yMax=32767)
        # self.p1.disableAutoRange()
        # self.p1.setXRange(0, 2*self.CHUNKSZ)
        # self.p1.setYRange(-32768, 32767)
        # self.curve1 = self.p1.plot(self.data1, pen='b')
        # self.win.addLabel('Time [s]', row=1, col=1)
        # self.win.addLabel('Amplitud [counts]', row=0, col=0, angle=-90)
        ############## test 3 GraphicsLayoutWidget ####################################
        # self.win.clear()
        # self.max_chunks = 10
        # self.p1 = self.win.addPlot()
        # self.p1.setDownsampling(mode='peak')
        # self.p1.disableAutoRange()
        # self.p1.setXRange(-self.CHUNKSZ, 0)
        # self.p1.setYRange(-32768, 32767)
        # self.curves = []
        # self.ptr = 0
        # self.data1 = np.empty((self.CHUNKSZ+1, 2))
        # self.win.addLabel('Time [s]', row=1, col=1)
        # self.win.addLabel('Amplitud [counts]', row=0, col=0, angle=-90)
        ############## test 4 PlotWidget (arrayToQPath) ################################
        # self.win.clear()  # .plotItem
        # self.win.setMouseEnabled(x=True, y=False)
        # self.win.showGrid(True, True, 0.5)
        # self.win.setLabel('bottom', 'Time', units='s')
        # self.win.setLabel('left', 'Amplitude', units='counts')
        # self.ptr = 0
        # self.n = 5
        # self.win.setRange(
        #     xRange=[0, self.CHUNKSZ*self.n], yRange=[-35000, 35000], padding=0,
        #     disableAutoRange=True)
        # self.win.enableAutoRange(False, False)
        # self.x = np.arange(self.n*self.CHUNKSZ)
        # self.ydata = np.zeros((self.n*self.CHUNKSZ))
        # self.conn = np.ones((self.n*self.CHUNKSZ))
        # self.item = QtGui.QGraphicsPathItem()
        # self.item.setPen(pg.mkPen('b', width=2, style=QtCore.Qt.SolidLine))
        # self.win.addItem(self.item)

        #####################################################################################
        # Spectrogram Properties

        self.win2 = self.graphicsView_2
        self.win2.clear()
        self.win2.enableAutoRange(False, False)
        Xffts = 1024  # Number of ffts along X axis
        window_len = self.CHUNKSZ/self.srate  # Time window length
        spect_len = window_len * Xffts  # Spectrogram length

        self.img_array = np.zeros((Xffts, int(self.CHUNKSZ/2)+1))
        self.win2.plotItem.setRange(
            xRange=[0, spect_len-2*window_len], yRange=[0, self.srate/2+100], padding=0,
            disableAutoRange=True)
        self.win2.setMouseEnabled(x=True, y=True)
        self.win2.setLabel('left', 'Frequency', units='Hz')
        self.win2.setLabel('bottom', 'Time', units='s')
        # ax = self.win2.getAxis('bottom')
        # ax.setTicks([
        #     [],
        #     [], ])

        self.win2.setLimits(minXRange=0, maxXRange=spect_len,
                            minYRange=0, maxYRange=self.srate/2+100, xMin=0, xMax=spect_len,
                            yMin=0, yMax=self.srate/2+100)
        self.img = pg.ImageItem()
        self.img.setAutoDownsample(True)
        self.win2.addItem(self.img)

        # self.img_array = np.zeros((1000, int(self.CHUNKSZ/2)+1))
        self.img_index = 2
        # bipolar colormap

        pos = np.array([0., 1., 0.5, 0.25, 0.75])
        color = np.array([[0, 255, 255, 255], [255, 255, 0, 255], [0, 0, 0, 255],
                          (0, 0, 255, 255), (255, 0, 0, 255)], dtype=np.ubyte)
        cmap = pg.ColorMap(pos, color)
        lut = cmap.getLookupTable(0.0, 1.0, 256)

        self.img.setLookupTable(lut)
        self.img.setLevels([-50, 40])

        # freq = np.arange((256/2)+1)/(float(256)/self.srate)
        # yscale = 1.0/(self.img_array.shape[1]/freq[-1])
        # self.img.scale((1./self.srate)*256, yscale)
        freq = np.arange((self.CHUNKSZ/2)+1)/(float(self.CHUNKSZ)/self.srate)
        yscale = 1.0/(self.img_array.shape[1]/freq[-1])
        self.img.scale((1./self.srate)*self.CHUNKSZ, yscale)

        self.hann = np.hanning(self.CHUNKSZ)
        # self.empty_plot()  # UNCOMMENT DESPUES DE TESTEO TIME PLOT
################################### test 1 s####################################
        # # Add a histogram with which to control the gradient of the image
        # hist = pg.HistogramLUTItem()
        # # Link the histogram to the image
        # hist.setImageItem(self.img)
        # # If you don't add the histogram to the window, it stays invisible, but I find it useful.
        # self.win2.addItem(hist)
        # # Show the window
        # self.win2.show()
        #
        # # This gradient is roughly comparable to the gradient used by Matplotlib
        # # You can adjust it and then save it using hist.gradient.saveState()
        # hist.gradient.restoreState(
        #     {'mode': 'rgb',
        #      'ticks': [(0.5, (0, 182, 188, 255)),
        #                (1.0, (246, 111, 0, 255)),
        #                (0.0, (75, 0, 113, 255))]})

        # # Fit the min and max levels of the histogram to the data available
        # hist.setLevels(np.min(Sxx), np.max(Sxx))
################################################################################
    # Plot allocation

    def empty_plot(self):
        # self.timedata = np.zeros(self.CHUNKSZ)
        # self.x = np.arange(0, self.CHUNKSZ, 1)
        # self.set_plot_data(self.x, self.timedata)
        # self.data1 = np.zeros(5*self.CHUNKSZ)
        # self.curve1 = self.p1.plot(self.data1, pen='b')
        self.clipBar.setValue(0)

        self.image_index = 2

    def setupThreads(self):
        # Data Aqcuisition Thread
        self.daq_thread = QThread()
        self.daq_worker = DaqThread(self.BIPRange, self.srate)
        self.daq_worker.moveToThread(self.daq_thread)
        self.daq_thread.start()
        # conexiones Thread -> Main
        self.daq_worker.chunk_signal.connect(self.update)
        # conexiones Main -> Thread
        self.StartButton.clicked.connect(self.daq_worker.start_scan)
        # self.StopButton.clicked.connect(self.daq_worker.stop_daq_thread) NOT WORKING
        self.daq_worker.finished_signal.connect(self.forceWorkerReset)
        # self.daq_thread.finished.connect(self.worker.deleteLater)
        # # Playback Thread
        # self.play_thread = QThread()          UNCOMMENT PARA PLAY HASTA FINAL
        # self.play_worker = PlayThread(self.srate, self.CHUNKSZ)
        # self.play_worker.moveToThread(self.play_thread)
        # self.play_thread.start()
        # # Play thread signals
        # self.StartButton.clicked.connect(self.play_worker.open_stream)
        # self.StopButton.clicked.connect(self.play_worker.end_stream)
        # self.daq_worker.chunk_signal.connect(self.play_worker.get_signal)

    def stop(self):
        self.toggle_buttons()
        self.daq_worker.stop_daq_thread()
        self.empty_plot()  # UNCOMMENT DESPUES DE TESTEO PLOT
        # self.play_worker.end_stream() UNCOMMENT PARA PLAY

    def toggle_buttons(self):
        self.StartButton.setEnabled(True)
        self.StopButton.setEnabled(False)
        self.SettingsButton.setEnabled(True)
        self.filenameLabel.setText('Status: Idle')

    @pyqtSlot()
    def forceWorkerReset(self):
        if self.daq_thread.isRunning():
            # print('Quitting Thread')
            # self.daq_thread.quit

            # print('Terminating thread.')
            self.daq_thread.terminate()

            # print('Waiting for thread termination.')
            self.daq_thread.wait()

            self.toggle_buttons()

        # print('building new working object.')
        self.setupThreads()

    def forceWorkerQuit(self):
        if self.daq_thread.isRunning():
            self.daq_thread.terminate()
            self.daq_thread.wait()

        # if self.play_thread.isRunning(): UNCOMMENT PARA PLAY
        #     self.play_thread.terminate()
        #     self.play_thread.wait()

    def start(self):
        self.StopButton.setEnabled(True)
        self.StartButton.setEnabled(False)
        self.SettingsButton.setEnabled(False)
        # if self.rec_settings.FolderLabel.text() == '': # and monitor not selected
        #     QtGui.QMessageBox.critical(self, "No Destination Folder", "Please select settings")
        #     #algun comando para que no avance el programa
        self.filenameLabel.setText('Status: Recording to ' + self.rec_settings.FolderLabel.text() +
                                   '/' + self.rec_settings.NamePrefixLabel.text() + '...')
        self.startTime = pg.ptime.time()

    def set_plot_data(self, x, y):
        self.win.plot(x, y, pen=pg.mkPen('b', style=QtCore.Qt.SolidLine), clear=True)

    @QtCore.pyqtSlot(np.ndarray)
    def update(self, chunk):
        self.chunk1 = copy(chunk)
        # # time series rolling array
        # now = pg.ptime.time()
        # self.timedata = np.roll(self.timedata, -len(self.chunk1))
        # self.timedata[-len(self.chunk1):] = self.chunk1
        # self.set_plot_data(self.x, self.timedata)
        # print("Plot time: %0.4f sec" % (pg.ptime.time()-now))
############## test 1 #########################################################
        # self.data1[:len(self.chunk1)] = self.data1[-len(self.chunk1):]
        # self.data1[-len(self.chunk1):] = self.chunk1  # shift data in the array one sample left
        # self.curve1.setData(self.data1)
        # print("Plot time: %0.4f sec" % (pg.ptime.time()-now))
############## test 2 #########################################################
        # if self.plot_index == 20*self.CHUNKSZ:
        #     self.plot_index = 0
        # self.data1[self.plot_index:self.plot_index+self.CHUNKSZ] = self.chunk1
        # self.curve1.setData(self.data1)
        # self.plot_index += self.CHUNKSZ
        # print("Plot time: %0.4f sec" % (pg.ptime.time()-now))
############## test 3 #########################################################
        # now = pg.ptime.time()
        # for c in self.curves:
        #     c.setPos(-(self.CHUNKSZ), 0)
        #
        # curve = self.p1.plot(pen='b')
        # self.curves.append(curve)
        # print('len curve: ', len(self.curves))
        # if len(self.curves) > 2:  # > self.max_chunks
        #     c = self.curves.pop(0)
        #     self.p1.removeItem(c)
        #     print('len curve post pop: ', len(self.curves))
        # curve.setData(x=np.arange(0, self.CHUNKSZ), y=self.chunk1)
        # print("Plot time: %0.4f sec" % (pg.ptime.time()-now))

############## test 4 (arrayToPyqtGraph) ######################################
        # now = pg.ptime.time()
        # if self.ptr == self.n*self.CHUNKSZ:
        #     self.ptr = 0
        #     self.ydata.fill(0)
        # self.ydata[self.ptr:self.ptr+self.CHUNKSZ] = self.chunk1
        # self.ptr += self.CHUNKSZ
        # # if self.ptr == int(self.n/2):
        # path = pg.arrayToQPath(self.x, self.ydata, self.conn)
        # self.item.setPath(path)
        # print("Plot time: %0.4f sec" % (pg.ptime.time()-now))
############### test 5 (resampling chunk test) ################################

######################################################################
        # Spectrogram
        # normalized, windowed frequencies in data chunk
        spec = np.fft.rfft(self.chunk1*self.hann) / self.CHUNKSZ  # add ,n=512/128/256
        # spec = np.fft.rfft(self.chunk1*self.hann, n=256) / self.CHUNKSZ  # add ,n=512/128/256

        # get magnitude
        psd = abs(spec)

        # # nivel del vúmetro
        chunk_abs = np.abs(self.chunk1)
        # avg_vumeter = np.average(chunk_abs)
        peak_val = np.max(chunk_abs)
        self.plot_iter += 1
        if self.plot_iter == 50:
            self.progressBar.setValue(peak_val)
            self.plot_iter = 0

        # Clip indicator
        if peak_val > 32760 and self.plot_iter > 5:
            self.clipBar.setValue(1)
        # convert to dB scale
        try:
            psd = 20 * np.log10(psd)
        except:
            psd[psd == 0] = np.amin(psd[psd != 0])
            psd = 20 * np.log10(psd)  # parche momentaneo

        # # roll down one and replace leading edge with new data
        # self.img_array = np.roll(self.img_array, -1, 0)
        # self.img_array[-1:] = psd

        # roll down one and replace leading edge with new data
        if self.img_index == len(self.img_array):
            self.img_index = 2

        self.img_array[self.img_index-2] = psd
        self.img_array[self.img_index-1:self.img_index+1] = 10*np.ones(len(psd))
        self.img_index += 1
        # self.plot_iter += 1
        # if self.plot_iter == 2:
        self.img.setImage(self.img_array, autoLevels=False)  # uncomment despues de time test
예제 #16
0
class QgsFmvPlayer(QMainWindow, Ui_PlayerWindow):
    """ Video Player Class """
    def __init__(self, iface, path=None, parent=None):
        """ Constructor """
        super(QgsFmvPlayer, self).__init__(parent)
        self.setupUi(self)
        self.parent = parent
        self.iface = iface
        self.fileName = None
        self.metadataDlg = None
        self.createingMosaic = False
        self.currentInfo = 0.0

        self.RecGIF = QMovie(":/imgFMV/images/record.gif")

        self.videoWidget.customContextMenuRequested[QPoint].connect(
            self.contextMenuRequested)

        self.duration = 0
        self.playerMuted = False
        self.HasFileAudio = False

        self.player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.player.setNotifyInterval(1000)  # One second
        self.pass_time = 0.1
        self.playlist = QMediaPlaylist()

        #         self.player.setVideoOutput(
        #             self.videoWidget)  # Standar Surface

        self.player.setVideoOutput(
            self.videoWidget.videoSurface())  # Custom Surface

        self.player.durationChanged.connect(self.durationChanged)
        self.player.positionChanged.connect(self.positionChanged)
        self.player.mediaStatusChanged.connect(self.statusChanged)

        self.player.stateChanged.connect(self.setCurrentState)

        self.playerState = QMediaPlayer.StoppedState
        self.playFile(path)

        self.sliderDuration.setRange(0, self.player.duration() / 1000)

        self.volumeSlider.setValue(self.player.volume())
        self.volumeSlider.enterEvent = self.showVolumeTip

        if self.metadataDlg is None:
            self.metadataDlg = QgsFmvMetadata(parent=self, player=self)
            self.addDockWidget(Qt.RightDockWidgetArea, self.metadataDlg)
            self.metadataDlg.setMinimumWidth(500)
            self.metadataDlg.hide()

    def HasMetadata(self, videoPath):
        """ Check if video have Metadata or not """
        try:
            p = _spawn([
                '-i', videoPath, '-map', 'data-re', '-codec', 'copy', '-f',
                'data', '-'
            ])

            stdout_data, _ = p.communicate()

            if stdout_data == b'':
                qgsu.showUserAndLogMessage(QCoreApplication.translate(
                    "QgsFmvPlayer", "This video don't have Metadata ! : "),
                                           level=QGis.Info)
                return False

            return True
        except Exception as e:
            qgsu.showUserAndLogMessage(QCoreApplication.translate(
                "QgsFmvPlayer", "Metadata Callback Failed! : "),
                                       str(e),
                                       level=QGis.Info)

    def HasAudio(self, videoPath):
        """ Check if video have Metadata or not """
        try:
            p = _spawn([
                '-i', videoPath, '-show_streams', '-select_streams', 'a',
                '-loglevel', 'error'
            ],
                       type="ffprobe")

            stdout_data, _ = p.communicate()

            if stdout_data == b'':
                qgsu.showUserAndLogMessage(QCoreApplication.translate(
                    "QgsFmvPlayer", "This video don't have Audio ! : "),
                                           level=QGis.Info)
                return False

            return True
        except Exception as e:
            qgsu.showUserAndLogMessage(QCoreApplication.translate(
                "QgsFmvPlayer", "Audio check Failed! : "),
                                       str(e),
                                       level=QGis.Info)

    def callBackMetadata(self, currentTime, nextTime):
        """ Metadata CallBack """
        try:
            # TODO : Speed this function
            #             stdout_data = _check_output(['-i', self.fileName,
            #                         '-ss', currentTime,
            #                         '-to', nextTime,
            #                         '-f', 'data', '-'])

            t = callBackMetadataThread(cmds=[
                '-i', self.fileName, '-ss', currentTime, '-to', nextTime,
                '-map', 'data-re', '-f', 'data', '-'
            ])
            t.start()
            t.join(1)
            if t.is_alive():
                t.p.terminate()
                t.join()
            if t.stdout == b'':
                return

            for packet in StreamParser(t.stdout):
                try:
                    self.addMetadata(packet.MetadataList())
                    UpdateLayers(packet,
                                 parent=self,
                                 mosaic=self.createingMosaic)
                    self.iface.mapCanvas().refresh()
                    QApplication.processEvents()
                    return
                except Exception as e:
                    None
        except Exception as e:
            qgsu.showUserAndLogMessage(QCoreApplication.translate(
                "QgsFmvPlayer", "Metadata Callback Failed! : "),
                                       str(e),
                                       level=QGis.Info)

    def addMetadata(self, packet):
        ''' Add Metadata to List '''
        self.clearMetadata()
        row = 0
        for key in sorted(packet.keys()):
            self.metadataDlg.VManager.insertRow(row)
            self.metadataDlg.VManager.setItem(row, 0,
                                              QTableWidgetItem(str(key)))
            self.metadataDlg.VManager.setItem(
                row, 1, QTableWidgetItem(str(packet[key][0])))
            self.metadataDlg.VManager.setItem(
                row, 2, QTableWidgetItem(str(packet[key][1])))
            row += 1
        self.metadataDlg.VManager.setVisible(False)
        self.metadataDlg.VManager.resizeColumnsToContents()
        self.metadataDlg.VManager.setVisible(True)
        self.metadataDlg.VManager.verticalScrollBar().setSliderPosition(
            self.sliderPosition)

    def clearMetadata(self):
        ''' Clear Metadata List '''
        try:
            self.sliderPosition = self.metadataDlg.VManager.verticalScrollBar(
            ).sliderPosition()
            self.metadataDlg.VManager.setRowCount(0)
        except:
            None

    def saveInfoToJson(self):
        """ Save video Info to json """
        if not self.KillAllProcessors():
            return
        out_json, _ = QFileDialog.getSaveFileName(self, "Save File", "",
                                                  "Json Files (*.json)")
        if out_json == "":
            return
        try:
            self.VPProbeToJson = Converter()
            self.VPTProbeToJson = QThread()

            self.VPProbeToJson.moveToThread(self.VPTProbeToJson)

            self.VPProbeToJson.finished.connect(self.QThreadFinished)

            self.VPProbeToJson.error.connect(self.QThreadError)

            self.VPProbeToJson.progress.connect(
                self.progressBarProcessor.setValue)

            self.VPTProbeToJson.start(QThread.LowPriority)

            QMetaObject.invokeMethod(self.VPProbeToJson, 'probeToJson',
                                     Qt.QueuedConnection,
                                     Q_ARG(str, self.fileName),
                                     Q_ARG(str, out_json))

        except Exception as e:
            qgsu.showUserAndLogMessage(
                QCoreApplication.translate("QgsFmvPlayer",
                                           "Error saving Json"))
            self.QThreadFinished("probeToJson", "Closing ProbeToJson")

    def showVideoInfo(self):
        ''' Show default probe info '''
        try:

            self.VPProbe = Converter()
            self.VPTProbe = QThread()

            self.VPProbe.moveToThread(self.VPTProbe)

            self.VPProbe.finishedJson.connect(self.QThreadFinished)

            self.VPProbe.error.connect(self.QThreadError)

            self.VPProbe.progress.connect(self.progressBarProcessor.setValue)

            self.VPTProbe.start(QThread.LowPriority)

            QMetaObject.invokeMethod(self.VPProbe, 'probeShow',
                                     Qt.QueuedConnection,
                                     Q_ARG(str, self.fileName))

        except Exception as e:
            qgsu.showUserAndLogMessage(
                QCoreApplication.translate("QgsFmvPlayer", "Error Info Show"))
            self.QThreadFinished("probeShow", "Closing Probe")
        return

    def state(self):
        ''' Return Current State '''
        return self.playerState

    def setCurrentState(self, state):
        ''' Set Current State '''
        if state != self.playerState:
            self.playerState = state

            if state == QMediaPlayer.StoppedState:
                self.btn_play.setIcon(QIcon(":/imgFMV/images/play-arrow.png"))

        return

    def showColorDialog(self):
        ''' Show Color dialog '''
        self.ColorDialog = ColorDialog(parent=self)
        self.ColorDialog.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint)
        # Fail if not uncheked
        self.actionMagnifying_glass.setChecked(False)
        self.actionZoom_Rectangle.setChecked(False)
        self.ColorDialog.exec_()
        return

    def createMosaic(self, value):
        ''' Function for create Video Mosaic '''
        home = os.path.expanduser("~")

        qgsu.createFolderByName(home, "QGIS_FMV")
        homefmv = os.path.join(home, "QGIS_FMV")
        root, ext = os.path.splitext(os.path.basename(self.fileName))
        qgsu.createFolderByName(homefmv, root)
        self.createingMosaic = value
        # Create Group
        CreateGroupByName()
        return

    def contextMenuRequested(self, point):
        ''' Context Menu Video '''
        menu = QMenu()

        #         actionColors = menu.addAction(
        #             QCoreApplication.translate("QgsFmvPlayer", "Color Options"))
        #         actionColors.setShortcut("Ctrl+May+C")
        #         actionColors.triggered.connect(self.showColorDialog)

        actionMute = menu.addAction(
            QCoreApplication.translate("QgsFmvPlayer", "Mute/Unmute"))
        actionMute.setShortcut("Ctrl+May+U")
        actionMute.triggered.connect(self.setMuted)

        menu.addSeparator()
        actionAllFrames = menu.addAction(
            QCoreApplication.translate("QgsFmvPlayer", "Extract All Frames"))
        actionAllFrames.setShortcut("Ctrl+May+A")
        actionAllFrames.triggered.connect(self.ExtractAllFrames)

        actionCurrentFrames = menu.addAction(
            QCoreApplication.translate("QgsFmvPlayer",
                                       "Extract Current Frame"))
        actionCurrentFrames.setShortcut("Ctrl+May+Q")
        actionCurrentFrames.triggered.connect(self.ExtractCurrentFrame)

        menu.addSeparator()
        actionShowMetadata = menu.addAction(
            QCoreApplication.translate("QgsFmvPlayer", "Show Metadata"))
        actionShowMetadata.setShortcut("Ctrl+May+M")
        actionShowMetadata.triggered.connect(self.OpenQgsFmvMetadata)

        menu.exec_(self.mapToGlobal(point))

    # Start Snnipet FILTERS
    def grayFilter(self, value):
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetGray(value)
        self.videoWidget.UpdateSurface()
        return

    def edgeFilter(self, value):
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetEdgeDetection(value)
        self.videoWidget.UpdateSurface()
        return

    def invertColorFilter(self, value):
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetInvertColor(value)
        self.videoWidget.UpdateSurface()
        return

    def autoContrastFilter(self, value):
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetAutoContrastFilter(value)
        self.videoWidget.UpdateSurface()
        return

    def monoFilter(self, value):
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetMonoFilter(value)
        self.videoWidget.UpdateSurface()
        return

    def magnifier(self, value):
        self.UncheckUtils(self.sender(), value)
        self.videoWidget.SetMagnifier(value)
        self.videoWidget.UpdateSurface()
        return

    def zoomRect(self, value):
        self.UncheckUtils(self.sender(), value)
        self.videoWidget.SetZoomRect(value)
        self.videoWidget.UpdateSurface()
        return

    def UncheckUtils(self, sender, value):
        #         p = self.player.position()
        #         self.player.setVideoOutput(
        #             self.videoWidget.videoSurface())  # Custom surface
        #         self.player.setPosition(p)
        QApplication.processEvents()
        name = sender.objectName()
        self.actionMagnifying_glass.setChecked(
            True if name == "actionMagnifying_glass" else False)
        self.actionZoom_Rectangle.setChecked(True if name ==
                                             "actionZoom_Rectangle" else False)

        sender.setChecked(value)
        return

    def UncheckFilters(self, sender, value):
        #         p = self.player.position()
        #         self.player.setVideoOutput(
        #             self.videoWidget.videoSurface())  # Custom surface
        #         self.player.setPosition(p)
        #         QApplication.processEvents()
        name = sender.objectName()

        self.actionGray.setChecked(True if name == "actionGray" else False)
        self.actionInvert_Color.setChecked(True if name ==
                                           "actionInvert_Color" else False)
        self.actionMono_Filter.setChecked(True if name ==
                                          "actionMono_Filter" else False)
        self.actionCanny_edge_detection.setChecked(
            True if name == "actionCanny_edge_detection" else False)
        self.actionAuto_Contrast_Filter.setChecked(
            True if name == "actionAuto_Contrast_Filter" else False)

        self.videoWidget.SetGray(True if name == "actionGray" else False)
        self.videoWidget.SetEdgeDetection(
            True if name == "actionCanny_edge_detection" else False)
        self.videoWidget.SetInvertColor(True if name ==
                                        "actionInvert_Color" else False)
        self.videoWidget.SetMonoFilter(True if name ==
                                       "actionMono_Filter" else False)
        self.videoWidget.SetAutoContrastFilter(
            True if name == "actionAuto_Contrast_Filter" else False)

        sender.setChecked(value)
        return

    # End Snnipet FILTERS

    def isMuted(self):
        ''' Is muted video property'''
        return self.playerMuted

    def setMuted(self):
        ''' Muted video '''
        if self.player.isMuted():
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_up.png"))
            self.player.setMuted(False)
            self.volumeSlider.setEnabled(True)
        else:
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_off.png"))
            self.player.setMuted(True)
            self.volumeSlider.setEnabled(False)
        return

    def stop(self):
        ''' Stop video'''
        self.player.stop()
        self.videoWidget.update()
        return

    def volume(self):
        ''' Volume Slider '''
        return self.volumeSlider.value()

    def setVolume(self, volume):
        ''' Tooltip and set value'''
        self.player.setVolume(volume)
        self.showVolumeTip(volume)
        if 0 < volume <= 30:
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_30.png"))
        elif 30 < volume <= 60:
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_60.png"))
        elif 60 < volume <= 100:
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_up.png"))
        elif volume == 0:
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_off.png"))

    def EndMedia(self):
        ''' Button end video position '''
        if self.player.isVideoAvailable():
            self.player.setPosition(self.player.duration())
            self.videoWidget.update()
        return

    def StartMedia(self):
        ''' Button start video position '''
        if self.player.isVideoAvailable():
            self.player.setPosition(0)
            self.videoWidget.update()
        return

    def forwardMedia(self):
        ''' Button forward Video '''
        forwardTime = int(self.player.position()) + 10 * 1000
        if forwardTime > int(self.player.duration()):
            forwardTime = int(self.player.duration())
        self.player.setPosition(forwardTime)

    def rewindMedia(self):
        ''' Button rewind Video '''
        rewindTime = int(self.player.position()) - 10 * 1000
        if rewindTime < 0:
            rewindTime = 0
        self.player.setPosition(rewindTime)

    def AutoRepeat(self, checked):
        ''' Button AutoRepeat Video '''
        if checked:
            self.playlist.setPlaybackMode(QMediaPlaylist.Loop)
        else:
            self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)
        return

    def showVolumeTip(self, _):
        ''' Volume Slider Tooltip Trick '''
        self.style = self.volumeSlider.style()
        self.opt = QStyleOptionSlider()
        self.volumeSlider.initStyleOption(self.opt)
        rectHandle = self.style.subControlRect(self.style.CC_Slider, self.opt,
                                               self.style.SC_SliderHandle)
        self.tip_offset = QPoint(5, 15)
        pos_local = rectHandle.topLeft() + self.tip_offset
        pos_global = self.volumeSlider.mapToGlobal(pos_local)
        QToolTip.showText(pos_global,
                          str(self.volumeSlider.value()) + " %", self)

    def showMoveTip(self, currentInfo):
        ''' Player Silder Move Tooptip Trick '''
        self.style = self.sliderDuration.style()
        self.opt = QStyleOptionSlider()
        self.sliderDuration.initStyleOption(self.opt)
        rectHandle = self.style.subControlRect(self.style.CC_Slider, self.opt,
                                               self.style.SC_SliderHandle)
        self.tip_offset = QPoint(5, 15)
        pos_local = rectHandle.topLeft() + self.tip_offset
        pos_global = self.sliderDuration.mapToGlobal(pos_local)

        tStr = _seconds_to_time(currentInfo)

        QToolTip.showText(pos_global, tStr, self)

    def durationChanged(self, duration):
        ''' Duration video change signal '''
        duration /= 1000
        self.duration = duration
        self.sliderDuration.setMaximum(duration)

    def positionChanged(self, progress):
        ''' Current Video position change '''
        progress /= 1000

        if not self.sliderDuration.isSliderDown():
            self.sliderDuration.setValue(progress)

        self.updateDurationInfo(progress)

    def updateDurationInfo(self, currentInfo):
        ''' Update labels duration Info and CallBack Metadata '''
        duration = self.duration
        self.currentInfo = currentInfo
        if currentInfo or duration:

            totalTime = _seconds_to_time(duration)
            currentTime = _seconds_to_time(currentInfo)
            tStr = currentTime + " / " + totalTime

            nextTime = currentInfo + self.pass_time
            currentTimeInfo = _seconds_to_time_frac(currentInfo)
            nextTimeInfo = _seconds_to_time_frac(nextTime)
            # Metadata CallBack
            self.callBackMetadata(currentTimeInfo, nextTimeInfo)

        else:
            tStr = ""

        self.labelDuration.setText(tStr)

    def handleCursor(self, status):
        ''' Change cursor '''
        if status in (QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia,
                      QMediaPlayer.StalledMedia):
            self.setCursor(Qt.BusyCursor)
        else:
            self.unsetCursor()

    def statusChanged(self, status):
        ''' Signal Status video change '''
        self.handleCursor(status)
        if status == QMediaPlayer.LoadingMedia:
            self.videoAvailableChanged(False)
        elif status == QMediaPlayer.StalledMedia:
            self.videoAvailableChanged(False)
        if status == QMediaPlayer.EndOfMedia:
            self.videoAvailableChanged(True)
        elif status == QMediaPlayer.InvalidMedia:
            qgsu.showUserAndLogMessage(QCoreApplication.translate(
                "QgsFmvPlayer", self.player.errorString()),
                                       level=QGis.Warning)
            self.videoAvailableChanged(False)
        else:
            self.videoAvailableChanged(True)

    def playFile(self, videoPath):
        ''' Play file from path '''
        try:
            RemoveVideoLayers()
            RemoveGroupByName()
            self.fileName = videoPath
            self.playlist = QMediaPlaylist()
            url = QUrl.fromLocalFile(videoPath)
            self.playlist.addMedia(QMediaContent(url))
            self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)
            self.player.setPlaylist(self.playlist)

            self.setWindowTitle("Playing : " +
                                os.path.basename(os.path.normpath(videoPath)))

            if self.HasMetadata(videoPath):
                CreateVideoLayers()
                self.clearMetadata()
                self.lb_cursor_coord.setText(
                    "<span style='font-size:10pt; font-weight:bold;'>Lon :</span>"
                    +
                    "<span style='font-size:9pt; font-weight:normal;'>Null</span>"
                    +
                    "<span style='font-size:10pt; font-weight:bold;'> Lat :</span>"
                    +
                    "<span style='font-size:9pt; font-weight:normal;'>Null</span>"
                )
            else:
                self.btn_GeoReferencing.setEnabled(False)

            self.HasFileAudio = True
            if not self.HasAudio(videoPath):
                self.actionAudio.setEnabled(False)
                self.actionSave_Audio.setEnabled(False)
                self.HasFileAudio = False

            self.playClicked(True)

        except Exception as e:
            qgsu.showUserAndLogMessage(QCoreApplication.translate(
                "QgsFmvPlayer", 'Open Video File : '),
                                       str(e),
                                       level=QGis.Warning)

    def ReciconUpdate(self, frame):
        self.btn_Rec.setIcon(QIcon(self.RecGIF.currentPixmap()))

    def RecordVideo(self, value):
        ''' Cut Video '''
        currentTime = _seconds_to_time(self.currentInfo)

        if value is False:
            self.endRecord = currentTime
            _, file_extension = os.path.splitext(self.fileName)
            out, _ = QFileDialog.getSaveFileName(self, "Save As", "",
                                                 file_extension)
            if not out:
                self.RecGIF.frameChanged.disconnect(self.ReciconUpdate)
                self.RecGIF.stop()
                self.btn_Rec.setIcon(QIcon(":/imgFMV/images/record.png"))
                return False

            lfn = out.lower()
            if not lfn.endswith((file_extension)):
                out += file_extension

            p = _spawn([
                '-i', self.fileName, '-ss', self.startRecord, '-to',
                self.endRecord, '-c', 'copy', out
            ])
            p.communicate()
            qgsu.showUserAndLogMessage(
                QCoreApplication.translate("QgsFmvPlayer",
                                           "Save file succesfully!"))

            self.RecGIF.frameChanged.disconnect(self.ReciconUpdate)
            self.RecGIF.stop()
            self.btn_Rec.setIcon(QIcon(":/imgFMV/images/record.png"))
        else:
            self.startRecord = currentTime
            self.RecGIF.frameChanged.connect(self.ReciconUpdate)
            self.RecGIF.start()
        return

    def videoAvailableChanged(self, available):
        ''' Buttons for video available '''
        # self.btn_Color.setEnabled(available)
        self.btn_CaptureFrame.setEnabled(available)
        self.gb_PlayerControls.setEnabled(available)
        return

    def toggleGroup(self, state):
        ''' Toggle GroupBox '''
        sender = self.sender()
        if state:
            sender.setFixedHeight(sender.sizeHint().height())
        else:
            sender.setFixedHeight(15)

    def playClicked(self, state):
        ''' Stop and Play video '''
        if self.playerState in (QMediaPlayer.StoppedState,
                                QMediaPlayer.PausedState):
            self.btn_play.setIcon(QIcon(":/imgFMV/images/pause.png"))
            self.player.play()
        elif self.playerState == QMediaPlayer.PlayingState:
            self.btn_play.setIcon(QIcon(":/imgFMV/images/play-arrow.png"))
            self.player.pause()

    def seek(self, seconds):
        '''Slider Move'''
        self.player.setPosition(seconds * 1000)
        self.showMoveTip(seconds)

    def convertVideo(self):
        '''Convert Video To Other Format '''
        if not self.KillAllProcessors():
            return
        sel = "mp4 Files (*.mp4)"
        out, _ = QFileDialog.getSaveFileName(
            self, "Save Video as...", None,
            "ogg files (*.ogg);;avi Files (*.avi);;mkv Files (*.mkv);;webm Files (*.webm);;flv Files (*.flv);;mov Files (*.mov);;mp4 Files (*.mp4);;mpg Files (*.mpg);;mp3 Files (*.mp3)",
            sel)

        if not out:
            return False

        lfn = out.lower()
        if not lfn.endswith(('.ogg', '.avi', '.mkv', '.webm', '.flv', '.mov',
                             '.mp4', '.mp3', '.mpg')):
            # The default.
            out += '.mp4'

        try:
            self.VPConverter = Converter()
            self.VPTConverter = QThread()

            self.VPConverter.moveToThread(self.VPTConverter)

            self.VPConverter.finished.connect(self.QThreadFinished)

            self.VPConverter.error.connect(self.QThreadError)

            self.VPConverter.progress.connect(
                self.progressBarProcessor.setValue)

            self.VPTConverter.start(QThread.LowPriority)

            # TODO : Make Correct format Conversion and embebed metadata
            info = self.VPConverter.probeInfo(self.fileName)
            if info is not None:
                if self.HasFileAudio:
                    audio_codec = info.audio.codec
                    audio_samplerate = info.audio.audio_samplerate
                    audio_channels = info.audio.audio_channels

                video_codec = info.video.codec
                video_width = info.video.video_width
                video_height = info.video.video_height
                video_fps = info.video.video_fps

            _, out_ext = os.path.splitext(out)

            if self.HasFileAudio:
                options = {
                    'format': out_ext[1:],
                    'audio': {
                        'codec': audio_codec,
                        'samplerate': audio_samplerate,
                        'channels': audio_channels
                    },
                    'video': {
                        'codec': video_codec,
                        'width': video_width,
                        'height': video_height,
                        'fps': video_fps
                    }
                }
            else:
                options = {
                    'format': out_ext[1:],
                    'video': {
                        'codec': video_codec,
                        'width': video_width,
                        'height': video_height,
                        'fps': video_fps
                    }
                }
            QMetaObject.invokeMethod(self.VPConverter, 'convert',
                                     Qt.QueuedConnection,
                                     Q_ARG(str,
                                           self.fileName), Q_ARG(str, out),
                                     Q_ARG(dict, options), Q_ARG(bool, False))

        except Exception as e:
            qgsu.showUserAndLogMessage(
                QCoreApplication.translate("QgsFmvPlayer",
                                           "Error converting video "))
            self.QThreadFinished("convert", "Closing convert")

    def ShowPlot(self, bitrate_data, frame_count, output=None):
        ''' Show plot,because show not work using threading '''
        matplot.figure().canvas.set_window_title(self.fileName)
        matplot.title("Stream Bitrate vs Time")
        matplot.xlabel("Time (sec)")
        matplot.ylabel("Frame Bitrate (kbit/s)")
        matplot.grid(True)
        # map frame type to color
        frame_type_color = {
            # audio
            'A': 'yellow',
            # video
            'I': 'red',
            'P': 'green',
            'B': 'blue'
        }

        global_peak_bitrate = 0.0
        global_mean_bitrate = 0.0

        # render charts in order of expected decreasing size
        for frame_type in ['I', 'P', 'B', 'A']:

            # skip frame type if missing
            if frame_type not in bitrate_data:
                continue

            # convert list of tuples to numpy 2d array
            frame_list = bitrate_data[frame_type]
            frame_array = numpy.array(frame_list)

            # update global peak bitrate
            peak_bitrate = frame_array.max(0)[1]
            if peak_bitrate > global_peak_bitrate:
                global_peak_bitrate = peak_bitrate

            # update global mean bitrate (using piecewise mean)
            mean_bitrate = frame_array.mean(0)[1]
            global_mean_bitrate += mean_bitrate * \
                (len(frame_list) / frame_count)

            # plot chart using gnuplot-like impulses
            matplot.vlines(frame_array[:, 0], [0],
                           frame_array[:, 1],
                           color=frame_type_color[frame_type],
                           label="{} Frames".format(frame_type))

        self.progressBarProcessor.setValue(90)
        # calculate peak line position (left 15%, above line)
        peak_text_x = matplot.xlim()[1] * 0.15
        peak_text_y = global_peak_bitrate + \
            ((matplot.ylim()[1] - matplot.ylim()[0]) * 0.015)
        peak_text = "peak ({:.0f})".format(global_peak_bitrate)

        # draw peak as think black line w/ text
        matplot.axhline(global_peak_bitrate, linewidth=2, color='black')
        matplot.text(peak_text_x,
                     peak_text_y,
                     peak_text,
                     horizontalalignment='center',
                     fontweight='bold',
                     color='black')

        # calculate mean line position (right 85%, above line)
        mean_text_x = matplot.xlim()[1] * 0.85
        mean_text_y = global_mean_bitrate + \
            ((matplot.ylim()[1] - matplot.ylim()[0]) * 0.015)
        mean_text = "mean ({:.0f})".format(global_mean_bitrate)

        # draw mean as think black line w/ text
        matplot.axhline(global_mean_bitrate, linewidth=2, color='black')
        matplot.text(mean_text_x,
                     mean_text_y,
                     mean_text,
                     horizontalalignment='center',
                     fontweight='bold',
                     color='black')

        matplot.legend()
        if output != "":
            matplot.savefig(output)
        else:
            matplot.show()

        self.progressBarProcessor.setValue(100)

    def CreateBitratePlot(self):
        ''' Create video Plot Bitrate Thread '''
        if not self.KillAllProcessors():
            return
        try:
            self.VPBitratePlot = CreatePlotsBitrate()
            self.VPTBitratePlot = QThread()

            self.VPBitratePlot.moveToThread(self.VPTBitratePlot)

            self.VPBitratePlot.finished.connect(self.QThreadFinished)

            self.VPBitratePlot.return_fig.connect(self.ShowPlot)

            self.VPBitratePlot.error.connect(self.QThreadError)

            self.VPBitratePlot.progress.connect(
                self.progressBarProcessor.setValue)

            self.VPTBitratePlot.start(QThread.LowPriority)

            sender = self.sender().objectName()

            if sender == "actionAudio":
                QMetaObject.invokeMethod(self.VPBitratePlot, 'CreatePlot',
                                         Qt.QueuedConnection,
                                         Q_ARG(str, self.fileName),
                                         Q_ARG(str, None), Q_ARG(str, 'audio'))

            elif sender == "actionVideo":
                QMetaObject.invokeMethod(self.VPBitratePlot, 'CreatePlot',
                                         Qt.QueuedConnection,
                                         Q_ARG(str, self.fileName),
                                         Q_ARG(str, None), Q_ARG(str, 'video'))

            elif sender == "actionSave_Audio":
                selfilter = "Portable Network Graphics (*.png)"
                fileaudio, _ = QFileDialog.getSaveFileName(
                    self, "Save Audio Bitrate Plot", "",
                    "EPS Encapsulated Postscript (*.eps);;"
                    "PGF code for LaTex (*.pgf);;"
                    "Portable document format(*pdf);;"
                    "Portable Network Graphics (*.png);;"
                    "Postscript (*.ps);;"
                    "Raw RGBA bitmap (*.raw*.rgba);;"
                    "Scalable vector graphics (*.svg*.svgz)", selfilter)
                if fileaudio == "":
                    return

                QMetaObject.invokeMethod(self.VPBitratePlot, 'CreatePlot',
                                         Qt.QueuedConnection,
                                         Q_ARG(str, self.fileName),
                                         Q_ARG(str, fileaudio),
                                         Q_ARG(str, 'audio'))

            elif sender == "actionSave_Video":
                selfilter = "Portable Network Graphics (*.png)"
                filevideo, _ = QFileDialog.getSaveFileName(
                    self, "Save Video Bitrate Plot", "",
                    "EPS Encapsulated Postscript (*.eps);;"
                    "PGF code for LaTex (*.pgf);;"
                    "Portable document format(*pdf);;"
                    "Portable Network Graphics (*.png);;"
                    "Postscript (*.ps);;"
                    "Raw RGBA bitmap (*.raw*.rgba);;"
                    "Scalable vector graphics (*.svg*.svgz)", selfilter)
                if filevideo == "":
                    return

                QMetaObject.invokeMethod(self.VPBitratePlot, 'CreatePlot',
                                         Qt.QueuedConnection,
                                         Q_ARG(str, self.fileName),
                                         Q_ARG(str, filevideo),
                                         Q_ARG(str, 'video'))

        except Exception as e:
            qgsu.showUserAndLogMessage(
                QCoreApplication.translate("QgsFmvPlayer",
                                           "Failed creating Plot Bitrate"))

    def ExtractAllFrames(self):
        """ Extract All Video Frames Thread """
        if not self.KillAllProcessors():
            return

        options = QFileDialog.DontResolveSymlinks | QFileDialog.ShowDirsOnly
        directory = QFileDialog.getExistingDirectory(
            self,
            QCoreApplication.translate("QgsFmvPlayer", "Save images"),
            '',
            options=options)

        if directory:

            self.VPExtractFrames = ExtractFramesProcessor()
            self.VPTExtractAllFrames = QThread()

            self.VPExtractFrames.moveToThread(self.VPTExtractAllFrames)
            self.VPExtractFrames.finished.connect(self.QThreadFinished)
            self.VPExtractFrames.error.connect(self.QThreadError)
            self.VPExtractFrames.progress.connect(
                self.progressBarProcessor.setValue)
            self.VPTExtractAllFrames.start(QThread.LowPriority)

            QMetaObject.invokeMethod(self.VPExtractFrames,
                                     'ExtractFrames', Qt.QueuedConnection,
                                     Q_ARG(str, directory),
                                     Q_ARG(str, self.fileName))
        return

    def ExtractCurrentFrame(self):
        """ Extract Current Frame Thread """
        image = self.videoWidget.GetCurrentFrame()
        out_image, _ = QFileDialog.getSaveFileName(
            self, "Save Current Frame", "",
            "Image File (*.png *.jpg *.bmp *.tiff)")

        if out_image == "":
            return

        if out_image:
            t = threading.Thread(target=self.SaveCapture,
                                 args=(
                                     image,
                                     out_image,
                                 ))
            t.start()
        return

    def SaveCapture(self, image, output):
        ''' Save Current Image '''
        image.save(output)
        QApplication.processEvents()
        return

    def QThreadFinished(self, process, msg, outjson=None):
        ''' Finish Threads '''
        if process == "ExtractFramesProcessor":
            self.VPExtractFrames.deleteLater()
            self.VPTExtractAllFrames.terminate()
            self.VPTExtractAllFrames.deleteLater()
        elif process == "CreatePlotsBitrate":
            self.VPBitratePlot.deleteLater()
            self.VPTBitratePlot.terminate()
            self.VPTBitratePlot.deleteLater()
        elif process == "convert":
            self.VPConverter.deleteLater()
            self.VPTConverter.terminate()
            self.VPTConverter.deleteLater()
        elif process == "probeToJson":
            self.VPProbeToJson.deleteLater()
            self.VPTProbeToJson.terminate()
            self.VPTProbeToJson.deleteLater()
        elif process == "probeShow":
            self.VPProbe.deleteLater()
            self.VPTProbe.terminate()
            self.VPTProbe.deleteLater()
            self.showVideoInfoDialog(outjson)

        QApplication.processEvents()
        self.progressBarProcessor.setValue(0)
        return

    def QThreadError(self, processor, e, exception_string):
        """ Threads Errors"""
        qgsu.showUserAndLogMessage(QCoreApplication.translate(
            "QgsFmvPlayer", processor),
                                   'Failed!\n'.format(exception_string),
                                   level=QGis.Warning)

        self.QThreadFinished(processor, "Closing Processor")

        return

    def OpenQgsFmvMetadata(self):
        """ Open Metadata Dock """
        if self.metadataDlg is None:
            self.metadataDlg = QgsFmvMetadata(parent=self, player=self)
            self.addDockWidget(Qt.RightDockWidgetArea, self.metadataDlg)
            self.metadataDlg.show()
        else:
            self.metadataDlg.show()
        return

    def KillAllProcessors(self):
        """Kill All Processors"""
        """ Extract all frames Processors """
        try:
            if self.VPTExtractAllFrames.isRunning():
                ret = qgsu.CustomMessage(
                    QCoreApplication.translate(
                        "QgsFmvPlayer", "HEY...Active background process!"),
                    QCoreApplication.translate("QgsFmvPlayer",
                                               "Do you really want close?"))
                if ret == QMessageBox.Yes:
                    self.QThreadFinished("ExtractFramesProcessor",
                                         "Closing Extract Frames Processor")
                else:
                    return False
        except:
            None
        """ Bitrates Processors"""
        try:
            if self.VPTBitratePlot.isRunning():
                ret = qgsu.CustomMessage(
                    QCoreApplication.translate(
                        "QgsFmvPlayer", "HEY...Active background process!"),
                    QCoreApplication.translate("QgsFmvPlayer",
                                               "Do you really want close?"))
                if ret == QMessageBox.Yes:
                    self.QThreadFinished("CreatePlotsBitrate",
                                         "Closing Plot Bitrate")
                else:
                    return False
        except:
            None
        """ Converter Processors """
        try:
            if self.VPTConverter.isRunning():
                ret = qgsu.CustomMessage(
                    QCoreApplication.translate(
                        "QgsFmvPlayer", "HEY...Active background process!"),
                    QCoreApplication.translate("QgsFmvPlayer",
                                               "Do you really want close?"))
                if ret == QMessageBox.Yes:
                    self.QThreadFinished("convert", "Closing convert")
                else:
                    return False
        except:
            None
        """ probeToJson Processors """
        try:
            if self.VPTProbeToJson.isRunning():
                ret = qgsu.CustomMessage(
                    QCoreApplication.translate(
                        "QgsFmvPlayer", "HEY...Active background process!"),
                    QCoreApplication.translate("QgsFmvPlayer",
                                               "Do you really want close?"))
                if ret == QMessageBox.Yes:
                    self.QThreadFinished("probeToJson", "Closing Info to Json")
                else:
                    return False
        except:
            None
        """ probeShow Processors """
        try:
            if self.VPTProbe.isRunning():
                ret = qgsu.CustomMessage(
                    QCoreApplication.translate(
                        "QgsFmvPlayer", "HEY...Active background process!"),
                    QCoreApplication.translate("QgsFmvPlayer",
                                               "Do you really want close?"))
                if ret == QMessageBox.Yes:
                    self.QThreadFinished("probeShow",
                                         "Closing Show Video Info")
                else:
                    return False
        except:
            None
        return True

    def showVideoInfoDialog(self, outjson):
        """ Show Video Information Dialog """
        view = QTreeView()
        model = QJsonModel()
        view.setModel(model)
        model.loadJsonFromConsole(outjson)

        self.VideoInfoDialog = QDialog(self)
        self.VideoInfoDialog.setWindowTitle("Video Information : " +
                                            self.fileName)
        self.VideoInfoDialog.setWindowIcon(
            QIcon(":/imgFMV/images/video_information.png"))

        self.verticalLayout = QVBoxLayout(self.VideoInfoDialog)
        self.verticalLayout.addWidget(view)
        view.expandAll()
        view.header().setSectionResizeMode(QHeaderView.ResizeToContents)

        self.VideoInfoDialog.setWindowFlags(Qt.Window
                                            | Qt.WindowCloseButtonHint)
        self.VideoInfoDialog.setObjectName("VideoInfoDialog")
        self.VideoInfoDialog.resize(500, 400)
        self.VideoInfoDialog.show()

    def closeEvent(self, evt):
        """ Close Event """
        if self.KillAllProcessors() is False:
            evt.ignore()
            return

        self.player.stop()
        self.parent._PlayerDlg = None
        self.parent.ToggleActiveFromTitle()
        RemoveVideoLayers()
        RemoveGroupByName()

        # Restore Filters State
        self.videoWidget.RestoreFilters()
        # QApplication.processEvents()
        del self.player
예제 #17
0
class Listener(QObject):
    finished = pyqtSignal()
    update = pyqtSignal(GridContainerUpdate)
    resize = pyqtSignal(GreetingData)
    status = pyqtSignal(str)
    tournament = pyqtSignal(str)

    def __init__(self, player, address, port, main, name=None):
        super().__init__()
        self.player: UIPlayer = player
        self.address = address
        self.port = port
        self.main = main
        self.name = name

        self.snakes = []

        self.thread = QThread()
        # move the Worker object to the Thread object
        # "push" self from the current thread to this thread
        self.moveToThread(self.thread)
        # Connect Worker Signals to the Thread slots
        self.finished.connect(self.thread.quit)
        # Connect Thread started signal to Worker operational slot method
        self.thread.started.connect(self.run)

    def start(self):
        # Start the thread
        self.thread.start()
        print("Thread_Start")

    def stop(self):
        self.thread.terminate()

    @pyqtSlot()
    def run(self):

        print("Thread_Running")

        client = Client(self.address, self.port)

        client.connect()

        while True:
            command = client.get_command()
            if command.comm == ENetworkCommand.greeting and self.main:
                client.send_name(self.name)

                self.resize.emit(command.data)
            elif command.comm == ENetworkCommand.greeting:
                client.send_name(self.name)
            elif command.comm == ENetworkCommand.container_update:
                self.update.emit(command.data)
                self.snakes = command.data.snakes
            elif command.comm == ENetworkCommand.turn_count_start and self.main:
                self.player.window.startPlaning(command.data)
                self.status.emit("")
            elif command.comm == ENetworkCommand.call_for_plans:
                if self.main:
                    self.player.window.endPlaningself()
                client.send_plans(self.player.movingPlans)
            elif command.comm == ENetworkCommand.game_end and self.main:
                send = F"Player {command.data.winner} won!"
                if command.data.has_next_game:
                    send += "\nWaiting\nfor next game"
                self.status.emit(send)
            elif command.comm == ENetworkCommand.tournament_update:
                self.tournament.emit(
                    Client.tournament_update_data_to_string(command.data))
예제 #18
0
class CutplanWidget(QWidget):
    newcutplans = pyqtSignal(bool)
    cploadfinish = pyqtSignal()

    def setupUi(self, Form, size=250):
        self.data = None
        self.addData = None
        self.host = "172.16.90.241"
        self.CPWidgets = []
        self.CPImg = []
        self.CPProgress = []
        self.CPLabels = []
        self.CPWidgetsH = []
        self.anims = []
        self.graphics = []
        self.CPLoads = []
        self.CPNum = 0
        self.CPMinSize = 0
        self.size = size
        self.speedsetting = 55

        Form.setObjectName("Form")
        Form.setMaximumWidth(self.size)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
        Form.setSizePolicy(sizePolicy)
        Form.setMinimumWidth(self.size)
        Form.setStyleSheet("background-color: rgb(255, 255, 255);")
        self.horizontalLayout = QVBoxLayout(Form)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setSpacing(15)
        self.horizontalLayout.setObjectName("horizontalLayout")

        # CPWidgets
        self.CPLayout = QVBoxLayout()
        self.CPLayout.setContentsMargins(0, 0, 0, 0)
        self.CPLayout.setObjectName("CPLayout")
        self.horizontalLayout.addLayout(self.CPLayout)

        self.AddButton = QPushButton(Form)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.AddButton.sizePolicy().hasHeightForWidth())
        self.AddButton.setSizePolicy(sizePolicy)
        self.AddButton.setMinimumSize(QSize(self.size, self.size))
        self.AddButton.setStyleSheet(
            "background-color: rgba(255, 255, 255, 0);")
        self.AddButton.setText("")
        self.AddButton.setCursor(QCursor(Qt.PointingHandCursor))
        icon1 = QIcon("images/add.png")
        self.AddButton.setIcon(icon1)
        self.AddButton.setIconSize(QSize(75, 75))
        self.AddButton.setObjectName("AddButton")
        self.horizontalLayout.addWidget(self.AddButton)

        self.setupThread()

        # EVENTS
        # self.AddButton.clicked.connect(self.onClick2)

        self.retranslateUi(Form)
        QMetaObject.connectSlotsByName(Form)
        self.Form = Form

    def setupThread(self):
        self.thread = QThread()
        self.loader = CutplanLoader()
        self.loader.moveToThread(self.thread)
        self.loader.cutplanloaded.connect(self.AddOneCP)
        self.threadconnected = False

    def onClick2(self):
        if self.threadconnected:
            self.thread.started.disconnect()
            self.threadconnected = False

    def onClick(self):
        # Ping to see if can connect, use wait cursor
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            check_output("ping -n 1 -w 2000 " + self.host, shell=True)
        except CalledProcessError:
            QApplication.restoreOverrideCursor()
            self.ErrorBox("Network Error", "Connection Failed",
                          "Check internet connection to Log Scanner Server.")
            return

        QApplication.restoreOverrideCursor()
        if self.threadconnected:
            self.thread.started.disconnect()
            self.threadconnected = False
        self.AddDialog = AddCutplanDialog()
        self.AddDialog.setupUi(self.addData, "support/cpquery.sql", self.host)
        self.AddDialog.show()
        self.AddDialog.buttonBox.accepted.connect(self.AddCP)

    def DeleteCP(self):
        for w in self.CPProgress:
            w.deleteLater()
        for w in self.CPLabels:
            w.deleteLater()
        for w in self.CPWidgetsH:
            w.deleteLater()
        for w in self.anims:
            w.deleteLater()
        for w in self.graphics:
            w.deleteLater()
        for w in self.CPLoads:
            w.deleteLater()
        for w in self.CPWidgets:
            widget = w.parent()
            self.CPLayout.removeWidget(widget)
            w.deleteLater()
            widget.deleteLater()
            widget = None
        self.CPWidgets = []
        self.CPImg = []
        self.CPProgress = []
        self.CPLabels = []
        self.CPWidgetsH = []
        self.anims = []
        self.graphics = []
        self.CPLoads = []
        self.data = None

    def AddCP(self, addData=None, errors=None):
        if addData is not None:
            # this happens when we're in popup read-only mode
            self.addData = addData
        if self.addData is None:
            self.newcutplans.emit(False)
            return

        self.DeleteCP()
        if self.addData.shape[0] == 0:
            # just return if no cutplans to add
            self.newcutplans.emit(False)
            return

        try:
            conn = sqlconnect(LogScanner)
        except sqlProgrammingError:
            conn = sqlconnect(LogScanner)

        sqlfile = "support\\cpquery2.sql"
        f = open(sqlfile, 'r')
        sqltext = f.read()
        sqltext = sqltext.replace("@CPID", "-1")
        self.data = read_sql(sqltext, conn)

        self.CPNum = self.addData.shape[0]
        self.CPErrors = []
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        for id in range(self.CPNum):
            self.CPLoads.append(QLabel(self))
            self.CPLoads[id].setAlignment(Qt.AlignCenter)
            self.CPLoads[id].setMinimumSize(QSize(self.size, self.size))
            self.CPLoads[id].setMaximumSize(QSize(self.size, self.size))
            movie = QMovie("images\\loading.gif")
            movie.setScaledSize(QSize(self.size, self.size))
            self.CPLoads[id].setMovie(movie)
            movie.start()
            self.CPLayout.addWidget(self.CPLoads[id])
            self.CPErrors.append(False)

        if errors is not None:
            e = []
            for i in range(errors.shape[0]):
                e.append(list(errors.iloc[i]))
                for j in range(errors.shape[1]):
                    if errors.iloc[i, j] > 0:
                        self.CPErrors[i] = True
                        break
            self.loader.errors = e

        self.loader.id = 0
        self.loader.cutplanid = self.addData.ID[0]
        self.loader.logcount = self.addData['Log Count'][0]
        self.thread.quit()
        self.thread.wait()
        self.thread.started.connect(self.loader.loadCutplan)
        self.threadconnected = True
        self.thread.start()

        # self.SimSchedule = SimSchedule(self.data, "logs//")
        if addData is None:
            # readonly will skip this
            self.newcutplans.emit(True)
        QApplication.restoreOverrideCursor()

    def AddOneCP(self, id):
        imgfolder = "images\\cps\\"
        data = self.loader.data
        speed = self.loader.speeds[id]
        if speed > self.speedsetting:
            speed = self.speedsetting

        widget = QWidget(self)
        widget.setObjectName('CPW' + str(id + 1))
        widget.setMinimumSize(QSize(self.size, self.size))
        widget.setMaximumSize(QSize(self.size, self.size))

        self.CPWidgetsH.append(QPushButton(widget))
        self.CPWidgetsH[id].setStyleSheet(
            "background-color: rgba(255, 255, 255, 0);")
        self.CPWidgetsH[id].setText("")
        icon = QIcon(imgfolder + "CP" + str(id + 1) + "_h.png")
        self.CPWidgetsH[id].setIcon(icon)
        self.CPWidgetsH[id].setIconSize(QSize(self.size, self.size))
        self.CPWidgetsH[id].setObjectName('CPH' + str(id + 1))
        self.CPWidgetsH[id].setGeometry(QRect(0, 0, self.size, self.size))

        self.CPWidgets.append(HoverButton(widget, id))
        self.CPWidgets[id].setMinimumSize(QSize(self.size, self.size))
        self.CPWidgets[id].setStyleSheet(
            "background-color: rgba(255, 255, 255, 0);")
        self.CPWidgets[id].setText("")
        icon = QIcon(imgfolder + "CP" + str(id + 1) + ".png")
        self.CPImg.append('test' + str(id + 1))
        self.CPWidgets[id].setIcon(icon)
        self.CPWidgets[id].setIconSize(QSize(self.size, self.size))
        self.CPWidgets[id].setObjectName('CP' + str(id + 1))
        self.CPWidgets[id].setGeometry(QRect(0, 0, self.size, self.size))

        self.graphics.append(QGraphicsOpacityEffect(widget))
        self.CPWidgets[id].setGraphicsEffect(self.graphics[id])
        self.graphics[id].setOpacity(1)
        self.anims.append(QPropertyAnimation(self.graphics[id], b"opacity"))
        self.anims[id].setDuration(400)
        self.anims[id].setEasingCurve(QEasingCurve.InOutSine)
        self.anims[id].finished.connect(self.uiUpdate)

        self.CPProgress.append(CPProgress(id, parent=widget))
        self.CPProgress[id].setObjectName('CPP' + str(id + 1))
        self.CPProgress[id].setGeometry(QRect(0, 0, self.size, self.size))
        if self.CPErrors[id]:
            self.CPProgress[id].c = (255, 121, 121)
            self.CPProgress[id].setValue(0)
        else:
            self.CPProgress[id].c = (255, 255, 255)
            self.CPProgress[id].setValue(100)

        self.CPLabels.append(QLabel(widget))
        self.CPLabels[id].setObjectName('CPL' + str(id + 1))
        self.CPLabels[id].setGeometry(QRect(10, 200, 60, 30))
        font = QFont()
        font.setPointSize(6)
        font.setBold(True)
        font.setFamily("Tahoma")
        self.CPLabels[id].setFont(font)
        self.CPLabels[id].setStyleSheet(
            "QLabel {\n"
            "	color: white;\n"
            "	background-color: rgb(0, 115, 119);\n"
            "	border-radius: 15px;\n"
            "}")
        self.CPLabels[id].setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        group = data.Description[0][2:4]
        length = data.Description[0][5:7]
        des = length[0] + "." + length[1] + "m " + group
        desPD = self.loader.des
        if desPD.shape[0] > 0:
            des = desPD.Description[0]
            self.CPLabels[id].setText(
                des[5:10] + " cm\n{:.1f} m\n".format(float(des[0:3]) - 0.1) +
                "{0} m/min".format(speed))
            self.CPLayout.insertWidget(id * 2, widget)
            self.CPLoads[id].setVisible(False)
            self.setMinimumSize(QSize(self.size * (self.CPNum + 1), self.size))
            self.CPProgress[id].mouseHover.connect(self.onHover)
            self.data = self.data.append(data.iloc[0], ignore_index=True)
        else:
            des = des + "-" + str(int(group) + 1)
            des = des[5:10] + "cm\n{:.1f}m".format(float(des[0:3]) - 0.1)
            QApplication.restoreOverrideCursor()
            self.ErrorBox(
                "Input Error", "Log group " + des + " doesn't exist.",
                "Cutplan \"" + data.Description[0] + "\" was skipped.")
            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        if id + 1 < self.CPNum:
            self.thread.quit()
            self.thread.wait()
            self.loader.id = id + 1
            self.loader.cutplanid = self.addData.ID[id + 1]
            self.loader.logcount = self.addData['Log Count'][id + 1]
            self.thread.start()
        else:
            self.thread.quit()
            self.thread.terminate()
            self.loader.errors = None
            self.cploadfinish.emit()

    def onHover(self, hover, id):
        self.anims[id].stop()
        if hover:
            self.anims[id].setStartValue(self.graphics[id].opacity())
            self.anims[id].setEndValue(0)
            # self.CPWidgets[id].setIcon(icon)
        else:
            self.anims[id].setStartValue(self.graphics[id].opacity())
            self.anims[id].setEndValue(1)
            # self.CPWidgets[id].setIcon(icon)
        self.anims[id].start()  # QAbstractAnimation.DeleteWhenStopped)

    def uiUpdate(self):
        for w in self.CPWidgets:
            w.update()

    def ErrorBox(self, errortxt, description, info):
        msgbox = QMessageBox(self)
        msgbox.setWindowTitle(errortxt)
        msgbox.setText(description)
        msgbox.setInformativeText(info)
        msgbox.setStandardButtons(QMessageBox.Ok)
        msgbox.setIcon(QMessageBox.Critical)
        msgbox.setStyleSheet("#qt_msgbox_label {font-weight: bold;}")
        msgbox.exec()

    def retranslateUi(self, Form):
        _translate = QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
예제 #19
0
class MainWindow(QMainWindow):
    """..."""
    def __init__(self, config_, tools_):
        super(MainWindow, self).__init__()
        self._config = config_
        self._tools = tools_
        self._setup_sub_thread()
        self._setup_ui()
        self.show()

    def _setup_ui(self):
        self.setWindowTitle('Wechat Toolkit')
        self.setGeometry(100, 100, 600, 400)
        self._set_menu_bar()
        self._set_main_window()

    def _set_main_window(self):
        _tab = QTabWidget()
        self._info_window = self._set_tool_window()
        self._console_window = GuiUtils.console_window()
        _tab.addTab(self._info_window, 'Tools Settings')
        _tab.addTab(self._console_window, 'Console Window')
        _tab.currentChanged.connect(self._save_tool)
        self.setCentralWidget(_tab)

    def _set_tool_window(self):
        _window = QWidget()
        _win_layout = QHBoxLayout()

        _tool_list = QWidget()
        _vbox = QVBoxLayout()
        self._all_tools = QCheckBox('Wechat Tools')
        self._all_tools.stateChanged.connect(self._on_check_all)
        self._tool_list = QListWidget()
        self._tool_list.setSelectionMode(QAbstractItemView.SingleSelection)
        for _t in self._tools:
            _enable = self._config[_t['config_key']]['enable']
            _check_state = Qt.Checked if _enable else Qt.Unchecked
            _item = QListWidgetItem()
            _item.setText(_t['type'])
            _item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
            _item.setCheckState(_check_state)
            self._tool_list.addItem(_item)
        self._tool_list.itemClicked.connect(self._on_tool_clicked)

        _vbox.addWidget(self._all_tools)
        _vbox.addWidget(self._tool_list)
        _tool_list.setLayout(_vbox)
        _win_layout.addWidget(_tool_list)

        self._friend_filter = ChatSetter('Friend Chat')
        self._friend_filter.saved.connect(self._save_filter)
        _win_layout.addWidget(self._friend_filter)
        self._group_filter = ChatSetter('Group Chat')
        self._group_filter.saved.connect(self._save_filter)
        _win_layout.addWidget(self._group_filter)

        self._default_tool = 0
        self._tool_list.setCurrentRow(self._default_tool)
        self._display_tool_setting(self._default_tool)
        self._tool_list.currentItem().setBackground(QColor(0, 105, 217))
        self._tool_list.currentItem().setForeground(QColor('white'))

        _window.setLayout(_win_layout)
        return _window

    def _on_check_all(self):
        if self._all_tools.isChecked():
            for _index in range(self._tool_list.count()):
                self._tool_list.item(_index).setCheckState(Qt.Checked)
        else:
            for _index in range(self._tool_list.count()):
                self._tool_list.item(_index).setCheckState(Qt.Unchecked)

    def _on_tool_clicked(self, item_):
        self._tool_list.setCurrentItem(item_)
        for _index in range(self._tool_list.count()):
            self._tool_list.item(_index).setBackground(QColor('white'))
            self._tool_list.item(_index).setForeground(QColor('black'))
        item_.setBackground(QColor(0, 105, 217))
        item_.setForeground(QColor('white'))
        _index = self._tool_list.currentRow()
        self._display_tool_setting(_index)

    def _display_tool_setting(self, tool_index_):
        _config = self._config[self._tools[tool_index_]['config_key']]
        _friend_enable = _config['friend_enable']
        _group_enable = _config['group_enable']
        _friend_filter = _config['friend_filter']
        _group_filter = _config['group_filter']
        self._friend_filter.enable(_friend_enable)
        self._group_filter.enable(_group_enable)
        self._friend_filter.display_filter(_friend_filter)
        self._group_filter.display_filter(_group_filter)

    def _save_filter(self, name_, enable_, filter_):
        _index = self._tool_list.currentRow()
        _config = self._config[self._tools[_index]['config_key']]
        if name_ == 'Friend Chat':
            _config['friend_enable'] = enable_
            _config['friend_filter'] = filter_
        elif name_ == 'Group Chat':
            _config['group_enable'] = enable_
            _config['group_filter'] = filter_

    def _save_tool(self):
        for _index in range(self._tool_list.count()):
            self._config[self._tools[_index]['config_key']]['enable'] = \
                self._tool_list.item(_index).checkState() == Qt.Checked

    def _set_menu_bar(self):
        self._menu_bar = self.menuBar()
        self._menu_bar.setNativeMenuBar(False)
        _menu = {
            'Main.Run': (self._run, 'Ctrl+R'),
            'Main.Stop': (self._stop, 'Ctrl+T'),
            'Main.Account': (self._test_action, 'Ctrl+A'),
            'Main.About': (self._test_action, ''),
            'Main.Quit': (self._quit, 'Ctrl+Q'),
            'View.Show Log.Main': (self._test_action, ''),
            'View.Show Log.Recall-Blocker': (self._test_action, ''),
            'View.Show Log.Auto-Replier': (self._test_action, ''),
            'View.Show Log.Auto-Repeater': (self._test_action, ''),
            'View.Show Log.Combined': (self._test_action, ''),
            'View.Clean Log': (self._clean_log, ''),
            'Settings.Edit Settings': (self._test_action, 'Ctrl+Alt+S'),
            'Settings.Import Settings': (self._test_action, 'Ctrl+Alt+I'),
            'Settings.Export Settings': (self._test_action, 'Ctrl+Alt+E'),
            'Help.Help': (self._test_action, '')
        }
        for _menu_name, _menu_params in _menu.items():
            self._add_menu(_menu_name, _menu_params)

        _stop = self._get_menu_wgt('Main.Stop')
        _stop.setDisabled(True)

    def _add_menu(self, menu_name_, menu_params_, menu_=None):
        _menu = menu_ if menu_ else self._menu_bar
        _menu_group = menu_name_.split(Separator.OBJ)
        if len(_menu_group) == 1:
            _menu_btn = QAction(menu_name_, self)
            _menu_btn.triggered.connect(menu_params_[0])
            _menu_btn.setShortcut(menu_params_[1])
            GuiUtils.add_widget(_menu, _menu_btn, menu_name_, WidgetType.BTN)
            _menu.addAction(_menu_btn)
        else:
            _sub_menu_name = _menu_group.pop(0)
            _sub_menu = GuiUtils.get_widget_by_name(
                _menu, _sub_menu_name) or _menu.addMenu(_sub_menu_name)
            GuiUtils.add_widget(_menu, _sub_menu, _sub_menu_name,
                                WidgetType.MENU)
            _rest_menu_name = Separator.OBJ.join(_menu_group)
            self._add_menu(_rest_menu_name, menu_params_, _sub_menu)

    def _get_menu_wgt(self, menu_name_, menu_=None):
        _menu = menu_ if menu_ else self._menu_bar
        _menu_group = menu_name_.split(Separator.OBJ)
        if len(_menu_group) == 1:
            return GuiUtils.get_widget_by_name(_menu, menu_name_)
        else:
            _sub_menu_name = _menu_group.pop(0)
            _sub_menu = GuiUtils.get_widget_by_name(_menu, _sub_menu_name)
            _rest_menu_name = Separator.OBJ.join(_menu_group)
            return self._get_menu_wgt(_rest_menu_name, _sub_menu)

    def _test_action(self):
        GuiUtils.info_dialog(self, 'Test', 'No integrated yet.')

    def _setup_sub_thread(self):
        self._sub_thread = QThread()
        self._wechat_tools = WechatThread(config_=self._config)
        self._wechat_tools.moveToThread(self._sub_thread)
        self._sub_thread.started.connect(self._wechat_tools.run)

    def _run(self):
        self._save_tool()
        self._sub_thread.start()
        self._get_menu_wgt('Main.Run').setDisabled(True)
        self._get_menu_wgt('Main.Stop').setDisabled(False)

    def _stop(self):
        self._wechat_tools.stop()
        self._sub_thread.terminate()
        self._sub_thread.wait()
        self._get_menu_wgt('Main.Run').setDisabled(False)
        self._get_menu_wgt('Main.Stop').setDisabled(True)

    def _quit(self):
        self.close()

    def closeEvent(self, event):
        """..."""
        if GuiUtils.question_dialog(self, 'Quit',
                                    'Are you sure to quit wechat tools?'):
            event.accept()
        else:
            event.ignore()

    def _clean_log(self):
        self._console_window.text.clear()
예제 #20
0
class Sweep2D(BaseSweep, QObject):
    """
    A 2-D Sweep of QCoDeS Parameters. 
    
    This class runs by setting an outside parameter, then running
    an inner Sweep1D object. The inner sweep handles all data saving
    and communications through the Thread objects. 
    
    Attributes
    ---------
    in_params:
        List defining the inner sweep [parameter, start, stop, step].
    out_params:
        List defining the outer sweep [parameter, start, stop, step].
    inter_delay: 
        Time (in seconds) to wait between data points on inner sweep.
    outer_delay: 
        Time (in seconds) to wait between data points on outer sweep.
    save_data: 
        Flag used to determine if the data should be saved or not.
    plot_data: 
        Flag to determine whether or not to live-plot data.
    complete_func:
        Sets a function to be executed upon completion of the outer sweep.
    update_func:
        Sets a function to be executed upon completion of the inner sweep.
    plot_bin: 
        Defaults to 1. Controls amount of data stored in the data_queue list 
        in the Plotter Thread.
    runner:
        Assigns the Runner Thread.
    plotter:
        Assigns the Plotter Thread.
    back_multiplier:
        Factor to scale the step size after flipping directions.
    heatmap_plotter:
        Uses color to represent values of a third parameter plotted against
        two sweeping parameters.
        
    Methods
    ---------
    follow_param(*p)
        Saves parameters to be tracked, for both saving and plotting data.
    follow_srs(self, l, name, gain=1.0)
        Adds an SRS lock-in to Sweep1D to ensure that the range is kept correctly.
    _create_measurement()
        Creates the measurement object for the sweep. 
    start(ramp_to_start=True, persist_data=None)
        Extends the start() function of BaseSweep.
    stop()
        Stops the sweeping of both the inner and outer sweep.
    resume()
        Resumes the inner and outer sweeps.
    update_values()
        Updates plots and heatmap based on data from the inner and outer sweeps.
    get_param_setpoint()
        Obtains the current value of the setpoint.
    set_update_rule(func)
        Sets a function to be called upon completion of each inner sweep.
    send_updates(no_sp=False)
        Passed in Sweep2D.
    kill()
        Ends all threads and closes any active plots.
    ramp_to(value, start_on_finish=False, multiplier=1)
        Ramps the set_param to a given value, at a rate specified by multiplier.
    ramp_to_zero()
        
    done_ramping(start_on_finish=False)
        
    """
    
    add_heatmap_lines = pyqtSignal(list)
    clear_heatmap_plot = pyqtSignal()

    def __init__(self, in_params, out_params, outer_delay=1, update_func=None, *args, **kwargs):
        """
        Initializes the sweep.
        
        The inner sweep parameters('in_params') and outer sweep parameters ('out_params') MUST be a list, 
        conforming to the following standard:
        
            [ <QCoDeS Parameter>, <start value>, <stop value>, <step size> ]
            
        Parameters
        ---------
        in_params:
            A list conforming to above standard for the inner sweep.
        out_params:
            A list conforming to above standard for the outer sweep.
        inter_delay:    
            Time (in seconds) to wait between data points on inner sweep.
        outer_delay: 
            Time (in seconds) to wait between data points on outer sweep.
        save_data: 
            Flag used to determine if the data should be saved or not.
        plot_data: 
            Flag to determine whether or not to live-plot data.
        complete_func:
            Sets a function to be executed upon completion of the outer sweep.
        update_func:
            Sets a function to be executed upon completion of the inner sweep.
        plot_bin: 
            Defaults to 1. Controls amount of data stored in the data_queue list 
            in the Plotter Thread.
        runner:
            Assigns the Runner Thread.
        plotter:
            Assigns the Plotter Thread.
        back_multiplier:
            Factor to scale the step size after flipping directions.
        heatmap_plotter:
            Uses color to represent values of a third parameter plotted against
            two sweeping parameters.
        """
        
        # Ensure that the inputs were passed (at least somewhat) correctly
        if len(in_params) != 4 or len(out_params) != 4:
            raise TypeError('For 2D Sweep, must pass list of 4 object for each sweep parameter, \
                             in order: [ <QCoDeS Parameter>, <start value>, <stop value>, <step size> ]')

        # Save our input variables
        self.in_param = in_params[0]
        self.in_start = in_params[1]
        self.in_stop = in_params[2]
        self.in_step = in_params[3]

        # Ensure that the step has the right sign
        if (self.in_stop - self.in_start) > 0:
            self.in_step = abs(self.in_step)
        else:
            self.in_step = (-1) * abs(self.in_step)

        self.set_param = out_params[0]
        self.out_start = out_params[1]
        self.out_stop = out_params[2]
        self.out_step = out_params[3]
        self.out_setpoint = self.out_start

        if (self.out_stop - self.out_start) > 0:
            self.out_step = abs(self.out_step)
        else:
            self.out_step = (-1) * abs(self.out_step)

        # Initialize the BaseSweep
        QObject.__init__(self)
        BaseSweep.__init__(self, set_param=self.set_param, *args, **kwargs)

        # Create the inner sweep object
        self.in_sweep = Sweep1D(self.in_param, self.in_start, self.in_stop, self.in_step, bidirectional=True,
                                inter_delay=self.inter_delay, save_data=self.save_data, x_axis_time=0,
                                plot_data=self.plot_data, back_multiplier=self.back_multiplier)
        # We set our outer sweep parameter as a follow param for the inner sweep, so that
        # it is always read and saved with the rest of our data
        self.in_sweep.follow_param(self.set_param)
        # Our update_values() function iterates the outer sweep, so when the inner sweep
        # is done, call that function automatically
        self.in_sweep.set_complete_func(self.update_values)

        self.outer_delay = outer_delay

        # Flags for ramping
        self.inner_ramp = False
        self.outer_ramp = False
        self.ramp_sweep = None

        # Set the function to call when the inner sweep finishes
        if update_func is None:
            self.update_rule = self.no_change

        # Initialize our heatmap plotting thread
        self.heatmap_plotter = None

    def __str__(self):
        return f"2D Sweep of {self.set_param.label} from {self.out_start} to {self.out_stop} with step {self.out_step}," \
               f"while sweeping {self.in_param.label} from {self.in_start} to {self.in_stop} with step {self.in_step}."

    def __repr__(self):
        return f"Sweep2D([{self.set_param.label}, {self.out_start}, {self.out_stop}, {self.out_step}], " \
               f"[{self.in_param.label}, {self.in_start}, {self.in_stop}, {self.in_step}])"

    def follow_param(self, *p):
        """
        Saves parameters to be tracked, for both saving and plotting data.
        
        
        Since the data saving is always handled by the inner Sweep1D object, all parameters
        are registered in the inner Sweep1D object.
        
        The parameters must be followed before '_create_measurement()' is called.
            
        Parameters
        ---------
        *p:
            Variable number of arguments, each of which must be a QCoDeS Parameter,
            or a list of QCoDeS Parameters, for the sweep to follow.
        """
        
        for param in p:
            if isinstance(param, list):
                for l in param:
                    self.in_sweep._params.append(l)
            else:
                self.in_sweep._params.append(param)
        self._params = self.in_sweep._params

    def follow_srs(self, l, name, gain=1.0):
        """
        Adds an SRS lock-in to Sweep1D to ensure that the range is kept correctly.
        
        Parameters
        ---------
        l: 
            The lock-in instrument.
        name:
            The user-defined name of the instrument.
        gain:
            The current gain value.
        """
        
        self.in_sweep.follow_srs((l, name, gain))

    def _create_measurement(self):
        """
        Creates the measurement object for the sweep. 
        
        The measurement object is created through the inner Sweep1D object.
        
        Returns
        ---------
        self.meas:
            The QCoDeS Measurement object responsible for running the sweep.
        """
        
        self.meas = self.in_sweep._create_measurement()

        return self.meas

    def start(self, ramp_to_start=True, persist_data=None):
        """
        Extends the start() function of BaseSweep. 
        
        The first outer sweep setpoint is set, and the inner sweep is started.
        
        Parameters
        ---------
        ramp_to_start:
            Sets a sweep to gently iterate the parameter to its starting value.
        persist_data:
            Sets the outer parameter for Sweep2D.
        """
        
        if self.is_running:
            print("Can't start the sweep, we're already running!")
            return
        elif self.outer_ramp:
            print("Can't start the sweep, we're currently ramping the outer sweep parameter!")
            return

        if self.meas is None:
            self._create_measurement()

        if ramp_to_start:
            self.ramp_to(self.out_setpoint, start_on_finish=True)
        else:
            print(
                f'Starting the 2D Sweep. Ramping {self.set_param.label} to {self.out_stop} {self.set_param.unit}, '
                f'while sweeping {self.in_param.label} between {self.in_start} {self.in_param.unit} and {self.in_stop} '
                f'{self.in_param.unit}')

            self.set_param.set(self.out_setpoint)

            time.sleep(self.outer_delay)

            self.is_running = True

            if self.heatmap_plotter is None:
                # Initialize our heatmap
                self.heatmap_plotter = Heatmap(self)
                self.heatmap_thread = QThread()
                self.heatmap_plotter.moveToThread(self.heatmap_thread)
                self.heatmap_plotter.create_figs()
                self.add_heatmap_lines.connect(self.heatmap_plotter.add_lines)
                self.clear_heatmap_plot.connect(self.heatmap_plotter.clear)
                self.heatmap_thread.start()

            self.in_sweep.start(persist_data=(self.set_param, self.out_setpoint))

            self.plotter = self.in_sweep.plotter
            self.runner = self.in_sweep.runner

    def stop(self):
        """ Stops the sweeping of both the inner and outer sweep. """
        self.is_running = False
        self.in_sweep.stop()

    def resume(self):
        """ Resumes the inner and outer sweeps. """
        self.is_running = True
        self.in_sweep.start(persist_data=(self.set_param, self.out_setpoint), ramp_to_start=False)

    def update_values(self):
        """
        Updates plots and heatmap based on data from the inner and outer sweeps.
        
        This function is automatically called upon completion of the inner sweep.
        The outer parameter is iterated and the inner sweep is restarted. If the stop
        condition is reached, the completed signal is emitted and the sweeps are stopped. 
        """
        
        # If this function was called from a ramp down to 0, a special case of sweeping, deal with that
        # independently
        if self.in_sweep.is_ramping:
            # We are no longer ramping to zero

            self.inner_ramp = False
            # Check if our outer ramp to zero is still going, and if not, then officially end
            # our ramping to zero
            if not self.outer_ramp:
                self.is_running = False
                self.inner_sweep.is_running = False
                print("Done ramping both parameters to zero")
            # Stop the function from running any further, as we don't want to check anything else
            return

        # print("trying to update heatmap")
        # Update our heatmap!
        lines = self.in_sweep.plotter.axes[1].get_lines()
        self.add_heatmap_lines.emit(lines)

        # Check our update condition
        self.update_rule(self.in_sweep, lines)
        #        self.in_sweep.ramp_to(self.in_sweep.begin, start_on_finish=False)

        #        while self.in_sweep.is_ramping == True:
        #            time.sleep(0.5)

        # If we aren't at the end, keep going
        if abs(self.out_setpoint - self.out_stop) - abs(self.out_step / 2) > abs(self.out_step) * 1e-4:
            self.out_setpoint = self.out_setpoint + self.out_step
            time.sleep(self.outer_delay)
            print(f"Setting {self.set_param.label} to {self.out_setpoint} {self.set_param.unit}")
            self.set_param.set(self.out_setpoint)
            time.sleep(self.outer_delay)
            # Reset our plots
            self.in_sweep.reset_plots()
            self.in_sweep.start(persist_data=(self.set_param, self.out_setpoint))
        # If neither of the above are triggered, it means we are at the end of the sweep
        else:
            self.is_running = False
            print(f"Done with the sweep, {self.set_param.label}={self.out_setpoint}")
            self.in_sweep.kill()
            self.completed.emit()

    def get_param_setpoint(self):
        """ Obtains the current value of the setpoint. """
        s = f"{self.set_param.label} = {self.set_param.get()} {self.set_param.unit} \
        \n{self.inner_sweep.set_param.label} = {self.inner_sweep.set_param.get()} {self.inner_sweep.set_param.unit}"
        return s

    def set_update_rule(self, func):
        """
        Sets a function to be called upon completion of each inner sweep.
        
        Parameters
        ---------
        func:
            The function handle desired to set the update function. 
        """
        
        self.update_rule = func

    def send_updates(self, no_sp=False):
        pass

    def kill(self):
        """ Ends all threads and closes any active plots. """
        
        self.in_sweep.kill()

        # Gently shut down the heatmap
        if self.heatmap_plotter is not None:
            self.clear_heatmap_plot.emit()
            self.heatmap_thread.exit()
            if not self.heatmap_thread.wait(1000):
                self.heatmap_thread.terminate()
                print('forced heatmap to terminate')
            self.heatmap_plotter = None

    def ramp_to(self, value, start_on_finish=False, multiplier=1):
        """
        Ramps the set_param to a given value, at a rate specified by the multiplier.

        Parameter
        ---------
        value:
            The setpoint for the sweep to ramp to.
        start_on_finish:
            Flag to determine whether to begin the sweep when ramping is finished.
        multiplier:
            The multiplier for the step size, to ramp quicker than the sweep speed.
        """
        
        # Ensure we aren't currently running
        if self.outer_ramp:
            print(f"Currently ramping. Finish current ramp before starting another.")
            return
        if self.is_running:
            print(f"Already running. Stop the sweep before ramping.")
            return

        # Check if we are already at the value
        curr_value = self.set_param.get()
        if abs(value - curr_value) <= self.out_step / 2:
            # print(f"Already within {self.step} of the desired ramp value. Current value: {curr_value},
            # ramp setpoint: {value}.\nSetting our setpoint directly to the ramp value.")
            self.set_param.set(value)
            self.done_ramping(start_on_finish=True)
            return

        # Create a new sweep to ramp our outer parameter to zero
        self.ramp_sweep = Sweep1D(self.set_param, curr_value, value, multiplier * self.out_step,
                                  inter_delay=self.inter_delay,
                                  complete_func=partial(self.done_ramping, start_on_finish),
                                  save_data=False, plot_data=True)

        self.is_running = False
        self.outer_ramp = True
        self.ramp_sweep.start(ramp_to_start=False)

        print(f'Ramping {self.set_param.label} to {value} . . . ')

    def ramp_to_zero(self):
        """Ramps the set_param to 0, at the same rate as already specified. """

        print("Ramping both parameters to 0.")
        # Ramp our inner sweep parameter to zero
        self.inner_ramp = True
        self.in_sweep.ramp_to(0)

        # Check our step sign
        if self.out_setpoint > 0:
            self.out_step = (-1) * abs(self.out_step)
        else:
            self.out_step = abs(self.out_step)

        # Create a new sweep to ramp our outer parameter to zero
        zero_sweep = Sweep1D(self.set_param, self.set_param.get(), 0, self.step, inter_delay=self.inter_delay,
                             complete_func=self.done_ramping)
        self.is_running = True
        self.outer_ramp = True
        zero_sweep.start()

    def done_ramping(self, start_on_finish=False):
        """
        Alerts the sweep that the ramp is finished.
        
        Parameters
        ---------
        start_on_finish:
            Sweep will be called to start immediately after ramping when set to True.
        """
        
        # Our outer parameter has finished ramping
        self.outer_ramp = False
        if self.ramp_sweep is not None:
            self.ramp_sweep.kill()
            self.ramp_sweep = None
        # Check if our inner parameter has finished
        while self.in_sweep.is_ramping:
            time.sleep(0.5)

        # If so, tell the system we are done
        self.is_running = False
        print("Done ramping!")

        if start_on_finish:
            self.start(ramp_to_start=False)

    def estimate_time(self, verbose=True):
        in_time = self.in_sweep.estimate_time()
        n_lines = abs((self.out_start-self.out_stop)/self.out_step)+1
        out_time = self.outer_delay * self.out_step

        t_est = in_time*n_lines + out_time

        hours = int(t_est / 3600)
        minutes = int((t_est % 3600) / 60)
        seconds = t_est % 60
        if verbose is True:
            print(f'Estimated time for {repr(self)} to run: {hours}h:{minutes:2.0f}m:{seconds:2.0f}s')
        return t_est
예제 #21
0
class InferenceViewer(QtGui.QMainWindow):
    def __init__(self, model_name, model_format, image_dir, model_location,
                 label, hierarchy, image_val, input_dims, output_dims,
                 batch_size, output_dir, add, multiply, verbose, fp16, replace,
                 loop, rali_mode, gui, container_logo, fps_file, cpu_name,
                 gpu_name, parent):
        super(InferenceViewer, self).__init__(parent)
        self.parent = parent

        self.model_name = model_name
        self.model_format = model_format
        self.image_dir = image_dir
        self.model_location = model_location
        self.label = label
        self.hierarchy = hierarchy
        self.image_val = image_val
        self.input_dims = input_dims
        self.output_dims = output_dims
        self.batch_size = batch_size
        self.batch_size_int = (int)(batch_size)
        self.output_dir = output_dir
        self.add = add
        self.multiply = multiply
        self.gui = True if gui == 'yes' else False
        self.fp16 = fp16
        self.replace = replace
        self.verbose = verbose
        self.loop = loop
        self.rali_mode = rali_mode
        inputImageDir = os.path.expanduser(image_dir)
        self.total_images = len(os.listdir(inputImageDir))
        self.origImageQueue = queue.Queue()
        self.augImageQueue = queue.Queue()
        self.fps_file = fps_file
        self.inferenceEngine = None
        self.receiver_thread = None

        self.initEngines()

        if self.gui:
            self.container_index = (int)(container_logo)
            self.cpu_name = cpu_name
            self.gpu_name = gpu_name
            self.pauseState = False
            self.showAug = False
            self.elapsedTime = QTime.currentTime()
            self.lastTime = 0
            self.totalElapsedTime = 0.0
            self.totalAccuracy = 0
            self.progIndex = 0
            self.augIntensity = 0.0
            self.imgCount = 0
            self.frameCount = 9
            self.lastIndex = self.frameCount - 1
            self.x = [0]
            self.y = [0]
            self.augAccuracy = []
            self.totalCurve = None
            self.augCurve = None
            self.graph = None
            self.legend = None
            self.lastAugName = None
            self.pen = pg.mkPen('w', width=4)
            self.AMD_Radeon_pixmap = QPixmap("./data/images/AMD_Radeon.png")
            self.AMD_Radeon_white_pixmap = QPixmap(
                "./data/images/AMD_Radeon-white.png")
            self.MIVisionX_pixmap = QPixmap("./data/images/MIVisionX-logo.png")
            self.MIVisionX_white_pixmap = QPixmap(
                "./data/images/MIVisionX-logo-white.png")
            self.EPYC_pixmap = QPixmap("./data/images/EPYC-blue.png")
            self.EPYC_white_pixmap = QPixmap(
                "./data/images/EPYC-blue-white.png")
            self.docker_pixmap = QPixmap("./data/images/Docker.png")
            self.singularity_pixmap = QPixmap("./data/images/Singularity.png")
            self.rali_pixmap = QPixmap("./data/images/RALI.png")
            self.rali_white_pixmap = QPixmap("./data/images/RALI-white.png")
            self.graph_image_pixmap = QPixmap("./data/images/Graph-image.png")
            self.graph_image_white_pixmap = QPixmap(
                "./data/images/Graph-image-white.png")

            self.initUI()
            self.updateTimer = QTimer()
            self.updateTimer.timeout.connect(self.update)
            self.updateTimer.timeout.connect(self.plotGraph)
            self.updateTimer.timeout.connect(self.setProgressBar)
            self.updateTimer.start(40)

    def initUI(self):
        uic.loadUi("inference_viewer.ui", self)
        self.setStyleSheet("background-color: white")
        self.name_label.setText("Model: %s" % (self.model_name))
        self.cpuName_label.setText(self.cpu_name)
        self.gpuName_label.setText(self.gpu_name)
        self.dataset_label.setText("Augmentation set - %d" % (self.rali_mode))
        self.imagesFrame.setStyleSheet(
            ".QFrame {border-width: 20px; border-image: url(./data/images/filmStrip.png);}"
        )
        self.total_progressBar.setStyleSheet(
            "QProgressBar::chunk { background: lightblue; }")
        self.top1_progressBar.setStyleSheet(
            "QProgressBar::chunk { background: green; }")
        self.top5_progressBar.setStyleSheet(
            "QProgressBar::chunk { background: lightgreen; }")
        self.mis_progressBar.setStyleSheet(
            "QProgressBar::chunk { background: red; }")
        self.total_progressBar.setMaximum(self.total_images *
                                          self.batch_size_int)
        self.graph = pg.PlotWidget(title="Accuracy vs Time")
        self.graph.setLabel('left', 'Accuracy', '%')
        self.graph.setLabel('bottom', 'Time', 's')
        self.graph.setYRange(0, 100, padding=0)
        pg.setConfigOptions(antialias=True)
        self.totalCurve = self.graph.plot(pen=self.pen)
        self.augCurve = self.graph.plot(pen=pg.mkPen('b', width=4))
        self.legend = pg.LegendItem(offset=(370, 1))
        self.legend.setParentItem(self.graph.plotItem)
        self.legend.addItem(self.totalCurve, 'Cumulative')
        self.graph.setBackground(None)
        self.graph.setMaximumWidth(550)
        self.graph.setMaximumHeight(300)
        self.verticalLayout_2.addWidget(self.graph)
        self.level_slider.setMaximum(100)
        self.level_slider.valueChanged.connect(self.setIntensity)
        self.pause_pushButton.setStyleSheet(
            "color: white; background-color: darkBlue")
        self.stop_pushButton.setStyleSheet(
            "color: white; background-color: darkRed")
        self.pause_pushButton.clicked.connect(self.pauseView)
        self.stop_pushButton.clicked.connect(self.terminate)
        self.dark_checkBox.stateChanged.connect(self.setBackground)
        self.verbose_checkBox.stateChanged.connect(self.showVerbose)
        self.rali_checkBox.stateChanged.connect(self.showRALI)
        self.dark_checkBox.setChecked(True)
        self.graph_imageLabel.setPixmap(self.graph_image_pixmap)
        if self.container_index == 1:
            self.container_logo.setPixmap(self.docker_pixmap)
        elif self.container_index == 2:
            self.container_logo.setPixmap(self.singularity_pixmap)
        else:
            self.container_logo.hide()
        for augmentation in range(self.batch_size_int):
            self.augAccuracy.append([0])

        self.showVerbose()
        self.showRALI()

    def initEngines(self):
        self.receiver_thread = QThread()
        # Creating an object for inference.
        self.inferenceEngine = modelInference(
            self.model_name, self.model_format, self.image_dir,
            self.model_location, self.label, self.hierarchy, self.image_val,
            self.input_dims, self.output_dims, self.batch_size,
            self.output_dir, self.add, self.multiply, self.verbose, self.fp16,
            self.replace, self.loop, self.rali_mode, self.origImageQueue,
            self.augImageQueue, self.gui, self.total_images, self.fps_file)

        self.inferenceEngine.moveToThread(self.receiver_thread)
        self.receiver_thread.started.connect(self.inferenceEngine.runInference)
        self.receiver_thread.finished.connect(self.inferenceEngine.deleteLater)
        self.receiver_thread.start()
        self.receiver_thread.terminate()

    def paintEvent(self, event):
        self.showAugImage()
        self.showImage()
        self.displayFPS()
        if self.imgCount == self.total_images:
            if self.loop == 'yes':
                self.resetViewer()
            else:
                self.terminate()

    def resetViewer(self):
        self.imgCount = 0
        del self.x[:]
        self.x.append(0)
        del self.y[:]
        self.y.append(0)
        del self.augAccuracy[:]
        for augmentation in range(self.batch_size_int):
            self.augAccuracy.append([0])

        self.lastTime = 0
        self.elapsedTime = QTime.currentTime()
        self.totalElapsedTime = 0.0
        self.progIndex = 0
        self.showAug = False
        self.lastIndex = self.frameCount - 1
        self.totalCurve.clear()
        self.augCurve.clear()
        self.name_label.setText("Model: %s" % (self.model_name))
        self.legend.removeItem(self.lastAugName)

    def setProgressBar(self):
        if self.showAug:
            self.setAugProgress(self.progIndex)
        else:
            self.setTotalProgress()

    def setTotalProgress(self):
        totalStats = self.inferenceEngine.getTotalStats()
        top1 = totalStats[0]
        top5 = totalStats[1]
        mis = totalStats[2]
        totalCount = top5 + mis
        self.totalAccuracy = (float)(top5) / (totalCount + 1) * 100
        self.total_progressBar.setValue(totalCount)
        self.total_progressBar.setMaximum(self.total_images *
                                          self.batch_size_int)
        self.imgProg_label.setText(
            "Processed: %d of %d" %
            (totalCount, self.total_images * self.batch_size_int))
        self.top1_progressBar.setValue(top1)
        self.top1_progressBar.setMaximum(totalCount)
        self.top5_progressBar.setValue(top5)
        self.top5_progressBar.setMaximum(totalCount)
        self.mis_progressBar.setValue(mis)
        self.mis_progressBar.setMaximum(totalCount)

    def setAugProgress(self, augmentation):
        augStats = self.inferenceEngine.getAugStats(augmentation)
        top1 = augStats[0]
        top5 = augStats[1]
        mis = augStats[2]
        totalCount = top5 + mis
        self.total_progressBar.setValue(totalCount)
        self.total_progressBar.setMaximum(self.total_images)
        self.imgProg_label.setText("Processed: %d of %d" %
                                   (totalCount, self.total_images))
        self.top1_progressBar.setValue(top1)
        self.top1_progressBar.setMaximum(totalCount)
        self.top5_progressBar.setValue(top5)
        self.top5_progressBar.setMaximum(totalCount)
        self.mis_progressBar.setValue(mis)
        self.mis_progressBar.setMaximum(totalCount)

    def plotGraph(self):
        if not self.pauseState:
            curTime = self.elapsedTime.elapsed() / 1000.0
            if (curTime - self.lastTime > 0.01):
                self.x.append(curTime + self.totalElapsedTime)
                self.y.append(self.totalAccuracy)
                self.totalCurve.setData(x=self.x, y=self.y, pen=self.pen)
                for augmentation in range(self.batch_size_int):
                    augStats = self.inferenceEngine.getAugStats(augmentation)
                    top5 = augStats[1]
                    mis = augStats[2]
                    totalCount = top5 + mis
                    totalAccuracy = (float)(top5) / (totalCount + 1) * 100
                    self.augAccuracy[augmentation].append(totalAccuracy)

                if self.showAug:
                    self.augCurve.setData(x=self.x,
                                          y=self.augAccuracy[self.progIndex],
                                          pen=pg.mkPen('b', width=4))

            self.lastTime = curTime

    def showImage(self):
        if not self.origImageQueue.empty():
            origImage = self.origImageQueue.get()
            origWidth = origImage.shape[1]
            origHeight = origImage.shape[0]
            qOrigImage = QtGui.QImage(origImage, origWidth, origHeight,
                                      origWidth * 3,
                                      QtGui.QImage.Format_RGB888)
            qOrigImageResized = qOrigImage.scaled(self.image_label.width(),
                                                  self.image_label.height(),
                                                  QtCore.Qt.IgnoreAspectRatio)
            index = self.imgCount % self.frameCount
            self.origImage_layout.itemAt(index).widget().setPixmap(
                QtGui.QPixmap.fromImage(qOrigImageResized))
            self.origImage_layout.itemAt(index).widget().setStyleSheet(
                "border: 5px solid yellow;")
            self.origImage_layout.itemAt(
                self.lastIndex).widget().setStyleSheet("border: 0")
            self.imgCount += 1
            self.lastIndex = index

    def showAugImage(self):
        if not self.augImageQueue.empty():
            augImage = self.augImageQueue.get()
            augWidth = augImage.shape[1]
            augHeight = augImage.shape[0]
            qAugImage = QtGui.QImage(augImage, augWidth, augHeight,
                                     augWidth * 3, QtGui.QImage.Format_RGB888)
            if self.batch_size_int == 64:
                qAugImageResized = qAugImage.scaled(
                    self.aug_label.width(), self.aug_label.height(),
                    QtCore.Qt.IgnoreAspectRatio)
            else:
                qAugImageResized = qAugImage.scaled(self.aug_label.width(),
                                                    self.aug_label.height(),
                                                    QtCore.Qt.KeepAspectRatio)
            self.aug_label.setPixmap(QtGui.QPixmap.fromImage(qAugImageResized))

    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_Escape:
            self.terminate()

        if event.key() == QtCore.Qt.Key_Space:
            self.pauseView()

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            mousePos = event.pos()
            if self.aug_label.geometry().contains(mousePos):
                index = self.calculateIndex(mousePos.x(), mousePos.y())
                self.progIndex = index
                self.showAug = True
                augName = self.inferenceEngine.getAugName(index)
                self.name_label.setText(augName)
                self.augCurve.setData(x=self.x,
                                      y=self.augAccuracy[self.progIndex],
                                      pen=pg.mkPen('b', width=4))
                self.legend.removeItem(self.lastAugName)
                self.legend.addItem(self.augCurve, augName)
                self.lastAugName = augName
            else:
                self.showAug = False
                self.name_label.setText("Model: %s" % (self.model_name))
                self.augCurve.clear()
                self.legend.removeItem(self.lastAugName)
                self.legend.removeItem('Cumulative')
                self.legend.addItem(self.totalCurve, 'Cumulative')
            if not self.pauseState:
                self.totalCurve.clear()
                self.augCurve.clear()

    def setBackground(self):
        if self.dark_checkBox.isChecked():
            self.setStyleSheet("background-color: #25232F;")
            self.pen = pg.mkPen('w', width=4)
            self.graph.setBackground(None)
            self.origTitle_label.setStyleSheet("color: #C82327;")
            self.controlTitle_label.setStyleSheet("color: #C82327;")
            self.progTitle_label.setStyleSheet("color: #C82327;")
            self.graphTitle_label.setStyleSheet("color: #C82327;")
            self.augTitle_label.setStyleSheet("color: #C82327;")
            self.name_label.setStyleSheet("color: white;")
            self.dataset_label.setStyleSheet("color: white;")
            self.imgProg_label.setStyleSheet("color: white;")
            self.fps_label.setStyleSheet("color: #C82327;")
            self.dark_checkBox.setStyleSheet("color: white;")
            self.verbose_checkBox.setStyleSheet("color: white;")
            self.rali_checkBox.setStyleSheet("color: white;")
            self.level_label.setStyleSheet("color: white;")
            self.low_label.setStyleSheet("color: white;")
            self.high_label.setStyleSheet("color: white;")
            self.cpu_label.setStyleSheet("color: #C82327;")
            self.gpu_label.setStyleSheet("color: #C82327;")
            self.cpuName_label.setStyleSheet("color: white;")
            self.gpuName_label.setStyleSheet("color: white;")
            self.AMD_logo.setPixmap(self.AMD_Radeon_white_pixmap)
            if self.rali_checkBox.isChecked():
                self.MIVisionX_logo.setPixmap(self.rali_white_pixmap)
                self.graph_imageLabel.setPixmap(self.graph_image_white_pixmap)
            else:
                self.MIVisionX_logo.setPixmap(self.MIVisionX_white_pixmap)
            self.EPYC_logo.setPixmap(self.EPYC_white_pixmap)
            self.totalCurve.setData(x=self.x, y=self.y, pen=self.pen)
        else:
            self.setStyleSheet("background-color: white;")
            self.pen = pg.mkPen('k', width=4)
            self.graph.setBackground(None)
            self.origTitle_label.setStyleSheet("color: 0;")
            self.controlTitle_label.setStyleSheet("color: 0;")
            self.progTitle_label.setStyleSheet("color: 0;")
            self.graphTitle_label.setStyleSheet("color: 0;")
            self.augTitle_label.setStyleSheet("color: 0;")
            self.name_label.setStyleSheet("color: 0;")
            self.dataset_label.setStyleSheet("color: 0;")
            self.imgProg_label.setStyleSheet("color: 0;")
            self.fps_label.setStyleSheet("color: 0;")
            self.dark_checkBox.setStyleSheet("color: 0;")
            self.verbose_checkBox.setStyleSheet("color: 0;")
            self.rali_checkBox.setStyleSheet("color: 0;")
            self.level_label.setStyleSheet("color: 0;")
            self.low_label.setStyleSheet("color: 0;")
            self.high_label.setStyleSheet("color: 0;")
            self.cpu_label.setStyleSheet("color: 0;")
            self.gpu_label.setStyleSheet("color: 0;")
            self.cpuName_label.setStyleSheet("color: 0;")
            self.gpuName_label.setStyleSheet("color: 0;")
            self.AMD_logo.setPixmap(self.AMD_Radeon_pixmap)
            if self.rali_checkBox.isChecked():
                self.MIVisionX_logo.setPixmap(self.rali_pixmap)
                self.graph_imageLabel.setPixmap(self.graph_image_pixmap)
            else:
                self.MIVisionX_logo.setPixmap(self.MIVisionX_pixmap)
            self.EPYC_logo.setPixmap(self.EPYC_pixmap)
            self.totalCurve.setData(x=self.x, y=self.y, pen=self.pen)

    def showVerbose(self):
        if self.verbose_checkBox.isChecked():
            self.dataset_label.show()
            self.fps_label.show()
            self.fps_lcdNumber.show()
            self.legend.show()
            self.cpu_label.show()
            self.gpu_label.show()
            self.cpuName_label.show()
            self.gpuName_label.show()
        else:
            self.dataset_label.hide()
            self.fps_label.hide()
            self.fps_lcdNumber.hide()
            self.legend.hide()
            self.cpu_label.hide()
            self.gpu_label.hide()
            self.gpuName_label.hide()
            self.cpuName_label.hide()

    def showRALI(self):
        if self.rali_checkBox.isChecked():
            if self.dark_checkBox.isChecked():
                self.MIVisionX_logo.setPixmap(self.rali_white_pixmap)
                self.graph_imageLabel.setPixmap(self.graph_image_white_pixmap)
            else:
                self.MIVisionX_logo.setPixmap(self.rali_pixmap)
                self.graph_imageLabel.setPixmap(self.graph_image_pixmap)
            self.graph_imageLabel.show()
        else:
            if self.dark_checkBox.isChecked():
                self.MIVisionX_logo.setPixmap(self.MIVisionX_white_pixmap)
            else:
                self.MIVisionX_logo.setPixmap(self.MIVisionX_pixmap)
            self.graph_imageLabel.hide()

    def displayFPS(self):
        self.fps_lcdNumber.display(self.inferenceEngine.getFPS())

    def pauseView(self):
        self.pauseState = not self.pauseState
        if self.pauseState:
            self.totalElapsedTime += self.elapsedTime.elapsed() / 1000.0
            self.pause_pushButton.setText('Resume')
        else:
            self.elapsedTime.restart()
            self.pause_pushButton.setText('Pause')

        self.inferenceEngine.pauseInference()

    def terminate(self):
        self.inferenceEngine.terminate()
        self.receiver_thread.quit()
        for count in range(10):
            QThread.msleep(50)

        self.close()

    def closeEvent(self, event):
        self.terminate()
        exit(0)

    def setIntensity(self):
        augIntensity = (float)(self.level_slider.value()) / 100.0
        self.inferenceEngine.setIntensity(augIntensity)

    def calculateIndex(self, x, y):
        if self.batch_size_int == 64:
            imgWidth = self.aug_label.width() / 16.0
        else:
            imgWidth = self.aug_label.width() / 4.0
        imgHeight = self.aug_label.height() / 4.0
        x -= self.aug_label.x()
        y -= self.aug_label.y()
        column = (int)(x / imgWidth)
        row = (int)(y / imgHeight)
        index = 4 * column + row
        return index
예제 #22
0
class Tachy2Gis:
    """QGIS Plugin Implementation."""

    # Custom methods go here:

    def vertexReceived(self, line):
        newVtx = T2G_Vertex.fromGSI(line)
        self.mapTool.addVertex(vtx=newVtx)

    ## Clears the map canvas and in turn the vertexList
    def clearCanvas(self):
        self.mapTool.clear()

    ## Opens the field dialog in preparation of dumping new vertices to the target layer
    def dump(self):

        # the input table of the dialog is updated
        targetLayer = self.dlg.sourceLayerComboBox.currentLayer()
        # if the target layer holds point geometries, only the currently selected vertex is dumped and
        # removed from the list
        project = QgsProject.instance()
        QgsExpressionContextUtils.setProjectVariable(project,
                                                     'maxWerteAktualisieren',
                                                     'False')
        #QgsMessageLog.logMessage('Test', 'T2G Archäologie', Qgis.Info)
        if targetLayer.geometryType() == QgsWkbTypes.PointGeometry:  # Timmel
            for i in range(0, len(self.vertexList)):
                #QgsExpressionContextUtils.setProjectVariable(project, 'SignalGeometrieNeu', 'True')
                self.dlg.vertexTableView.selectRow(i)
                self.vertexList.dumpToFile(targetLayer,
                                           self.fieldDialog.fieldData)
                #QgsExpressionContextUtils.setProjectVariable(project, 'SignalGeometrieNeu', 'False')
        # self.mapTool.deleteVertex()
        # otherwise the list is cleared
        #self.mapTool.clear()
        else:
            #QgsExpressionContextUtils.setProjectVariable(project, 'SignalGeometrieNeu', 'True')
            self.vertexList.dumpToFile(targetLayer, self.fieldDialog.fieldData)
            #QgsExpressionContextUtils.setProjectVariable(project, 'SignalGeometrieNeu', 'False')
            # otherwise the list is cleared
            #self.mapTool.clear()

        #QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(), 'SignalGeometrieNeu', 'False')
        targetLayer.commitChanges()
        # Attributdialog mit Filter zum schreiben öffnen
        # letzte id ermitteln
        idfeld = targetLayer.dataProvider().fieldNameIndex('id')

        if targetLayer.maximumValue(idfeld) == None:
            idmaxi = 0
        else:
            idmaxi = targetLayer.maximumValue(idfeld)

        if targetLayer.geometryType() == QgsWkbTypes.PointGeometry:
            query = "id >= " + str(int(idmaxi) - len(self.vertexList) + 1)
        else:
            query = "id = " + str(int(idmaxi))

        self.messpunktAdd()
        targetLayer.startEditing()
        targetLayer.fields().at(
            0).constraints().constraintDescription(), 'desc'
        #self.iface.openFeatureForm(targetLayer, 3, True)
        box = self.iface.showAttributeTable(targetLayer, query)
        box.exec_()
        self.mapTool.clear()
        targetLayer.commitChanges()
        QgsExpressionContextUtils.setProjectVariable(project,
                                                     'maxWerteAktualisieren',
                                                     'True')
        self.iface.mapCanvas().refreshAllLayers()
        targetLayer.removeSelection()

    def messpunktAdd(self):
        #T2G_Vertex.messliste.clear()
        #T2G_Vertex.messliste.append({'pointId': 1, 'targetX': 1, 'targetY': 1, 'targetZ': 1})
        #T2G_Vertex.messliste.append({'pointId': 2, 'targetX': 1, 'targetY': 1, 'targetZ': 1})
        #T2G_Vertex.messliste.append({'pointId': 3, 'targetX': 1, 'targetY': 1, 'targetZ': 1})
        layer = QgsProject.instance().mapLayersByName('Messpunkte')[0]
        layer.startEditing()
        for item in T2G_Vertex.messliste:
            ptnr = str(item['pointId']).lstrip("0")
            x = str(item['targetX'])
            y = str(item['targetY'])
            z = str(item['targetZ'])
            # Timmel Spiegelhöhe
            try:
                project = QgsProject.instance()
                reflH = QgsExpressionContextUtils.projectScope(
                    project).variable('reflH')
                reflH = float(reflH)
                z = round(float(z) - reflH, 3)
            except:
                pass

            pt = QgsPoint(float(x), float(y), float(z))
            attL = {'ptnr': ptnr, 'x': x, 'y': y, 'z': z, 'reflH': reflH}
            self.addPoint3D(layer, pt, attL)
            QgsMessageLog.logMessage(
                str(ptnr) + '|' + str(x) + '|' + str(y) + '|' + str(z),
                'Messpunkte', Qgis.Info)
        layer.commitChanges()
        layer.removeSelection()

    def addPoint3D(self, layer, point, attListe):
        #layer.startEditing()
        feature = QgsFeature()
        fields = layer.fields()
        feature.setFields(fields)
        feature.setGeometry(QgsGeometry(point))
        # Attribute
        layer.dataProvider().addFeatures([feature])
        layer.updateExtents()
        features = [feature for feature in layer.getFeatures()]
        lastfeature = features[-1]

        for item in attListe:
            #QgsMessageLog.logMessage(str(item), 'T2G', Qgis.Info)
            fIndex = layer.dataProvider().fieldNameIndex(item)
            layer.changeAttributeValue(lastfeature.id(), fIndex,
                                       attListe[item])
        T2G_VertexList.addStaticAttribut(self, layer, lastfeature)
        #layer.commitChanges()

    def snap(self):
        if self.dlg.checkBox.isChecked():
            T2G_VertexList.snap = 1
        else:
            T2G_VertexList.snap = 0

    ## Restores the map tool to the one that was active before T2G was started
    #  The pan tool is the default tool used by QGIS
    def restoreTool(self):
        if self.previousTool is None:
            self.previousTool = QgsMapToolPan(self.iface.mapCanvas())
        self.iface.mapCanvas().setMapTool(self.previousTool)
        self.iface.actionSelectRectangle().trigger()

    def setActiveLayer(self):  #Timmel
        if Qt is None:
            return
        activeLayer = self.dlg.sourceLayerComboBox.currentLayer()
        if activeLayer is None or activeLayer.type(
        ) == QgsMapLayer.RasterLayer:
            return
        self.iface.setActiveLayer(activeLayer)
        self.vertexList.updateAnchors(activeLayer)

    def targetChanged(self):
        targetLayer = self.fieldDialog.targetLayerComboBox.setLayer  # Timmel
        self.mapTool.setGeometryType(targetLayer)

    def toggleEdit(self):
        iface.actionToggleEditing().trigger()

    def connectSerial(self):
        port = self.dlg.portComboBox.currentText()
        self.tachyReader.setPort(port)

    def setLog(self):
        logFileName = QFileDialog.getSaveFileName()[0]
        self.dlg.logFileEdit.setText(logFileName)
        self.tachyReader.setLogfile(logFileName)

    def dumpEnabled(self):
        verticesAvailable = (len(self.vertexList) > 0
                             )  # tim > durch >= ersetzt
        # Selecting a target layer while there are no vertices in the vertex list may cause segfaults. To avoid this,
        # the 'Dump' button is disabled as long there are none:
        self.dlg.dumpButton.setEnabled(verticesAvailable)

    # Interface code goes here:
    def setupControls(self):
        """This method connects all controls in the UI to their callbacks.
        It is called in ad_action"""

        portNames = [
            port.portName() for port in QSerialPortInfo.availablePorts()
        ]
        self.dlg.portComboBox.addItems(portNames)
        self.dlg.portComboBox.currentIndexChanged.connect(self.connectSerial)

        self.dlg.logFileButton.clicked.connect(self.setLog)

        self.dlg.deleteAllButton.clicked.connect(self.clearCanvas)
        self.dlg.finished.connect(self.mapTool.clear)
        self.dlg.dumpButton.clicked.connect(self.dump)
        self.dlg.deleteVertexButton.clicked.connect(self.mapTool.deleteVertex)

        self.dlg.vertexTableView.setModel(self.vertexList)
        self.dlg.vertexTableView.horizontalHeader().setSectionResizeMode(
            0, QHeaderView.Stretch)
        self.dlg.vertexTableView.setSelectionModel(
            QItemSelectionModel(self.vertexList))
        self.dlg.vertexTableView.selectionModel().selectionChanged.connect(
            self.mapTool.selectVertex)

        self.dlg.finished.connect(self.restoreTool)
        self.dlg.accepted.connect(self.restoreTool)
        self.dlg.rejected.connect(self.restoreTool)

        self.dlg.sourceLayerComboBox.layerChanged.connect(self.setActiveLayer)
        self.dlg.sourceLayerComboBox.layerChanged.connect(self.mapTool.clear)

        self.fieldDialog.targetLayerComboBox.layerChanged.connect(
            self.targetChanged)
        self.vertexList.layoutChanged.connect(self.dumpEnabled)

        self.dlg.checkBox.stateChanged.connect(self.snap)

    ## Constructor
    #  @param iface An interface instance that will be passed to this class
    #  which provides the hook by which you can manipulate the QGIS
    #  application at run time.
    def __init__(self, iface):
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(self.plugin_dir, 'i18n',
                                   'Tachy2Gis_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr('&Tachy2GIS')
        self.toolbar = self.iface.addToolBar('Tachy2Gis')
        self.toolbar.setObjectName('Tachy2Gis')

        ## From here: Own additions
        self.vertexList = T2G_VertexList()

        self.mapTool = T2G_VertexePickerTool(self)
        self.previousTool = None
        self.fieldDialog = FieldDialog(self.iface.activeLayer())
        self.tachyReader = TachyReader(QSerialPort.Baud9600)
        self.pollingThread = QThread()
        self.tachyReader.moveToThread(self.pollingThread)
        self.pollingThread.start()
        self.tachyReader.lineReceived.connect(self.vertexReceived)
        self.tachyReader.beginListening()

    # noinspection PyMethodMayBeStatic

    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('Tachy2Gis', message)

    def add_action(self,
                   icon_path,
                   text,
                   callback,
                   enabled_flag=True,
                   add_to_menu=True,
                   add_to_toolbar=True,
                   status_tip=None,
                   whats_this=None,
                   parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        # Create the dialog (after translation) and keep reference
        self.dlg = Tachy2GisDialog(
            iface.mainWindow())  # Dialog im Vordergrund halten.
        self.setupControls()

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.iface.addToolBarIcon(action)

        if add_to_menu:
            self.iface.addPluginToMenu(self.menu, action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/Tachy2Gis/icon.png'
        self.add_action(icon_path,
                        text=self.tr('Tachy2GIS'),
                        callback=self.run,
                        parent=self.iface.mainWindow())

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(self.tr('&Tachy2GIS'), action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar
        if self.pollingThread.isRunning():
            self.tachyReader.shutDown()
            self.pollingThread.terminate()
            self.pollingThread.wait()

    def run(self):
        """Run method that performs all the real work"""
        # Store the active map tool and switch to the T2G_VertexPickerTool
        self.previousTool = self.iface.mapCanvas().mapTool()
        self.iface.mapCanvas().setMapTool(self.mapTool)
        self.mapTool.alive = True
        #self.setActiveLayer()
        self.dlg.sourceLayerComboBox.setLayer(self.iface.activeLayer())
        self.dlg.show()

        outLayerList = []
        self.layerLine = QgsProject.instance().mapLayersByName('E_Line')[0]
        self.layerPoly = QgsProject.instance().mapLayersByName('E_Polygon')[0]
        self.layerPoint = QgsProject.instance().mapLayersByName('E_Point')[0]
        for lay in QgsProject.instance().mapLayers().values():
            if lay == self.layerLine or lay == self.layerPoly or lay == self.layerPoint:
                QgsMessageLog.logMessage('gleich', 'T2G Archäologie',
                                         Qgis.Info)
                pass
            else:
                QgsMessageLog.logMessage(lay.name(), 'T2G Archäologie',
                                         Qgis.Info)
                outLayerList.append(lay)
        self.dlg.sourceLayerComboBox.setExceptedLayerList(outLayerList)
예제 #23
0
class T2G_VertexList(QAbstractTableModel):
    ## The color of unselected vertice
    VERTEX_COLOR = Qt.red
    ### Selected vertices get a different color
    SELECTED_COLOR = Qt.green
    signal_feature_dumped = pyqtSignal()

    ## Ctor
    #  @param vertices the vertex list can be initialized with a list of vertices
    #  @param parent included to match the ctor of QAbstractTableModel
    #  @param args see above
    def __init__(self, vertices=[], parent=None, *args):
        QAbstractTableModel.__init__(self, parent, *args)
        self.columnCount = len(T2G_Vertex().fields())
        self.vertices = vertices
        self.colors = []
        self.shapes = []
        self.anchorPoints = []
        self.anchorIndex = QgsSpatialIndex()
        self.selected = None
        self.maxIndex = None
        self.updateThread = QThread()
        self.anchorUpdater = None
        self.layer = None

    ## Reimplemented from QAbstractTableModel
    def rowCount(self, *args, **kwargs):
        return len(self)

    ## Reimplemented from QAbstractTableModel
    def columnCount(self, *args, **kwargs):
        return self.columnCount

    ## Reimplemented from QAbstractTableModel
    def data(self, index, role):
        if Qt is None:
            return
        if not index.isValid():
            return
        elif role != Qt.DisplayRole:
            return
        row = index.row()
        col = index.column()
        vertex = self.vertices[row]
        field = vertex.fields()[col]
        return field

    ## Reimplemented from QAbstractTableModel
    def headerData(self, section, orientation, role):
        if Qt is None:
            return
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            headers = T2G_Vertex.HEADERS
            return headers[section]
        return QAbstractTableModel.headerData(self, section, orientation, role)

    ## this method updates anchor points for snapping
    #  extracting all vertices from the geometries in a layer may take some time,
    #  so the method reports its progress via a dialog that allows aborting the
    #  operation when it takes too long.
    #  @param layer: The currently active layer
    def updateAnchors(self, layer):
        #  Snapping is driven by a spatial index of all distinct existing vertices
        #  the index is 2D only, so 3-4D information has to be stored elsewhere
        #  (self.anchorPoints in this case).
        #  self.anchorPoints holds wkt representation of all vertices that can be
        #  passed to the ctor of a new T2G_Vertex.
        self.anchorIndex = QgsSpatialIndex()
        self.anchorPoints = []
        if layer is None:
            # empty or nonexisting layers leave us with an empty point list and index.
            return
        self.layer = layer
        # Initializing the progress dialog
        aud = AnchorUpdateDialog.AnchorUpdateDialog()
        aud.abortButton.clicked.connect(self.abortUpdate)
        aud.geometriesBar.setMaximum(layer.featureCount())
        aud.geometriesBar.setValue(0)
        aud.anchorBar.setValue(0)
        # the layer is passed to the anchorUpdater and the updater is moved to
        # a new thread
        self.anchorUpdater = AnchorUpdater(layer=layer)
        self.anchorUpdater.moveToThread(self.updateThread)
        self.updateThread.start()
        self.anchorUpdater.signalAnchorCount.connect(aud.setAnchorCount)
        self.anchorUpdater.signalAnchorProgress.connect(aud.anchorProgress)
        self.anchorUpdater.signalGeometriesProgress.connect(
            aud.geometriesProgress)
        # the dialog is displayed and extraction is started
        aud.show()
        self.anchorUpdater.startExtraction()
        # the abort method of the updater clears its index and points list, so
        # we can just use them, even if we aborted. They will be empty in that
        # case.
        self.anchorIndex = self.anchorUpdater.anchorIndex
        self.anchorPoints = self.anchorUpdater.anchorPoints

    ## checks if there are anchors
    #  @return bool
    def hasAnchors(self):
        return len(self.anchorPoints) > 0

    ## Tells the update thread to stop and cleans up after it
    @pyqtSlot()
    def abortUpdate(self):
        if self.updateThread.isRunning():
            # abortExtraction lets the process exit the extraction loop and
            # resets its anchorIndex and anchorPoints
            self.anchorUpdater.abortExtraction()
            self.updateThread.terminate()
            self.updateThread.wait()

    ## makes the vertexList look more like a regular list
    #  @return an int representing the number of vertices on the list
    def __len__(self):
        return self.vertices.__len__()

    ## allows to access vertices by index: 'vertexList[2]' works as expected.
    #  @param index the index of the requested vertex
    #  @return the T2G_Vertex at 'index'
    def __getitem__(self, index):
        return self.vertices.__getitem__(index)

    ## Allows appending new vertices. Manually created vertices will be snapped
    #  to the nearest anchor.
    #  @param vertex the vertex to append, is assumed to be of type T2G_Vertex
    #  @return the probably modified vertex
    def append(self, vertex):
        if vertex.source == T2G_Vertex.SOURCE_INTERNAL:
            anchorId = self.anchorIndex.nearestNeighbor(
                vertex.getQgsPointXY(), 1)
            # nearestNeighbour returns a list. It is not unpacked yet, because
            # doing so causes errors if it is empty, also index '0' is interpreted
            # as 'False' and gets ignored
            if anchorId:
                wkt = self.anchorPoints[anchorId[0]]
                vertex.setWkt(wkt)
        self.vertices.append(vertex)
        self.layoutChanged.emit()
        return vertex

    ## removes a vertex and emits a signal to update the tableView
    def deleteVertex(self, index):
        del self.vertices[index]
        self.layoutChanged.emit()

    def select(self, index):
        if index >= len(self):
            return
        self.selected = index

    def clearSelection(self):
        self.selected = None

    ## This method is used to get colors for vertex markers that let you
    #  distinguish between selected and unselected vertices
    #  @return a list of QColors
    def getColors(self):
        colors = []
        for i, vertex in enumerate(self.vertices):
            if i == self.selected:
                colors.append(self.SELECTED_COLOR)
            else:
                colors.append(self.VERTEX_COLOR)
        return colors

    ## Deletes all vertices and emits a signal to update the tableView
    def clear(self):
        self.vertices = []
        self.layoutChanged.emit()

    ## returns the coordinates of all vertices in a format that is useful for
    #  shapefile.writer
    #  @return a double nested list of coordinates.
    def getParts(self):
        return [[v.getCoords() for v in self.vertices]]

    ## Turns the vertices into geometry and writes them to a shapefile
    #  @param targetLayer a vectordatalayer that is suspected to be based on a
    #  shapefile (required to do so, actually)
    #  @param fieldData the attributes of the new feature as list
    def dumpToFile(self, targetLayer, fieldData):
        # nonexistant and non-shapefile layers will be ignored
        if targetLayer is None:
            return
        if not targetLayer.dataProvider().name() == 'ogr':
            return
        # the absolute path to the shapefile is extracted from its URI
        dataUri = targetLayer.dataProvider().dataSourceUri()
        targetFileName = os.path.splitext(dataUri.split('|')[0])[0]
        add_shape(targetFileName, self.getParts(), fieldData)
        self.signal_feature_dumped.emit()

    def get_qgs_points(self):
        return [vertex.getQgsPointXY() for vertex in self.vertices]
예제 #24
0
파일: qtcodegen.py 프로젝트: orSpec/vaccipy
class QtCodeGen(QtWidgets.QDialog):
    # Folgende Widgets stehen zur Verfügung:
    def __init__(self, kontaktdaten: dict, ROOT_PATH: str, parent = None):
        super().__init__(parent)
        uic.loadUi(os.path.join(PATH, "ui_qtcodegen.ui"), self)
        self.setupUi(self, ROOT_PATH)
        
        self.parent = parent
        self._hardClose = False
        
        # Attribute erstellen
        self.kontaktdaten = kontaktdaten
        self.ROOT_PATH = ROOT_PATH

        # std.out & error auf das Textfeld umleiten
        sys.stdout = EigenerStream(text_schreiben=self.update_ausgabe)
        sys.stderr = EigenerStream(text_schreiben=self.update_ausgabe)

        # Entsprechend Konfigurieren
        self.setup_thread()
        self.thread.start()
        
        # show gui
        self.show()
  
    def __del__(self):
        print("QtCodeGen destruct")
        
    def setupUi(self, QtCodeGen, ROOT_PATH):
        self.setObjectName("QtCodeGen")
        self.setWindowModality(QtCore.Qt.WindowModal)
        self.setModal(False)
        self.resize(700, 300)
        self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint);
        self.setWindowIcon(QIcon(os.path.join(ROOT_PATH, "images/spritze.ico")))

    def setup_thread(self):
        """
        Thread + Worker erstellen und Konfigurieren
        """
        self.thread = QThread(parent=self)
        self.thread.setTerminationEnabled(True)
        self.worker = Worker(self.kontaktdaten, self.ROOT_PATH)
        
        # Worker und Thread verbinden
        self.worker.moveToThread(self.thread)
        
        # Signale setzen
        self.thread.started.connect(self.worker.code_gen)
        self.worker.signalShowInput.connect(self.showInputDlg)
        self.worker.signalShowDlg.connect(self.showDlg)


    def update_ausgabe(self, text):
        """
        Fügt den übergeben Text dem textAusgabe hinzu

        Args:
            text (str): Text welcher hinzukommen soll
        """

        # Austausch der Farbcodes / Ascii Zeichen aus der Shell
        listeCodes = ['\033[95m', '\033[91m', '\033[33m', '\x1b[0m', '\033[94m', '\033[32m', '\033[0m']
        for farbcode in listeCodes:
            if farbcode in text:
                if farbcode == '\033[95m' or farbcode == '\033[91m':
                    text = f"<div style='color:red'>{text}</div>"
                elif farbcode == '\033[33m':
                    text = f"<div style='color:orange'>{text}</div>"
                elif farbcode == '\x1b[0m':
                    text = f"<div>{text}</div>"
                elif farbcode == '\033[94m':
                    text = f"<div style='color:blue'>{text}</div>"
                elif farbcode == '\033[32m':
                    text = f"<div style='color:green'>{text}</div>"
                text = text.replace(farbcode, '')

        cursor = self.textAusgabe.textCursor()
        cursor.movePosition(QtGui.QTextCursor.End)
        cursor.insertHtml(str(text))
        cursor.insertText(str("\n"))
        self.textAusgabe.setTextCursor(cursor)
        self.textAusgabe.ensureCursorVisible()

    def showInputDlg(self, dlgType):
        
        if dlgType == "GEBURTSDATUM":
           while True:
                try:
                    text, ok = QtWidgets.QInputDialog.getText(self, 'Geburtsdatum', 'Bitte trage nachfolgend dein Geburtsdatum im Format DD.MM.YYYY ein.\n'
                        'Beispiel: 02.03.1982\n')
                    if ok:
                        geburtsdatum = str(text)
                        validate_datum(geburtsdatum)
                        self.worker.signalUpdateData.emit("GEBURTSDATUM",geburtsdatum)
                        break
                    else:
                        self.hardClose()
                        break
                except ValidationError as exc:
                    QtWidgets.QMessageBox.critical(self, "Geburtsdatum ungültiges Format", "Das Datum entspricht nicht dem richtigen Format (DD.MM.YYYY).")
 
        elif dlgType == "SMSCODE_OK":
            ret = QtWidgets.QMessageBox.information(self, "Erfolgreich", "Code erfolgreich generiert. Du kannst jetzt mit der Terminsuche fortfahren.",QMessageBox.StandardButton.Ok)
            if ret == QMessageBox.StandardButton.Ok:
                self.worker.signalUpdateData.emit("SMSCODE_OK","")
                self.hardClose()
            
    def showDlg(self, strMode, strTxt):
        if strMode == "MISSING_KONTAKT":
            ret = QtWidgets.QMessageBox.critical(self, "Kontaktdaten ungültig",
                "Die Kontakdaten sind nicht korrekt!.\n\nBitte Datei neu erstellen!", QMessageBox.StandardButton.Ok)
            if ret == QMessageBox.StandardButton.Ok:
                self.hardClose()
        elif strMode == "CRITICAL_CLOSE":
            ret = QtWidgets.QMessageBox.critical(self, "Error", strTxt, QMessageBox.StandardButton.Ok)
            if ret == QMessageBox.StandardButton.Ok:
                self.hardClose()

                
    # force to close the dialog without confirmation
    def hardClose(self):
        self._hardClose = True
        self.close()
         
    def closeEvent(self, event):
        """
        Wird aufgerufen, wenn die Anwendung geschlossen wird
        """

        if self.thread.isRunning():
            if self._hardClose is False:
                res = QtWidgets.QMessageBox.warning(self, "Suche beenden", "Suche wirklich beenden?\n",
                                                    (QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Cancel))

                if res != QMessageBox.StandardButton.Ok:
                    event.ignore()
                    return
                
            
        #exit

        #stop worker
        self.worker.stop()
        self.worker.deleteLater()

        #stop thread
        self.thread.quit()
        self.thread.wait(3000)
        self.thread.terminate()

        # Streams wieder korrigieren, damit kein Fehler kommt
        sys.stdout = sys.__stdout__
        sys.stderr = sys.__stderr__

        self.deleteLater()
        event.accept()

    @staticmethod
    def start_code_gen(kontaktdaten: dict,  ROOT_PATH: str):
        app = QtWidgets.QApplication(list())
        window = QtCodeGen(kontaktdaten, ROOT_PATH)
        app.exec_()
예제 #25
0
class MainWindow(QMainWindow, gui.Ui_MainWindow):
    def __init__(self):
        super(self.__class__, self).__init__()
        self.setupUi(self)

        self.thread = QThread()

        self.tests = []
        self.filelist = []
        self.filename = os.path.expanduser('~/Documents/FTPDownloaderDefault.pickle')

        if os.path.isfile(self.filename):
            with open(self.filename, 'rb') as f:
                self.tests, self.filelist, self.loadIP, self.loadPass, self.loadReq, self.loadUser = pickle.load(f)

            self.lineIP.setText(self.loadIP)
            self.lineUser.setText(self.loadUser)
            self.linePass.setText(self.loadPass)
            self.lineReq.setText(self.loadReq)

        self.load_archivos()
        self.load_casos()

        self.downloader = Downloader(self.lineIP.text(), self.lineUser.text(), self.linePass.text(),
                                     self.lineReq.text(), self.filelist, self.tests)
        self.downloader.moveToThread(self.thread)
        self.downloader.log.connect(self.print_log)
        self.downloader.progress.connect(self.descargado)
        self.thread.started.connect(self.downloader.prepara_descarga)
        self.downloader.finished.connect(self.terminado)

        self.actionNuevo.triggered.connect(self.reset_all)
        self.actionLoad.triggered.connect(self.load_template)
        self.actionSave.triggered.connect(self.save_as)
        self.actionCerrar.triggered.connect(sys.exit)

        self.pushAddPrueba.clicked.connect(self.add_caso)
        self.lineCaso.returnPressed.connect(self.add_caso)
        self.pushAddArchivo.clicked.connect(self.add_archivo)
        self.lineNombreMainframe.returnPressed.connect(self.add_archivo)
        self.pushCargarArchivos.clicked.connect(self.load_list)
        self.pushClearArchivos.clicked.connect(self.clear_archivos)
        self.pushClearPruebas.clicked.connect(self.clear_pruebas)
        self.pushDeletePrueba.clicked.connect(self.borrar_caso)
        self.pushDeleteArchivo.clicked.connect(self.borrar_archivo)
        self.pushRenamePrueba.clicked.connect(self.renombrar_caso)
        self.pushRenameArchivo.clicked.connect(self.renombrar_archivo)

        self.pushDownload.clicked.connect(self.start_downloads)
        self.pushStop.clicked.connect(self.stop_process)

        self.pushButton.clicked.connect(self.about)

        atexit.register(self.save_state)

    def save_as(self):
        filename = QFileDialog.getSaveFileName(QFileDialog(), 'Guardar como', os.path.expanduser('~/Documents/'),
                                               '*.pickle')

        if filename[0]:
            save_ip = self.lineIP.text()
            save_user = self.lineUser.text()
            save_pass = self.linePass.text()
            save_req = self.lineReq.text()

            with open(filename[0] + '.pickle', 'wb') as s:
                pickle.dump([self.tests, self.filelist, save_ip, save_pass, save_req, save_user], s)

            self.print_log('Se guardo la plantilla en {}'.format(filename[0]))

    def load_template(self):

        filename = QFileDialog.getOpenFileName(QFileDialog(), 'Abrir', os.path.expanduser('~/Documents/'), '*.pickle')
        if filename[0]:
            with open(filename[0], 'rb') as f:
                self.tests, self.filelist, self.loadIP, self.loadPass, self.loadReq, self.loadUser = pickle.load(f)

            self.lineIP.setText(self.loadIP)
            self.lineUser.setText(self.loadUser)
            self.linePass.setText(self.loadPass)
            self.lineReq.setText(self.loadReq)
            self.load_archivos()
            self.load_casos()

            self.print_log('Se cargo la planitlla {}'.format(filename[0]))

    def load_list(self):
        filename = QFileDialog.getOpenFileName(QFileDialog(), 'Abrir')

        if filename[0]:
            with open(filename[0], 'r') as f:
                files = f.read().splitlines()

            for i in files:
                self.filelist.append(('', i.upper(), False))
                self.load_archivos()

    def print_log(self, message):
        log = '[{}] {}'.format(strftime("%H:%M:%S", localtime()), message)
        self.plainTextLog.appendPlainText(log)

    def load_casos(self):
        self.listPruebas.clear()
        for caso in self.tests:
            self.listPruebas.addItem(caso.capitalize())

    def load_archivos(self):
        self.listArchivos.clear()
        for item in self.filelist:
            self.listArchivos.addItem(
                '{} - {}   - {}'.format(item[0], item[1].upper(), 'Tx' if item[2] == True else 'Único'))

    def add_caso(self):
        test = self.lineCaso.text()

        if test:
            self.tests.append(test.capitalize())
            self.lineCaso.clear()
            self.load_casos()
        else:
            self.print_log('Campo Caso vacio.')

    def add_archivo(self):
        mainframe = self.lineNombreMainframe.text()
        nombre = self.lineNombre.text()
        tx = True if self.checkTx.isChecked() else False

        if mainframe:
            if not nombre:
                self.print_log('Nombre vacio. Se nombrara como archivo en mainframe.')
            self.filelist.append((nombre, mainframe.upper(), tx))
            self.lineNombreMainframe.clear()
            self.lineNombre.clear()
            self.load_archivos()
        else:
            self.print_log('Nombre en Mainframe vacio.')

    def clear_archivos(self):
        confirm = self.del_confirmation('Reset', 'Desea borrar todos los archivos?')

        if confirm == gui.QtWidgets.QMessageBox.Yes:
            self.filelist.clear()
            self.listArchivos.clear()
            self.load_archivos()

    def clear_pruebas(self):
        confirm = self.del_confirmation('Reset', 'Desea borrar todas las pruebas?')

        if confirm == gui.QtWidgets.QMessageBox.Yes:
            self.tests.clear()
            self.listPruebas.clear()
            self.load_casos()

    def borrar_caso(self):
        index = self.listPruebas.currentRow()
        if index == -1:
            self.print_log('Seleccionar item.')
        else:
            self.tests.pop()
            self.load_casos()

    def borrar_archivo(self):
        index = self.listArchivos.currentRow()
        if index == -1:
            self.print_log('Seleccionar item.')
        else:
            self.filelist.pop(index)
            self.load_archivos()

    def renombrar_caso(self):
        new_name = self.lineCaso.text()
        index = self.listPruebas.currentRow()

        if not new_name:
            self.print_log('Ingresar nombre nuevo')
        elif index == -1:
            self.print_log('Seleccionar item.')
        else:
            self.tests[index] = new_name
            self.lineCaso.clear()
            self.load_casos()

    def renombrar_archivo(self):
        index = self.listArchivos.currentRow()

        new_name = self.lineNombre.text()
        new_mainframe_name = self.lineNombreMainframe.text()
        new_state = True if self.checkTx.isChecked() else False

        if not new_mainframe_name:
            self.print_log('Ingresar nombre nuevo.')
        elif index == -1:
            self.print_log('Seleccionar item.')
        else:
            self.filelist[index] = (new_name, new_mainframe_name, new_state)
            self.lineNombre.clear()
            self.lineNombreMainframe.clear()
            self.load_archivos()

    def reset_all(self):
        confirm = self.del_confirmation('Reset', 'Se borrarán todos los datos,\n'
                                            'excepto la info de login.\n'
                                            'Desea continuar?')
        if confirm == QMessageBox.Yes:
            self.clear_archivos()
            self.clear_pruebas()
            self.lineReq.clear()
            self.lineCaso.clear()
            self.lineNombreMainframe.clear()
            self.lineNombre.clear()
            self.checkTx.setCheckState(0)
            self.plainTextLog.clear()

    def save_state(self):
        save_ip = self.lineIP.text()
        save_user = self.lineUser.text()
        save_pass = self.linePass.text()
        save_req = self.lineReq.text()

        with open(self.filename, 'wb') as s:
            pickle.dump([self.tests, self.filelist, save_ip, save_pass, save_req, save_user], s)

    def start_downloads(self):

        progress_max = 100

        if not self.thread.isRunning():
            if len(self.tests) == 0:
                progress_max = len(self.filelist)
            else:
                progress_max = len(self.tests) * len(self.filelist)
            self.progressBar.setMaximum(progress_max)
            print(progress_max)
            print(self.progressBar.value())
            self.progressBar.setValue(0)
            self.thread.start()
        else:
            self.print_log('Hay una descarga en proceso. Cancelar o reintentar al finalizar.')

    def terminado(self):
        self.thread.terminate()
        self.progressBar.setValue(len(self.tests) * len(self.filelist))

    def stop_process(self):
        if self.thread.isRunning():
            self.downloader.cancelar.emit()
        else:
            self.print_log('No hay descargas en curso.')

    def descargado(self):
        self.progressBar.setValue(self.progressBar.value() + 1)
        print(self.progressBar.value())

    def about(self):
        QMessageBox.information(self, 'About', '  \'FTPDownloader 2016\' \n        Ignacio Freire',
                                QMessageBox.Ok)

    def del_confirmation(self, title, message):
        choice = QMessageBox.question(self, title, message, QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        return choice
예제 #26
0
class TVLinker(QWidget):
    def __init__(self, settings: QSettings, parent=None):
        super(TVLinker, self).__init__(parent)
        self.firstrun = True
        self.rows, self.cols = 0, 0
        self.parent = parent
        self.settings = settings
        self.taskbar = TaskbarProgress(self)
        self.init_styles()
        self.init_settings()
        self.init_icons()
        if sys.platform.startswith('linux'):
            notify.init(qApp.applicationName())
        layout = QVBoxLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(15, 15, 15, 0)
        form_groupbox = QGroupBox(self, objectName='mainForm')
        form_groupbox.setLayout(self.init_form())
        self.table = TVLinkerTable(0, 4, self)
        self.table.doubleClicked.connect(self.show_hosters)
        layout.addWidget(form_groupbox)
        layout.addWidget(self.table)
        layout.addLayout(self.init_metabar())
        self.setLayout(layout)
        qApp.setWindowIcon(self.icon_app)
        self.resize(FixedSettings.windowSize)
        self.show()
        self.start_scraping()
        self.firstrun = False

    class ProcError(Enum):
        FAILED_TO_START = 0
        CRASHED = 1
        TIMED_OUT = 2
        READ_ERROR = 3
        WRITE_ERROR = 4
        UNKNOWN_ERROR = 5

    class NotifyIcon(Enum):
        SUCCESS = ':assets/images/tvlinker.png'
        DEFAULT = ':assets/images/tvlinker.png'

    def init_threads(self, threadtype: str = 'scrape') -> None:
        if threadtype == 'scrape':
            if hasattr(self, 'scrapeThread'):
                if not sip.isdeleted(
                        self.scrapeThread) and self.scrapeThread.isRunning():
                    self.scrapeThread.terminate()
                    del self.scrapeWorker
                    del self.scrapeThread
            self.scrapeThread = QThread(self)
            self.scrapeWorker = ScrapeWorker(self.source_url, self.user_agent,
                                             self.dl_pagecount)
            self.scrapeThread.started.connect(self.show_progress)
            self.scrapeThread.started.connect(self.scrapeWorker.begin)
            self.scrapeWorker.moveToThread(self.scrapeThread)
            self.scrapeWorker.addRow.connect(self.add_row)
            self.scrapeWorker.workFinished.connect(self.scrape_finished)
            self.scrapeWorker.workFinished.connect(
                self.scrapeWorker.deleteLater, Qt.DirectConnection)
            self.scrapeWorker.workFinished.connect(self.scrapeThread.quit,
                                                   Qt.DirectConnection)
            self.scrapeThread.finished.connect(self.scrapeThread.deleteLater,
                                               Qt.DirectConnection)
        elif threadtype == 'unrestrict':
            pass

    @staticmethod
    def load_stylesheet(qssfile: str) -> None:
        if QFileInfo(qssfile).exists():
            qss = QFile(qssfile)
            qss.open(QFile.ReadOnly | QFile.Text)
            qApp.setStyleSheet(QTextStream(qss).readAll())

    def init_styles(self) -> None:
        if sys.platform == 'darwin':
            qss_stylesheet = self.get_path('%s_osx.qss' %
                                           qApp.applicationName().lower())
        else:
            qss_stylesheet = self.get_path('%s.qss' %
                                           qApp.applicationName().lower())
        TVLinker.load_stylesheet(qss_stylesheet)
        QFontDatabase.addApplicationFont(':assets/fonts/opensans.ttf')
        QFontDatabase.addApplicationFont(':assets/fonts/opensans-bold.ttf')
        QFontDatabase.addApplicationFont(':assets/fonts/opensans-semibold.ttf')
        qApp.setFont(QFont('Open Sans',
                           12 if sys.platform == 'darwin' else 10))

    def init_icons(self) -> None:
        self.icon_app = QIcon(
            self.get_path('images/%s.png' % qApp.applicationName().lower()))
        self.icon_faves_off = QIcon(':assets/images/star_off.png')
        self.icon_faves_on = QIcon(':assets/images/star_on.png')
        self.icon_refresh = QIcon(':assets/images/refresh.png')
        self.icon_menu = QIcon(':assets/images/menu.png')
        self.icon_settings = QIcon(':assets/images/cog.png')
        self.icon_updates = QIcon(':assets/images/cloud.png')

    def init_settings(self) -> None:
        self.provider = 'Scene-RLS'
        self.select_provider(0)
        self.user_agent = self.settings.value('user_agent')
        self.dl_pagecount = self.settings.value('dl_pagecount', 20, int)
        self.dl_pagelinks = FixedSettings.linksPerPage
        self.realdebrid_api_token = self.settings.value('realdebrid_apitoken')
        self.realdebrid_api_proxy = self.settings.value('realdebrid_apiproxy')
        self.download_manager = self.settings.value('download_manager')
        self.persepolis_cmd = self.settings.value('persepolis_cmd')
        self.pyload_host = self.settings.value('pyload_host')
        self.pyload_username = self.settings.value('pyload_username')
        self.pyload_password = self.settings.value('pyload_password')
        self.idm_exe_path = self.settings.value('idm_exe_path')
        self.kget_cmd = self.settings.value('kget_cmd')
        self.favorites = self.settings.value('favorites')

    def init_form(self) -> QHBoxLayout:
        self.search_field = QLineEdit(self,
                                      clearButtonEnabled=True,
                                      placeholderText='Enter search criteria')
        self.search_field.setObjectName('searchInput')
        self.search_field.setSizePolicy(QSizePolicy.Expanding,
                                        QSizePolicy.Fixed)
        self.search_field.setFocus()
        self.search_field.textChanged.connect(self.clear_filters)
        self.search_field.returnPressed.connect(
            lambda: self.filter_table(self.search_field.text()))
        self.favorites_button = QPushButton(parent=self,
                                            flat=True,
                                            cursor=Qt.PointingHandCursor,
                                            objectName='favesButton',
                                            toolTip='Favorites',
                                            checkable=True,
                                            toggled=self.filter_faves,
                                            checked=self.settings.value(
                                                'faves_filter', False, bool))
        self.refresh_button = QPushButton(parent=self,
                                          flat=True,
                                          cursor=Qt.PointingHandCursor,
                                          objectName='refreshButton',
                                          toolTip='Refresh',
                                          clicked=self.start_scraping)
        self.dlpages_field = QComboBox(self,
                                       toolTip='Pages',
                                       editable=False,
                                       cursor=Qt.PointingHandCursor)
        self.dlpages_field.addItems(
            ('10', '20', '30', '40', '50', '60', '70', '80'))
        self.dlpages_field.setCurrentIndex(
            self.dlpages_field.findText(str(self.dl_pagecount),
                                        Qt.MatchFixedString))
        self.dlpages_field.currentIndexChanged.connect(self.update_pagecount)
        self.settings_button = QPushButton(parent=self,
                                           flat=True,
                                           toolTip='Menu',
                                           objectName='menuButton',
                                           cursor=Qt.PointingHandCursor)
        self.settings_button.setMenu(self.settings_menu())
        layout = QHBoxLayout(spacing=10)
        # providerCombo = QComboBox(self, toolTip='Provider', editable=False, cursor=Qt.PointingHandCursor)
        # providerCombo.setObjectName('providercombo')
        # providerCombo.addItem(QIcon(':assets/images/provider-scenerls.png'), '')
        # providerCombo.addItem(QIcon(':assets/images/provider-tvrelease.png'), '')
        # providerCombo.setIconSize(QSize(146, 36))
        # providerCombo.setMinimumSize(QSize(160, 40))
        # providerCombo.setStyleSheet('''
        #     QComboBox, QComboBox::drop-down { background-color: transparent; border: none; margin: 5px; }
        #     QComboBox::down-arrow { image: url(:assets/images/down_arrow.png); }
        #     QComboBox QAbstractItemView { selection-background-color: #DDDDE4; }
        # ''')
        # providerCombo.currentIndexChanged.connect(self.select_provider)
        layout.addWidget(
            QLabel(pixmap=QPixmap(':assets/images/provider-scenerls.png')))
        layout.addWidget(self.search_field)
        layout.addWidget(self.favorites_button)
        layout.addWidget(self.refresh_button)
        layout.addWidget(QLabel('Pages:'))
        layout.addWidget(self.dlpages_field)
        layout.addWidget(self.settings_button)
        return layout

    @pyqtSlot(int)
    def select_provider(self, index: int):
        if index == 0:
            self.provider = 'Scene-RLS'
            self.source_url = 'http://scene-rls.net/releases/index.php?p={0}&cat=TV%20Shows'
        elif index == 1:
            self.provider = 'TV-Release'
            self.source_url = 'http://tv-release.pw/?cat=TV'
        self.setWindowTitle('%s :: %s' %
                            (qApp.applicationName(), self.provider))

    def settings_menu(self) -> QMenu:
        settings_action = QAction(self.icon_settings,
                                  'Settings',
                                  self,
                                  triggered=self.show_settings)
        updates_action = QAction(self.icon_updates,
                                 'Check for updates',
                                 self,
                                 triggered=self.check_update)
        aboutqt_action = QAction('About Qt', self, triggered=qApp.aboutQt)
        about_action = QAction('About %s' % qApp.applicationName(),
                               self,
                               triggered=self.about_app)
        menu = QMenu()
        menu.addAction(settings_action)
        menu.addAction(updates_action)
        menu.addSeparator()
        menu.addAction(aboutqt_action)
        menu.addAction(about_action)
        return menu

    def init_metabar(self) -> QHBoxLayout:
        self.meta_template = 'Total number of links retrieved: <b>%i</b> / <b>%i</b>'
        self.progress = QProgressBar(parent=self,
                                     minimum=0,
                                     maximum=(self.dl_pagecount *
                                              self.dl_pagelinks),
                                     visible=False)
        self.taskbar.setProgress(0.0, True)
        if sys.platform == 'win32':
            self.win_taskbar_button = QWinTaskbarButton(self)

        self.meta_label = QLabel(textFormat=Qt.RichText,
                                 alignment=Qt.AlignRight,
                                 objectName='totals')
        self.update_metabar()
        layout = QHBoxLayout()
        layout.setContentsMargins(10, 5, 10, 10)
        layout.addWidget(self.progress, Qt.AlignLeft)
        layout.addWidget(self.meta_label, Qt.AlignRight)
        return layout

    @pyqtSlot()
    def check_update(self) -> None:
        QDesktopServices.openUrl(QUrl(FixedSettings.latest_release_url))

    @pyqtSlot()
    def show_settings(self) -> None:
        settings_win = Settings(self, self.settings)
        settings_win.exec_()

    def update_metabar(self) -> bool:
        rowcount = self.table.rowCount()
        self.meta_label.setText(
            self.meta_template %
            (rowcount, self.dl_pagecount * self.dl_pagelinks))
        self.progress.setValue(rowcount)
        self.taskbar.setProgress(rowcount / self.progress.maximum())
        if sys.platform == 'win32':
            self.win_taskbar_button.progress().setValue(self.progress.value())
        return True

    def start_scraping(self) -> None:
        self.init_threads('scrape')
        self.rows = 0
        self.table.clearContents()
        self.table.setRowCount(0)
        self.table.setSortingEnabled(False)
        self.update_metabar()
        self.scrapeThread.start()

    @pyqtSlot()
    def about_app(self) -> None:
        about_html = '''<style>
        a { color:#441d4e; text-decoration:none; font-weight:bold; }
        a:hover { text-decoration:underline; }
    </style>
    <p style="font-size:24pt; font-weight:bold; color:#6A687D;">%s</p>
    <p>
        <span style="font-size:13pt;"><b>Version: %s</b></span>
        <span style="font-size:10pt;position:relative;left:5px;">( %s )</span>
    </p>
    <p style="font-size:13px;">
        Copyright &copy; %s <a href="mailto:[email protected]">Pete Alexandrou</a>
        <br/>
        Web: <a href="%s">%s</a>
    </p>
    <p style="font-size:11px;">
        This program is free software; you can redistribute it and/or
        modify it under the terms of the GNU General Public License
        as published by the Free Software Foundation; either version 2
        of the License, or (at your option) any later version.
    </p>''' % (qApp.applicationName(), qApp.applicationVersion(),
               platform.architecture()[0], datetime.now().year,
               qApp.organizationDomain(), qApp.organizationDomain())
        QMessageBox.about(self, 'About %s' % qApp.applicationName(),
                          about_html)

    @pyqtSlot(int)
    def update_pagecount(self, index: int) -> None:
        self.dl_pagecount = int(self.dlpages_field.itemText(index))
        self.scrapeWorker.maxpages = self.dl_pagecount
        self.progress.setMaximum(self.dl_pagecount * self.dl_pagelinks)
        self.settings.setValue('dl_pagecount', self.dl_pagecount)
        if sys.platform == 'win32':
            self.win_taskbar_button.progress().setMaximum(self.dl_pagecount *
                                                          self.dl_pagelinks)
        if self.scrapeThread.isRunning():
            self.scrapeThread.requestInterruption()
        self.start_scraping()

    @pyqtSlot()
    def show_progress(self):
        self.progress.show()
        self.taskbar.setProgress(0.0, True)
        if sys.platform == 'win32':
            self.win_taskbar_button.setWindow(self.windowHandle())
            self.win_taskbar_button.progress().setRange(
                0, self.dl_pagecount * self.dl_pagelinks)
            self.win_taskbar_button.progress().setVisible(True)
            self.win_taskbar_button.progress().setValue(self.progress.value())

    @pyqtSlot()
    def scrape_finished(self) -> None:
        self.progress.hide()
        self.taskbar.setProgress(0.0, False)
        if sys.platform == 'win32':
            self.win_taskbar_button.progress().setVisible(False)
        self.table.setSortingEnabled(True)
        self.filter_table(text='')

    @pyqtSlot(list)
    def add_row(self, row: list) -> None:
        if self.scrapeThread.isInterruptionRequested():
            self.scrapeThread.terminate()
        else:
            self.cols = 0
            self.table.setRowCount(self.rows + 1)
            if self.table.cursor() != Qt.PointingHandCursor:
                self.table.setCursor(Qt.PointingHandCursor)
            for item in row:
                table_item = QTableWidgetItem(item)
                table_item.setToolTip(
                    '%s\n\nDouble-click to view hoster links.' % row[1])
                table_item.setFont(QFont('Open Sans', weight=QFont.Normal))
                if self.cols == 2:
                    if sys.platform == 'win32':
                        table_item.setFont(
                            QFont('Open Sans Semibold', pointSize=10))
                    elif sys.platform == 'darwin':
                        table_item.setFont(
                            QFont('Open Sans Bold', weight=QFont.Bold))
                    else:
                        table_item.setFont(
                            QFont('Open Sans',
                                  weight=QFont.DemiBold,
                                  pointSize=10))
                    table_item.setText('  ' + table_item.text())
                elif self.cols in (0, 3):
                    table_item.setTextAlignment(Qt.AlignCenter)
                self.table.setItem(self.rows, self.cols, table_item)
                self.update_metabar()
                self.cols += 1
            self.rows += 1

    @pyqtSlot(list)
    def add_hosters(self, links: list) -> None:
        self.hosters_win.show_hosters(links)

    @pyqtSlot(QModelIndex)
    def show_hosters(self, index: QModelIndex) -> None:
        qApp.setOverrideCursor(Qt.BusyCursor)
        self.hosters_win = HosterLinks(self)
        self.hosters_win.downloadLink.connect(self.download_link)
        self.hosters_win.copyLink.connect(self.copy_download_link)
        self.links = HostersThread(
            self.table.item(self.table.currentRow(), 1).text(),
            self.user_agent)
        self.links.setHosters.connect(self.add_hosters)
        self.links.noLinks.connect(self.no_links)
        self.links.start()

    @pyqtSlot()
    def no_links(self) -> None:
        self.hosters_win.loading_progress.cancel()
        self.hosters_win.close()
        QMessageBox.warning(
            self, 'No Links Available',
            'No links are available yet for the chosen TV show. ' +
            'This is most likely due to the files still being uploaded. This is normal if the '
            +
            'link was published 30-45 mins ago.\n\nPlease check back again in 10-15 minutes.'
        )

    @pyqtSlot(bool)
    def filter_faves(self, checked: bool) -> None:
        self.settings.setValue('faves_filter', checked)
        # if hasattr(self, 'scrapeWorker') and (sip.isdeleted(self.scrapeWorker) or self.scrapeWorker.complete):
        if not self.firstrun:
            self.filter_table()

    @pyqtSlot(str)
    @pyqtSlot()
    def filter_table(self, text: str = '') -> None:
        filters = []
        if self.favorites_button.isChecked():
            filters = self.favorites
            self.table.sortItems(2, Qt.AscendingOrder)
        else:
            self.table.sortItems(0, Qt.DescendingOrder)
        if len(text):
            filters.append(text)
        if not len(filters) or not hasattr(self, 'valid_rows'):
            self.valid_rows = []
        for search_term in filters:
            for item in self.table.findItems(search_term, Qt.MatchContains):
                self.valid_rows.append(item.row())
        for row in range(0, self.table.rowCount()):
            if not len(filters):
                self.table.showRow(row)
            else:
                if row not in self.valid_rows:
                    self.table.hideRow(row)
                else:
                    self.table.showRow(row)

    @pyqtSlot()
    def clear_filters(self):
        if not len(self.search_field.text()):
            self.filter_table('')

    @pyqtSlot(bool)
    def aria2_confirmation(self, success: bool) -> None:
        qApp.restoreOverrideCursor()
        if success:
            if sys.platform.startswith('linux'):
                self.notify(
                    title=qApp.applicationName(),
                    msg='Your download link has been unrestricted and now ' +
                    'queued in Aria2 RPC Daemon',
                    icon=self.NotifyIcon.SUCCESS)
            else:
                QMessageBox.information(
                    self, qApp.applicationName(),
                    'Download link has been queued in Aria2.', QMessageBox.Ok)
        else:
            QMessageBox.critical(
                self, 'Aria2 RPC Daemon',
                'Could not connect to Aria2 RPC Daemon. ' +
                'Check your %s settings and try again.' %
                qApp.applicationName(), QMessageBox.Ok)

    @pyqtSlot(str)
    def download_link(self, link: str) -> None:
        if len(self.realdebrid_api_token) > 0 and 'real-debrid.com' not in link \
            and 'rdeb.io' not in link:
            qApp.setOverrideCursor(Qt.BusyCursor)
            self.unrestrict_link(link, True)
        else:
            if self.download_manager == 'aria2':
                self.aria2 = Aria2Thread(settings=self.settings, link_url=link)
                self.aria2.aria2Confirmation.connect(self.aria2_confirmation)
                self.aria2.start()
                self.hosters_win.close()
            elif self.download_manager == 'pyload':
                self.pyload_conn = PyloadConnection(self.pyload_host,
                                                    self.pyload_username,
                                                    self.pyload_password)
                pid = self.pyload_conn.addPackage(name='TVLinker',
                                                  links=[link])
                qApp.restoreOverrideCursor()
                self.hosters_win.close()
                if sys.platform.startswith('linux'):
                    self.notify(title='Download added to %s' %
                                self.download_manager,
                                icon=self.NotifyIcon.SUCCESS)
                else:
                    QMessageBox.information(
                        self, self.download_manager,
                        'Your link has been queued in %s.' %
                        self.download_manager, QMessageBox.Ok)
                # open_pyload = msgbox.addButton('Open pyLoad', QMessageBox.AcceptRole)
                # open_pyload.clicked.connect(self.open_pyload)
            elif self.download_manager in ('kget', 'persepolis'):
                provider = self.kget_cmd if self.download_manager == 'kget' else self.persepolis_cmd
                cmd = '{0} "{1}"'.format(provider, link)
                if self.cmdexec(cmd):
                    qApp.restoreOverrideCursor()
                    self.hosters_win.close()
                    if sys.platform.startswith('linux'):
                        self.notify(title='Download added to %s' %
                                    self.download_manager,
                                    icon=self.NotifyIcon.SUCCESS)
                    else:
                        QMessageBox.information(
                            self, self.download_manager,
                            'Your link has been queued in %s.' %
                            self.download_manager, QMessageBox.Ok)
            elif self.download_manager == 'idm':
                cmd = '"%s" /n /d "%s"' % (self.idm_exe_path, link)
                if self.cmdexec(cmd):
                    qApp.restoreOverrideCursor()
                    self.hosters_win.close()
                    QMessageBox.information(
                        self, 'Internet Download Manager',
                        'Your link has been queued in IDM.')
                else:
                    print('IDM QProcess error = %s' %
                          self.ProcError(self.idm.error()).name)
                    qApp.restoreOverrideCursor()
                    self.hosters_win.close()
                    QMessageBox.critical(
                        self, 'Internet Download Manager',
                        '<p>Could not connect to your local IDM application instance. '
                        +
                        'Please check your settings and ensure the IDM executable path is correct '
                        +
                        'according to your installation.</p><p>Error Code: %s</p>'
                        % self.ProcError(self.idm.error()).name,
                        QMessageBox.Ok)
            else:
                dlpath, _ = QFileDialog.getSaveFileName(
                    self, 'Save File',
                    link.split('/')[-1])
                if dlpath != '':
                    self.directdl_win = DirectDownload(parent=self)
                    self.directdl = DownloadThread(link_url=link,
                                                   dl_path=dlpath)
                    self.directdl.dlComplete.connect(
                        self.directdl_win.download_complete)
                    if sys.platform.startswith('linux'):
                        self.directdl.dlComplete.connect(
                            lambda: self.notify(qApp.applicationName(
                            ), 'Download complete', self.NotifyIcon.SUCCESS))
                    else:
                        self.directdl.dlComplete.connect(
                            lambda: QMessageBox.information(
                                self, qApp.applicationName(),
                                'Download complete', QMessageBox.Ok))
                    self.directdl.dlProgressTxt.connect(
                        self.directdl_win.update_progress_label)
                    self.directdl.dlProgress.connect(
                        self.directdl_win.update_progress)
                    self.directdl_win.cancelDownload.connect(
                        self.cancel_download)
                    self.directdl.start()
                    self.hosters_win.close()

    def _init_notification_icons(self):
        for icon in self.NotifyIcon:
            icon_file = QPixmap(icon.value, 'PNG')
            icon_file.save(
                os.path.join(FixedSettings.config_path,
                             os.path.basename(icon.value)), 'PNG', 100)

    def notify(self,
               title: str,
               msg: str = '',
               icon: Enum = None,
               urgency: int = 1) -> bool:
        icon_path = icon.value if icon is not None else self.NotifyIcon.DEFAULT.value
        icon_path = os.path.join(FixedSettings.config_path,
                                 os.path.basename(icon_path))
        if not os.path.exists(icon_path):
            self._init_notification_icons()
        notification = notify.Notification(title, msg, icon_path)
        notification.set_urgency(urgency)
        return notification.show()

    def cmdexec(self, cmd: str) -> bool:
        self.proc = QProcess()
        self.proc.setProcessChannelMode(QProcess.MergedChannels)
        if hasattr(self.proc, 'errorOccurred'):
            self.proc.errorOccurred.connect(lambda error: print(
                'Process error = %s' % self.ProcError(error).name))
        if self.proc.state() == QProcess.NotRunning:
            self.proc.start(cmd)
            self.proc.waitForFinished(-1)
            rc = self.proc.exitStatus(
            ) == QProcess.NormalExit and self.proc.exitCode() == 0
            self.proc.deleteLater()
            return rc
        return False

    @pyqtSlot()
    def cancel_download(self) -> None:
        self.directdl.cancel_download = True
        self.directdl.quit()
        self.directdl.deleteLater()

    def open_pyload(self) -> None:
        QDesktopServices.openUrl(QUrl(self.pyload_config.host))

    @pyqtSlot(str)
    def copy_download_link(self, link: str) -> None:
        if len(self.realdebrid_api_token) > 0 and 'real-debrid.com' not in link \
            and 'rdeb.io' not in link:
            qApp.setOverrideCursor(Qt.BusyCursor)
            self.unrestrict_link(link, False)
        else:
            clip = qApp.clipboard()
            clip.setText(link)
            self.hosters_win.close()
            qApp.restoreOverrideCursor()

    def unrestrict_link(self, link: str, download: bool = True) -> None:
        caller = inspect.stack()[1].function
        self.realdebrid = RealDebridThread(
            settings=self.settings,
            api_url=FixedSettings.realdebrid_api_url,
            link_url=link,
            action=RealDebridThread.RealDebridAction.UNRESTRICT_LINK)
        self.realdebrid.errorMsg.connect(self.error_handler)
        if download:
            self.realdebrid.unrestrictedLink.connect(self.download_link)
        else:
            self.realdebrid.unrestrictedLink.connect(self.copy_download_link)
        self.realdebrid.start()

    def closeEvent(self, event: QCloseEvent) -> None:
        if hasattr(self, 'scrapeThread'):
            if not sip.isdeleted(
                    self.scrapeThread) and self.scrapeThread.isRunning():
                self.scrapeThread.requestInterruption()
                self.scrapeThread.quit()
        qApp.quit()

    def error_handler(self, props: list) -> None:
        qApp.restoreOverrideCursor()
        QMessageBox.critical(self, props[0], props[1], QMessageBox.Ok)

    @staticmethod
    def get_path(path: str = None, override: bool = False) -> str:
        if override:
            if getattr(sys, 'frozen', False):
                return os.path.join(sys._MEIPASS, path)
            return os.path.join(QFileInfo(__file__).absolutePath(), path)
        return ':assets/%s' % path

    @staticmethod
    def get_version(filename: str = '__init__.py') -> str:
        with open(TVLinker.get_path(filename, override=True), 'r') as initfile:
            for line in initfile.readlines():
                m = re.match('__version__ *= *[\'](.*)[\']', line)
                if m:
                    return m.group(1)
예제 #27
0
class mainWindow(QMainWindow, Ui_MainWindow):
    """
    The main class for the GUI window
    """

    def __init__(self):
        """
        The constructor and initiator.
        :return:
        """
        # initial setup
        super(mainWindow, self).__init__()
        self.setupUi(self)
        self.thread = QThread()

        self.connected = False

        # Connect signals
        self.connect_signals()

    def on_push_button_clicked(self):
        if self.pushButton.isChecked():

            try:
                host = str(ip_address(self.lineEdit_host.text()))
                port = self.lineEdit_port.text()
                if not port.isdigit():
                    raise ValueError

            except(ValueError):
                self.pushButton.setChecked(False)
                self.show_message('Please enter valid numeric IP address and port number.')
                return

            self.zeromq_listener_10001 = ZMQListener(host, port, '10001')
            self.zeromq_listener_10001.moveToThread(self.thread)
            self.zeromq_listener_10001.message.connect(self.signal_received_10001)
            self.zeromq_listener_10001.err_msg.connect(self.show_message)

            self.thread.started.connect(self.zeromq_listener_10001.loop)
            self.thread.start()

            self.pushButton.setText('Stop')
            self.show_message('Connected to server: {}:{}'.format(host, port))
        else:
            self.zeromq_listener_10001.running = False
            self.thread.terminate()
            self.pushButton.setText('Start')
            self.show_message('Disconnected.')

    def connect_signals(self):
        """
        Connects signals.
        :return:
        """

        # Action about and Action quit will be shown differently in OSX

        self.actionAbout.triggered.connect(self.show_about_dialog)
        self.actionQuit.triggered.connect(self.shutdown)
        self.pushButton.clicked.connect(self.on_push_button_clicked)

    def shutdown(self):
        self.zeromq_listener_10001.running = False
        self.thread.terminate()
        self.thread.quit()
        self.thread.wait()
        QCoreApplication.instance().quit()

    def signal_received_10001(self, message):
        # get the message and split it
        topic, time, stat_bits, value_str = message.split()
        current_range = int(stat_bits[-3:], 2)
        range_str = RANGE_DIC[current_range]

        self.label_time_stamp.setText(time)
        self.label_status.setText(stat_bits)
        self.label_range.setText(range_str)

        # do the calibration
        value_float = float(value_str) * CAL_SLOPE + CAL_ITCPT

        # convert binary to float value
        value = value_float * RAIL_VOLTAGE / ADC_QUANTIZATION

        # set to 2 decimal points
        value = int(value * 100) / 100

        # in case more digits are needed
        # self.lcdNumber.setDigitCount(8)
        if self.zeromq_listener_10001.running:
            self.lcdNumber.display(value)
        else:
            self.lcdNumber.display(0)

    def closeEvent(self, event):
        self.zeromq_listener_10001.running = False
        self.thread.terminate()
        self.thread.quit()
        self.thread.wait()

    def show_message(self, message):
        """
        Implementation of an abstract method:
        Show text in status bar
        :param message:
        :return:
        """
        self.statusbar.showMessage(message)

    def show_about_dialog(self):
        """
        Show about dialog
        :return:
        """
        about_dialog = QDialog()
        about_dialog.ui = Ui_AbooutDialog()
        about_dialog.ui.setupUi(about_dialog)
        about_dialog.ui.labelVersion.setText('Version: {}'.format(__version__))
        about_dialog.exec_()
        about_dialog.show()
예제 #28
0
class SpaceMApp(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(SpaceMApp, self).__init__(parent)
        self.step = 2
        self.setupUi(self)
        self.tabWidget.setCurrentWidget(
            self.tabWidget.findChild(QWidget, 'global_vars'))
        self.setup_tabs()
        self.setup_workers()
        self.connect_callbacks()
        self.set_btns_static_state()

    def setup_workers(self):
        self.setup_full_thread()
        self.setup_am_thread()
        self.setup_fid_filter_thread()
        self.setup_reg_image_thread()
        self.setup_grab_ms_data_thread()
        self.setup_cellprof_thread()
        self.setup_cellprof_postproc_thread()
        self.setup_gen_csv_thread()

    def setup_tabs(self):
        gv()
        gsTab(self)
        amfTab(self)
        gmsTab(self)
        genCsvTab(self)

    def setup_full_thread(self):
        self.thread_full = QThread()
        self.worker = FullPipeWorker()
        self.worker.moveToThread(self.thread_full)
        self.thread_full.started.connect(self.worker.work_full)
        self.worker.finishPipeSig.connect(self.fullpipe_finished)
        self.worker.progressBarSig.connect(self.update_pb)
        self.worker.pipeStatusToLogger.connect(self.update_logger)

    def setup_am_thread(self):
        self.thread_0 = QThread()
        self.worker_0 = AMWorker()
        self.worker_0.moveToThread(self.thread_0)
        self.thread_0.started.connect(self.worker_0.work_1)
        self.worker_0.progressBarSig.connect(self.update_pb)
        self.worker_0.pipeStatusToLogger.connect(self.update_logger)
        self.worker_0.incrementStepSig.connect(self.increment_step)
        self.worker_0.changeTabSig.connect(self.update_tab)

        self.thread_1 = QThread()
        self.worker_1 = AMWorker()
        self.worker_1.moveToThread(self.thread_1)
        self.thread_1.started.connect(self.worker_1.work_2)
        self.worker_1.progressBarSig.connect(self.update_pb)
        self.worker_1.pipeStatusToLogger.connect(self.update_logger)
        self.worker_1.incrementStepSig.connect(self.increment_step)
        self.worker_1.changeTabSig.connect(self.update_tab)

    def setup_fid_filter_thread(self):
        self.thread_2 = QThread()
        self.worker_2 = FidFilterWorker()
        self.worker_2.moveToThread(self.thread_2)
        self.thread_2.started.connect(self.worker_2.work_1)
        self.worker_2.progressBarSig.connect(self.update_pb)
        self.worker_2.pipeStatusToLogger.connect(self.update_logger)
        self.worker_2.incrementStepSig.connect(self.increment_step)
        self.worker_2.changeTabSig.connect(self.update_tab)

    def setup_reg_image_thread(self):
        self.thread_3 = QThread()
        self.worker_3 = RegImageWorker()
        self.worker_3.moveToThread(self.thread_3)
        self.thread_3.started.connect(self.worker_3.work_1)
        self.worker_3.progressBarSig.connect(self.update_pb)
        self.worker_3.pipeStatusToLogger.connect(self.update_logger)
        self.worker_3.incrementStepSig.connect(self.increment_step)
        self.worker_3.changeTabSig.connect(self.update_tab)

    def setup_grab_ms_data_thread(self):
        self.thread_4 = QThread()
        self.worker_4 = GrabMSDataWorker()
        self.worker_4.moveToThread(self.thread_4)
        self.thread_4.started.connect(self.worker_4.work_1)
        self.worker_4.progressBarSig.connect(self.update_pb)
        self.worker_4.pipeStatusToLogger.connect(self.update_logger)
        self.worker_4.incrementStepSig.connect(self.increment_step)
        self.worker_4.changeTabSig.connect(self.update_tab)

    def setup_cellprof_thread(self):
        self.thread_5 = QThread()
        self.worker_5 = CPWorker()
        self.worker_5.moveToThread(self.thread_5)
        self.thread_5.started.connect(self.worker_5.work_1)
        self.worker_5.progressBarSig.connect(self.update_pb)
        self.worker_5.pipeStatusToLogger.connect(self.update_logger)
        self.worker_5.incrementStepSig.connect(self.increment_step)
        self.worker_5.changeTabSig.connect(self.update_tab)

    def setup_cellprof_postproc_thread(self):
        self.thread_6 = QThread()
        self.worker_6 = CPWorker()
        self.worker_6.moveToThread(self.thread_6)
        self.thread_6.started.connect(self.worker_6.work_2)
        self.worker_6.progressBarSig.connect(self.update_pb)
        self.worker_6.pipeStatusToLogger.connect(self.update_logger)
        self.worker_6.incrementStepSig.connect(self.increment_step)
        self.worker_6.changeTabSig.connect(self.update_tab)

    def setup_gen_csv_thread(self):
        self.thread_7 = QThread()
        self.worker_7 = GenCSVWorker()
        self.worker_7.moveToThread(self.thread_7)
        self.thread_7.started.connect(self.worker_7.work_1)
        self.worker_7.progressBarSig.connect(self.update_pb)
        self.worker_7.pipeStatusToLogger.connect(self.update_logger)
        self.worker_7.incrementStepSig.connect(self.increment_step)
        self.worker_7.changeTabSig.connect(self.update_tab)

    def set_btns_static_state(self):
        self.btnImprtSettings.setEnabled(True)
        self.btnSaveSettings.setEnabled(True)
        if self.step == 8:
            self.btnRepeatStep.setEnabled(False)
        else:
            self.btnRepeatStep.setEnabled(True)
        self.btnRunNextStep.setEnabled(True)
        self.btnStartAnalysis.setEnabled(True)

    def set_btns_running_state(self):
        self.btnImprtSettings.setEnabled(False)
        self.btnSaveSettings.setEnabled(False)
        self.btnRepeatStep.setEnabled(False)
        self.btnRunNextStep.setEnabled(False)
        self.btnStartAnalysis.setEnabled(False)

    def connect_callbacks(self):
        self.btnImprtSettings.clicked.connect(self.import_settings)
        self.btnSaveSettings.clicked.connect(self.save_settings)
        self.btnRunNextStep.clicked.connect(self.run_prev_or_next_step)
        self.btnRepeatStep.clicked.connect(self.repeat_step)
        self.btnStartAnalysis.clicked.connect(self.run_full_pipe_check)

    def import_settings(self):
        if os.path.exists('./configs/settings.json'):
            with open('./configs/settings.json') as f:
                settings = json.load(f)
                self.lineEditMainFolder.setText(settings['inp_path'])
                self.lineEditPythonPath.setText(settings['python_path'])
                self.lineEditCellProfiler.setText(settings['cellprofilerPath'])

                self.lineEditStitchedPreMImage.setText(
                    settings['stitchedImgPreMPath'])
                self.lineEditStitchedPostMImage.setText(
                    settings['stitchedImgPostMPath'])

                self.lineEditUdpFile.setText(settings['udpFile'])
                self.lineEditMetadata.setText(settings['microscopyMetadata'])

                self.tab_amf_FluoCh.setText(settings['postMaldiDapi'])
                self.tab_amf_ifftImgPath.setText(
                    settings['manProcessedImgPath'])

                self.tab_gms_lineEditMSDs.setText(settings['MSDsName'])

                self.lineEditCPpipeFile.setText(settings['cpPipeLine'])

                self.tab_csvGen_lineEditCells.setText(settings['csvCellsPath'])
        else:
            QMessageBox.warning(self, "Warning", "No settings file found!")
            Exception('Settings file was not found or does not exist')

    def save_settings(self):
        if os.path.exists('./configs/settings.json'):
            with open('./configs/settings.json', 'r') as f:
                settings = json.load(f)
                settings['inp_path'] = global_vars.inpPath
                settings['python_path'] = global_vars.pythonPath
                settings['cellprofilerPath'] = global_vars.cellprofilerPath

                settings[
                    'stitchedImgPreMPath'] = global_vars.stitchedImgPreMPath
                settings[
                    'stitchedImgPostMPath'] = global_vars.stitchedImgPostMPath

                settings['udpFile'] = global_vars.udpFile
                settings['microscopyMetadata'] = global_vars.microscopyMetadata

                settings['MSDsName'] = global_vars.tab_gms_msDSName

                settings['cpPipeLine'] = global_vars.cpPipeLine

                settings['csvCellsPath'] = global_vars.tab_genCsv_csvFilePath
            with open('./configs/settings.json', 'w') as f:
                json.dump(settings, f)
                QMessageBox.information(self, "Info",
                                        "The new settings were saved")
        else:
            QMessageBox.warning(self, "Warning", "No settings file found!")
            Exception('Settings file was not found or does not exist')

    def increment_step(self):
        self.step += 1
        print(self.step)

    def decrement_step(self):
        self.step -= 1

    def repeat_step(self):
        self.decrement_step()
        self.run_prev_or_next_step()

    def run_prev_or_next_step(self):
        if self.validate_inputs():
            if self.step == 0:
                self.set_btns_running_state()
                self.thread_0.start()
            elif self.step == 1:
                self.set_btns_running_state()
                self.thread_1.start()
            elif self.step == 2:
                self.set_btns_running_state()
                self.thread_2.start()
            elif self.step == 3:
                self.set_btns_running_state()
                self.thread_3.start()
            elif self.step == 4:
                self.set_btns_running_state()
                self.thread_4.start()
            elif self.step == 5:
                self.set_btns_running_state()
                self.thread_5.start()
            elif self.step == 6:
                self.set_btns_running_state()
                self.thread_6.start()
            elif self.step == 7:
                self.set_btns_running_state()
                self.thread_7.start()
            elif self.step == 8:
                self.set_btns_running_state()
                self.thread_8.start()

    def validate_inputs(self):
        if self.step == 0:
            check_line = global_vars.inpPath and global_vars.pythonPath and global_vars.stitchedImgPreMPath \
            and global_vars.stitchedImgPostMPath and global_vars.udpFile and global_vars.microscopyMetadata
            if platform == "win32":
                check_line = check_line and global_vars.cellprofilerPath
            if check_line == '':
                QMessageBox.warning(
                    self, "Warning",
                    "Please check that all inputs are correctly entered and are "
                    "not empty for general settings")
                return
            else:
                return True
        elif self.step == 1:
            if global_vars.tab_amf_postMaldiDapi == '' and self.tab_amf_cbMatrix.currentText() == 'DHB' or \
                self.tab_amf_manFftRb.isChecked() and global_vars.tab_amf_ifftImage == '':
                QMessageBox.warning(
                    self, "Warning",
                    "Please check that all inputs are correctly entered and are "
                    "not empty for general settings")
                return
            else:
                return True
        elif self.step == 2 or self.step == 3:
            check_line = global_vars.inpPath and global_vars.pythonPath and global_vars.stitchedImgPreMPath \
                         and global_vars.stitchedImgPostMPath
            if check_line == '':
                QMessageBox.warning(
                    self, "Warning",
                    "Please check that all inputs are correctly entered and are "
                    "not empty for general settings")
                return
            else:
                return True
        elif self.step == 4:
            prev_checks = global_vars.inpPath and global_vars.pythonPath and global_vars.stitchedImgPreMPath \
            and global_vars.stitchedImgPostMPath and global_vars.udpFile and global_vars.microscopyMetadata \
            #               and \

            # (global_vars.tab_amf_postMaldiDapi == '' and self.tab_amf_cbMatrix.currentText() == 'DHB' or \
            #     self.tab_amf_manFftRb.isChecked() and global_vars.tab_amf_ifftImage == '')
            if global_vars.tab_gms_msDSName == '':
                QMessageBox.warning(
                    self, "Warning",
                    "Please make sure that your provided the name of the "
                    "dataset")
                return
            elif prev_checks == '':
                QMessageBox.warning(
                    self, "Warning",
                    "Please make sure that all the paths from the previous steps "
                    "are provided")
                return
            else:
                return True
        elif self.step == 5:
            if platform == "win32" and global_vars.cellprofilerPath == '':
                QMessageBox.warning(
                    self, "Warning",
                    "Please check that the path to CellProfler.exe is provided"
                )
                return
            else:
                check_line = global_vars.inpPath and global_vars.pythonPath and global_vars.stitchedImgPreMPath \
                             and global_vars.stitchedImgPostMPath and global_vars.udpFile and global_vars.microscopyMetadata
                if check_line == '':
                    QMessageBox.warning(
                        self, "Warning",
                        "Please check that all inputs are correctly entered and are "
                        "not empty for general settings")
                    return
                else:
                    return True
        elif self.step == 6:
            check_line = global_vars.inpPath and global_vars.pythonPath and global_vars.stitchedImgPreMPath \
                          and global_vars.stitchedImgPostMPath and global_vars.udpFile and global_vars.microscopyMetadata and \
                          global_vars.tab_gms_msDSName
            if check_line == '':
                QMessageBox.warning(
                    self, "Warning",
                    "Please check that all inputs are correctly entered and are "
                    "not empty for general settings")
                return
            else:
                return True
        elif self.step == 7:
            prev_checks = global_vars.inpPath and global_vars.pythonPath and global_vars.stitchedImgPreMPath \
                          and global_vars.stitchedImgPostMPath and global_vars.udpFile and global_vars.microscopyMetadata
            if global_vars.tab_genCsv_csvFilePath == '':
                QMessageBox.warning(
                    self, "Warning",
                    "Please make sure that your provided the csv file with features path"
                )
                return
            elif global_vars.tab_gms_msDSName == '':
                QMessageBox.warning(self, "Warning",
                                    "Please provide dataset's metaspace name")
                return
            elif prev_checks == '':
                QMessageBox.warning(
                    self, "Warning",
                    "Please make sure that all the paths from the previous steps "
                    "are provided")
                return
            else:
                return True

    '''Running threads of workers/Full pipeline'''

    def run_full_pipe(self):
        if self.validate_inputs():
            self.set_btns_running_state()
            self.thread_full.start()

    def run_full_pipe_check(self):
        if self.step != 0 and self.step != 8:
            btnRunPipeReply = QMessageBox.question(
                self, "Warning",
                "You are in the middle of running pipeline step by step wise, "
                "running the full pipeline will start it from the first step again. "
                "Are you sure you want to proceed?")
            if btnRunPipeReply == QMessageBox.Yes:
                self.run_full_pipe()
            else:
                print('Running the full pipeline was aborted.')
        else:
            self.run_full_pipe()

    def fullpipe_finished(self):
        self.thread_full.terminate()
        self.set_btns_static_state()

    def update_tab(self):
        if self.step == 1:
            self.tabWidget.setCurrentWidget(
                self.tabWidget.findChild(QWidget, 'amf_tab'))
            self.set_btns_static_state()
            self.thread_0.terminate()
        elif self.step == 2:
            self.tabWidget.setCurrentWidget(
                self.tabWidget.findChild(QWidget, 'fids_tab'))
            self.set_btns_static_state()
            self.thread_1.terminate()
        elif self.step == 3:
            self.tabWidget.setCurrentWidget(
                self.tabWidget.findChild(QWidget, 'reg_tab'))
            self.set_btns_static_state()
            self.thread_2.terminate()
        elif self.step == 4:
            self.tabWidget.setCurrentWidget(
                self.tabWidget.findChild(QWidget, 'grab_ms_data_tab'))
            self.set_btns_static_state()
            self.thread_3.terminate()
        elif self.step == 5:
            self.tabWidget.setCurrentWidget(
                self.tabWidget.findChild(QWidget, 'cell_segm_tab'))
            self.set_btns_static_state()
            self.thread_4.terminate()
        elif self.step == 6:
            self.tabWidget.setCurrentWidget(
                self.tabWidget.findChild(QWidget, 'cell_segm_tab'))
            self.set_btns_static_state()
            self.thread_5.terminate()
        elif self.step == 7:
            self.tabWidget.setCurrentWidget(
                self.tabWidget.findChild(QWidget, 'csv_gen_tab'))
            self.set_btns_static_state()
            self.thread_6.terminate()
        elif self.step == 8:
            self.set_btns_static_state()
            self.thread_7.terminate()

    def update_pb(self, val):
        self.progressBar.setValue(val)

    def update_logger(self, val):
        logging.info(val)
        with open('./logs/spaceM.log', 'r') as f:
            self.LogsTextBrowser.setText(f.read())
예제 #29
0
class ChatClient(QObject):
    class Reader(QObject):

        onReceive = pyqtSignal(bytes)

        def __init__(self, connection):
            super().__init__()
            self.connection = connection
            self.is_open = True

        def read(self):
            buffer = b""
            while self.is_open:
                try:
                    buffer += self.connection.recv(4096)
                    if buffer.endswith(MSG_TERMINATOR):
                        buffer.strip(MSG_TERMINATOR)
                        self.onReceive.emit(buffer)
                        buffer = b""
                except socket.timeout as e:
                    print(e.args)
                    buffer = b""

    def __init__(self, intro_msg):
        """
        Socket client that support async communication.
        :param sock: socket to ther other end of communication
        :param incoming_queue: deque that retrieves incoming messages
        :param outcoming_queue: deque that contains message to send to other end of communication
        """
        super().__init__()
        self.connection = None
        self.reader = None
        self._read_thread = None
        self.intro_msg: IntroductionMessage = intro_msg

    @property
    def is_open(self):
        return True if self.connection else False

    @property
    def onReceive(self):
        if self.reader:
            return self.reader.onReceive
        else:
            raise Exception(
                "Establish connection before geting access to signal")

    def connect(self, address, port):
        self.connection = socket.create_connection((address, port), 10)
        self._read_thread = QThread(self)
        self.reader = self.Reader(self.connection)
        self.reader.moveToThread(self._read_thread)
        self._read_thread.started.connect(self.reader.read)
        self._read_thread.start()
        self.connection.send(bytes(self.intro_msg) + MSG_TERMINATOR)

    @pyqtSlot(bytes)
    def write(self, message: bytes):
        if self.is_open:
            obj_to_send = Serializable.from_bytes(message)
            if type(obj_to_send) is ChatMessage:
                obj_to_send.sender = self.intro_msg.nickname
            self.connection.send(bytes(obj_to_send) + MSG_TERMINATOR)
        else:
            raise Exception("Cannot write to closed connection")

    def close(self):
        if self.is_open:
            self._read_thread.terminate()
            self.connection.close()
            self.connection = None
예제 #30
0
class ExampleThread(Qt.QWidget):
    def __init__(self, parent=None):
        super(ExampleThread, self).__init__(parent)

        layout = Qt.QVBoxLayout(self)
        self.lbl = Qt.QLabel("Start")
        layout.addWidget(self.lbl)
        self.btnA = Qt.QPushButton("Запустить AThread(QThread)")
        layout.addWidget(self.btnA)
        self.btnB = Qt.QPushButton("Запустить SomeObject(QObject)")
        layout.addWidget(self.btnB)
        self.btnC = Qt.QPushButton("Запустить Worker(QRunnable)")
        layout.addWidget(self.btnC)
        self.progressBar = Qt.QProgressBar()
        self.progressBar.setProperty("value", 0)
        layout.addWidget(self.progressBar)

        self.setGeometry(550, 65, 300, 300)
        self.setWindowTitle('3 разных и простых способа работы с потоками.')

        self.btnA.clicked.connect(self.using_q_thread)
        self.btnB.clicked.connect(self.using_move_to_thread)
        self.btnC.clicked.connect(self.using_q_runnable)

        self.msg = MsgBoxAThread()
        self.thread = None

        self.msgSomeObject = MsgBoxSomeObject()
        self.objThread = None

        self.counter = 0
        self.timer = Qt.QTimer()
        self.timer.setInterval(1000)
        # -------- timeout -------> def recurring_timer(self):
        self.timer.timeout.connect(self.recurring_timer)
        self.timer.start()

        self.threadpool = QThreadPool()
        print("Max потоков, кот. будут использоваться=`%d`" % self.threadpool.maxThreadCount())
        self.msgWorker = MsgBoxWorker()

        self.threadtest = QThread(self)
        self.idealthreadcount = self.threadtest.idealThreadCount()
        print("Ваша машина может обрабатывать `{}` потокa оптимально.".format(self.idealthreadcount))

    def recurring_timer(self):
        self.counter += 1
        self.lbl.setText("СЧЁТЧИК цикл GUI: %d" % self.counter)

    # ---- AThread(QThread) -----------#
    def using_q_thread(self):
        if self.thread is None:
            self.thread = AThread()
            self.thread.threadSignalAThread.connect(self.on_threadSignalAThread)
            self.thread.finished.connect(self.finishedAThread)
            self.thread.start()
            self.btnA.setText("Stop AThread(QThread)")
        else:
            self.thread.terminate()
            self.thread = None
            self.btnA.setText("Start AThread(QThread)")

    def finishedAThread(self):
        self.thread = None
        self.btnA.setText("Start AThread(QThread)")

    def on_threadSignalAThread(self, value):
        self.msg.label.setText(str(value))
        # Восстанавливаем визуализацию потокового окна, если его закрыли. Поток работает.
        # .setVisible(true) или .show() устанавливает виджет в видимое состояние,
        # если видны все его родительские виджеты до окна.
        if not self.msg.isVisible():
            self.msg.show()

    # --END-- AThread(QThread) -------------------#

    # ---- SomeObject(QObject) -------------------#
    def using_move_to_thread(self):
        if self.objThread is None:
            self.objThread = QThread()
            self.obj = SomeObject()
            self.obj.moveToThread(self.objThread)  # Переместить в поток для выполнения

            self.obj.threadSignalSomeObject.connect(self.on_threadSignalSomeObject)
            self.obj.finishedSomeObject.connect(self.finishedSomeObject)
            self.objThread.started.connect(self.obj.long_running)
            self.objThread.start()

            self.btnB.setText("Wait SomeObject(QObject)")
            self.btnB.setEnabled(False)
        else:
            pass

    def finishedSomeObject(self):
        self.objThread.terminate()
        self.objThread.wait(1)

        self.objThread = None
        self.btnB.setEnabled(True)
        self.btnB.setText("Start SomeObject(QObject)")

    def on_threadSignalSomeObject(self, value):
        self.msgSomeObject.label.setText(str(value))
        # Восстанавливаем визуализацию потокового окна, если его закрыли. Поток работает.
        if not self.msgSomeObject.isVisible():
            self.msgSomeObject.show()

    # --END-- SomeObject(QObject) -------------------#

    # ---- Worker(QRunnable) ------------------------#
    def using_q_runnable(self):
        # Передайте функцию для выполнения
        # Любые другие аргументы, kwargs передаются функции run
        worker = Worker(self.execute_this_fn)
        worker.signals.result.connect(self.print_output)
        worker.signals.finish.connect(self.thread_complete)
        worker.signals.progress.connect(self.progress_fn)
        self.threadpool.start(worker)

    def progress_fn(self, n):
        self.progressBar.setValue(n)
        self.msgWorker.label.setText(str(n))
        # Восстанавливаем визуализацию потокового окна, если его закрыли. Поток работает.
        if not self.msgWorker.isVisible():
            self.msgWorker.show()

    def execute_this_fn(self, progress_callback):
        for n in range(0, 11):
            Qt.QThread.msleep(600)
            progress_callback.emit(n * 100 / 10)
        return "Готово."

    def print_output(self, s):
        print("\ndef print_output(self, s):", s)

    def thread_complete(self):
        print("\nTHREAD ЗАВЕРШЕН!, self->", self)

    # --END-- Worker QRunnable) -------------------#

    # ==============================================###
    # потоки или процессы должны быть завершены    ###
    def closeEvent(self, event):
        reply = Qt.QMessageBox.question \
            (self, 'Информация',
             "Вы уверены, что хотите закрыть приложение?",
             Qt.QMessageBox.Yes,
             Qt.QMessageBox.No)
        if reply == Qt.QMessageBox.Yes:
            if self.thread:
                self.thread.quit()
            del self.thread
            self.msg.close()

            if self.objThread:
                self.objThread.setTerminationEnabled(True)
                self.objThread.terminate()
                self.objThread.wait(1)
            self.msgSomeObject.close()

            # закрыть поток Worker(QRunnable)
            self.msgWorker.close()

            super(ExampleThread, self).closeEvent(event)
        else:
            event.ignore()
예제 #31
0
class WowFishingBotUI:
    def __init__(self):
        self.startStop_fishing_flag = False
        self.app = None
        self.window = None

        self.game_process_name = None
        self.window_name = None

        self.log_viewer = None
        self.auto_fish_toggle = None
        self.find_wow_button = None
        self.fish_button = None
        self.fish_key_edit = None
        self.fishing_wait_time_edit = None
        self.stop_fishing_label = None
        self.fishing_thread = None
        self.tries_count_label = None
        self.loot_coords_edit = None
        self.loot_delta_edit = None
        self.bait_mov_sensibility_edit = None
        self.binary_image_widget = None
        self.rgb_image_widget = None
        self.background_model = None
        self.score_signal_viewer = None
        self.slope_signal_viewer = None
        self.post_detection_sleep_edit = None
        self.slope_samples_edit = None

        self.create_ui()

        self.bot = WowFishingBot(self)

        self.start_ui()

    def create_ui(self):
        # create QT application and main window
        self.app = QApplication(["WowFishingBotUI"])
        self.window = QMainWindow()
        self.window.setGeometry(1100, 50, 800, 900)
        self.window.setWindowTitle("WOW fishing bot")

        # create central widget and layout
        central_widget = QGroupBox()
        layout = QGridLayout()

        # CONTROLS AND TOGGLES
        # edit to change games process name
        self.game_process_name = LabeledLineEdit("WOW process name: ",
                                                 "Wow.exe")
        layout.addWidget(self.game_process_name, 0, 0, 1, 1)
        # edit to change games window name
        self.window_name = LabeledLineEdit("WOW window name: ",
                                           "World of Warcraft")
        layout.addWidget(self.window_name, 0, 1, 1, 1)

        # fishing key
        self.fish_key_edit = LabeledLineEdit("Fishing key:", "0")
        layout.addWidget(self.fish_key_edit, 2, 0, 1, 1)

        # fishing wait time
        self.fishing_wait_time_edit = LabeledLineEdit("Fishing start delay",
                                                      "4")
        layout.addWidget(self.fishing_wait_time_edit, 2, 1, 1, 1)

        # looting coordinates
        self.loot_coords_edit = LabeledLineEdit("Looting coords", "0.048 0.31")
        layout.addWidget(self.loot_coords_edit, 3, 0, 1, 1)

        # looting delta
        self.loot_delta_edit = LabeledLineEdit("Vertical looting delta",
                                               "0.03")
        layout.addWidget(self.loot_delta_edit, 3, 1, 1, 1)

        # bait movement sensibility (in standard deviations)
        self.bait_mov_sensibility_edit = LabeledLineEdit(
            "Bait movement sensibility", "4")
        layout.addWidget(self.bait_mov_sensibility_edit, 4, 0, 1, 1)

        # bait movement sensibility (in standard deviations)
        self.post_detection_sleep_edit = LabeledLineEdit(
            "Post-detection sleep time", "0.0")
        layout.addWidget(self.post_detection_sleep_edit, 4, 1, 1, 1)

        # bait movement sensibility (in standard deviations)
        self.slope_samples_edit = LabeledLineEdit("Slope estimation samples",
                                                  "30")
        layout.addWidget(self.slope_samples_edit, 5, 0, 1, 1)

        # Fish! button
        self.fish_button = QPushButton("Fish")
        self.fish_button.clicked.connect(self.start_fishing)
        self.fish_button.setEnabled(False)
        layout.addWidget(self.fish_button, 6, 1, 1, 1)

        # button to try to find the process from the line edit
        self.find_wow_button = QPushButton("Find WOW!")
        self.find_wow_button.clicked.connect(self.find_wow)
        layout.addWidget(self.find_wow_button, 6, 0, 1, 1)

        # toggle for loop fishing
        self.auto_fish_toggle = QCheckBox("Auto pilot")
        self.auto_fish_toggle.setChecked(True)
        layout.addWidget(self.auto_fish_toggle, 7, 0, 1, 1)

        # warning label with hotkey to stop fishing
        self.stop_fishing_label = DynamicLabel("Jump to stop fishing")
        self.stop_fishing_label.setFont(QFont("Times", 12, QFont.Bold))
        self.stop_fishing_label.setStyleSheet("color: red;")
        self.stop_fishing_label.setVisible(False)
        layout.addWidget(self.stop_fishing_label, 8, 0, 1, 3)

        # label with the number of captures
        self.tries_count_label = DynamicLabel("0 tries")
        self.tries_count_label.setFont(QFont("Times", 24, QFont.Bold))
        self.tries_count_label.setStyleSheet("color: red;")
        layout.addWidget(self.tries_count_label, 9, 0, 1, 3)

        # LOG FROM BOT ACTIVITY
        self.log_viewer = Log()
        layout.addWidget(self.log_viewer, 12, 0, 3, 2)

        # image display
        self.binary_image_widget = QImshow()
        layout.addWidget(self.binary_image_widget, 15, 0, 5, 1)

        self.slope_signal_viewer = QSignalViewer(2, None)
        layout.addWidget(self.slope_signal_viewer, 15, 1, 5, 1)

        central_widget.setLayout(layout)
        self.window.setCentralWidget(central_widget)

        # register flag callback to let the backend know the UI has died
        self.app.aboutToQuit.connect(self.kill_bot)

    def start_ui(self):
        self.window.show()
        self.app.exec_()

    def _fish(self):
        while True:
            self.bot.fish_grid()
            if not self.auto_fish_toggle.isChecked():
                break

    def start_fishing(self):
        # activate warning on how to stop fishing
        self.stop_fishing_label.visibility_emitter.emit(True)

        # give user time to place mouse on WOW's window
        self.log_viewer.emitter.emit("Place the cursor inside the WOW window")
        for i in reversed(range(int(self.fishing_wait_time_edit.edit.text()))):
            time.sleep(1)
            self.log_viewer.emitter.emit(
                "Fishing will start in {} ...".format(i))

        # launch fishing thread in parallel
        self.fishing_thread = QThread()
        self.fishing_thread.run = self._fish
        self.fishing_thread.start()

        # watch if the user jumps to stop fishing
        while True:
            time.sleep(0.001)
            self.app.processEvents()
            if keyboard.is_pressed(" "):
                # kill fishing thread
                self.fishing_thread.terminate()
                # remove warning on how to sto fishing
                self.stop_fishing_label.visibility_emitter.emit(False)

                return

    def find_wow(self):

        process_running = utils.check_process(
            self.game_process_name.edit.text())
        window = utils.get_window(self.window_name.edit.text())

        # check Wow is running
        if process_running and window:
            self.bot.set_wow_frame(window)
            self.log_viewer.emitter.emit("Wow window at" + str(self.bot.frame))
            self.fish_button.setEnabled(True)
            self.find_wow_button.setStyleSheet("background: green;")
        else:
            self.log_viewer.emitter.emit("Wow not found running")
            self.fish_button.setEnabled(False)
            self.find_wow_button.setStyleSheet("")

    def kill_bot(self):
        self.bot.dead_UI = True
예제 #32
0
class FixateGUI(QtWidgets.QMainWindow, layout.Ui_FixateUI):
    """
    GUI Main window
    """
    # QT Signals
    # These are the thread safe signals to update UI elements
    # Multiple Choices/ OK  signal
    sig_choices_input = pyqtSignal(str, tuple)
    # Updates the test Information above the image
    sig_label_update = pyqtSignal(str, str)
    # Signal for the text user input
    sig_text_input = pyqtSignal(str)
    # Timer for abort cleanup. TODO Rethink?
    sig_timer = pyqtSignal()
    # Tree Events
    sig_tree_init = pyqtSignal(list)
    sig_tree_update = pyqtSignal(str, str)
    # Active Window
    sig_active_update = pyqtSignal(str)
    sig_active_clear = pyqtSignal()
    # History Window
    sig_history_update = pyqtSignal(str)
    sig_history_clear = pyqtSignal(str)
    # Error Window
    sig_error_update = pyqtSignal(str)
    sig_error_clear = pyqtSignal(str)
    # Image Window
    sig_image_update = pyqtSignal(str)
    sig_image_clear = pyqtSignal()

    # Progress Signals
    sig_indicator_start = pyqtSignal()
    sig_indicator_stop = pyqtSignal()
    sig_working = pyqtSignal()
    sig_progress = pyqtSignal()
    sig_finish = pyqtSignal()

    # Deprecated Replace with Active , History and Error Window signals
    output_signal = pyqtSignal(str, str)
    # Deprecated replace with Image Window signals
    update_image = pyqtSignal(str, bool)

    """Class Constructor and destructor"""

    def __init__(self, worker, application):
        super(FixateGUI, self).__init__(None)
        self.application = application
        self.register_events()
        self.setupUi(self)
        self.treeSet = False
        self.blocked = False
        self.closing = False

        # Extra GUI setup not supported in the designer
        self.TestTree.setColumnWidth(1, 90)
        self.TestTree.header().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
        self.TestTree.header().setSectionResizeMode(1, QtWidgets.QHeaderView.Fixed)

        self.base_image = ""
        self.dialog = None
        self.image_scene = QtWidgets.QGraphicsScene()
        self.ImageView.set_scene(self.image_scene)
        self.ImageView.setScene(self.image_scene)

        self.working_indicator = QtGui.QMovie(QT_GUI_WORKING_INDICATOR)
        self.WorkingIndicator.setMovie(self.working_indicator)
        self.start_indicator()

        self.status_code = -1  # Default status code used to check for unusual exit

        # Timers and Threads
        self.input_queue = Queue()
        self.abort_timer = QtCore.QTimer(self)
        self.abort_timer.timeout.connect(self.abort_check)
        self.worker = SequencerThread(worker)
        self.worker_thread = QThread()
        self.worker.moveToThread(self.worker_thread)
        self.worker_thread.started.connect(self.worker.run_thread)

        self.user_action_queue = None
        self.abort_queue = None

        # UI Binds
        self.Button_1.clicked.connect(self.button_1_click)
        self.Button_2.clicked.connect(self.button_2_click)
        self.Button_3.clicked.connect(self.button_3_click)
        self.UserInputBox.submit.connect(self.text_input_submit)

        self.bind_qt_signals()
        sys.excepthook = exception_hook  # TODO DEBUG REMOVE

    def run_sequencer(self):
        self.worker_thread.start()

    def closeEvent(self, event):
        """ This function overrides closeEvent from the MainWindow class, called in case of unusual termination"""

        event.ignore()
        self.hide()
        self.clean_up()

    def bind_qt_signals(self):
        """
        Binds the qt signals to the appropriate handlers
        :return:
        """
        # Signal Binds
        self.sig_finish.connect(self.clean_up)  # Normal termination
        self.sig_choices_input.connect(self.get_input)
        self.sig_label_update.connect(self.display_test)
        self.sig_text_input.connect(self.open_text_input)
        self.sig_timer.connect(self.start_timer)
        self.sig_tree_init.connect(self.display_tree)
        self.sig_tree_update.connect(self.update_tree)
        self.sig_progress.connect(self.progress_update)

        # New Binds
        self.sig_indicator_start.connect(self._start_indicator)
        self.sig_indicator_stop.connect(self._stop_indicator)
        self.sig_active_update.connect(self._active_update)
        self.sig_active_clear.connect(self._active_clear)
        self.sig_history_update.connect(self.history_update)
        self.sig_history_clear.connect(self.history_clear)
        self.sig_error_update.connect(self.error_update)
        self.sig_error_clear.connect(self.error_clear)
        self.sig_image_update.connect(self._image_update)
        self.sig_image_clear.connect(self._image_clear)
        # Deprecated
        # self.update_image.connect(self.display_image)
        # self.output_signal.connect(self.display_output)
        # self.working.connect(self.start_indicator)

    """Pubsub handlers for setup and teardown
       These are run in the main thread"""

    def register_events(self):
        pub.subscribe(self._seq_abort, "Sequence_Abort")
        pub.subscribe(self._user_ok, 'UI_req')
        pub.subscribe(self._user_choices, "UI_req_choices")
        pub.subscribe(self._user_input, 'UI_req_input')
        pub.subscribe(self._user_display, 'UI_display')
        pub.subscribe(self._user_display_important, "UI_display_important")
        pub.subscribe(self._user_action, 'UI_action')
        pub.subscribe(self._completion_code, 'Finish')
        # Image Window
        pub.subscribe(self.image_update, "UI_image")
        pub.subscribe(self.image_clear, "UI_image_clear")
        pub.subscribe(self.image_clear, "UI_block_end")
        # Active Window
        pub.subscribe(self.active_clear, "UI_block_end")
        # Multi Window
        pub.subscribe(self._print_test_start, 'Test_Start')
        pub.subscribe(self._print_test_seq_start, 'TestList_Start')
        pub.subscribe(self._print_test_complete, 'Test_Complete')
        pub.subscribe(self._print_comparisons, 'Check')
        pub.subscribe(self._print_errors, "Test_Exception")
        pub.subscribe(self._print_sequence_end, "Sequence_Complete")
        pub.subscribe(self._print_test_skip, 'Test_Skip')
        pub.subscribe(self._print_test_retry, 'Test_Retry')

        # Error Window

        # Working Indicator
        pub.subscribe(self.start_indicator, 'Test_Start')
        pub.subscribe(self.start_indicator, 'UI_block_end')
        pub.subscribe(self.stop_indicator, 'UI_block_start')

        return

    def unregister_events(self):
        pub.unsubAll()
        return

    """Slot handlers for thread-gui interaction
       These are run in the main thread"""

    def start_timer(self):
        self.abort_timer.start(100)

    def abort_check(self):
        if self.abort_queue is None:
            return
        try:
            self.abort_queue.get_nowait()
            self.abort_queue = None
            self.button_reset(True)
            self.abort_timer.stop()
        except Empty:
            return

    def open_text_input(self, message):
        self.ActiveEvent.append(message)
        self.ActiveEvent.verticalScrollBar().setValue(self.ActiveEvent.verticalScrollBar().maximum())
        self.Events.append(message)
        self.Events.verticalScrollBar().setValue(self.Events.verticalScrollBar().maximum())
        self.UserInputBox.setPlaceholderText("Input:")
        self.UserInputBox.setEnabled(True)
        self.UserInputBox.setFocus()

    def start_indicator(self, **kwargs):
        self.sig_indicator_start.emit()

    def _start_indicator(self):
        self.WorkingIndicator.show()
        self.working_indicator.start()

    def stop_indicator(self):
        self.sig_indicator_stop.emit()

    def _stop_indicator(self):
        self.working_indicator.stop()
        self.WorkingIndicator.hide()

    def retrieve_packaged_data(self, path):
        try:
            return pkgutil.get_data("module.loaded_tests", path)
        except FileNotFoundError:
            return b""

    def image_update(self, path):
        self.sig_image_update.emit(path)

    def _image_update(self, path):
        """
        Adds an image to the image viewer. These images can be stacked with transparent layers to form overlays
        :param path: Relative path to image within the test scripts package
        :return: None
        """
        image = QtGui.QPixmap()
        image.loadFromData(self.retrieve_packaged_data(path))
        if image.isNull():
            self.file_not_found(path)
        self.image_scene.addPixmap(image)
        self.ImageView.fitInView(0, 0, self.image_scene.width(), self.image_scene.height(), QtCore.Qt.KeepAspectRatio)

    def image_clear(self):
        self.sig_image_clear.emit()

    def _image_clear(self):
        self.image_scene.clear()

    def display_image(self, path="", overlay=False):
        if path == "" or not overlay:
            self.image_scene.clear()
            if overlay:
                image = QtGui.QPixmap()
                image.loadFromData(self.base_image)
                if image.isNull():
                    self.file_not_found(self.base_image)
            elif path == "":
                self.base_image = path
                return
            else:
                self.base_image = self.retrieve_packaged_data(path)
                image = QtGui.QPixmap()
                image.loadFromData(self.base_image)
                if image.isNull():
                    self.file_not_found(path)
            self.image_scene.addPixmap(image)
            self.ImageView.fitInView(0, 0, self.image_scene.width(), self.image_scene.height(),
                                     QtCore.Qt.KeepAspectRatio)
            return
        image = QtGui.QPixmap()
        image.loadFromData(self.retrieve_packaged_data(path))
        if image.isNull():
            self.file_not_found(path)
        self.image_scene.addPixmap(image)
        self.ImageView.fitInView(0, 0, self.image_scene.width(), self.image_scene.height(), QtCore.Qt.KeepAspectRatio)
        return

    def file_not_found(self, path):
        """
        Display warning box for an invalid image path
        :param path:
        :return:
        """

        self.dialog = QtWidgets.QMessageBox()
        self.dialog.setText("Warning: Image not Found")
        self.dialog.setInformativeText("Filename: {}".format(path))
        self.dialog.setStandardButtons(QtWidgets.QMessageBox.Ok)
        self.dialog.setDefaultButton(QtWidgets.QMessageBox.Ok)
        self.dialog.setIcon(QtWidgets.QMessageBox.Warning)
        self.dialog.exec()

    def display_tree(self, tree):

        # Make sure this function is only run once
        if self.treeSet:
            return
        self.treeSet = True

        level_stack = []
        for item in tree:
            # Check Level
            if item[0].count('.') + 1 <= len(level_stack):  # Case 1: Going out one or more levels or same level
                for _ in range(len(level_stack) - item[0].count('.')):
                    level_stack.pop()
            elif item[0].count('.') + 1 > len(level_stack):  # Case 2: Going in one or more levels
                for index in range(item[0].count('.') + 1 - len(level_stack), 0, -1):
                    split_index = item[0].split('.')
                    if index > 1:  # More than one level, append dummy items as required
                        dummy = QtWidgets.QTreeWidgetItem()
                        dummy.setText(0, '.'.join(split_index[:-(index - 1)]))
                        dummy.setText(1, 'Queued')
                        dummy.setTextAlignment(1, QtCore.Qt.AlignRight)
                        level_stack.append(dummy.clone())

            tree_item = QtWidgets.QTreeWidgetItem()
            tree_item.setText(0, item[0] + '. ' + item[1])
            tree_item.setTextAlignment(1, QtCore.Qt.AlignRight)
            tree_item.setText(1, 'Queued')

            level_stack.append(tree_item.clone())
            if len(level_stack) > 1:  # Child Add
                level_stack[-2].addChild(level_stack[-1])
            else:  # Top Level
                self.TestTree.addTopLevelItem(level_stack[-1])

    def update_tree(self, test_index, status):

        if len(test_index) == 0:
            return

        colours = get_status_colours(status)
        test_index = test_index.split('.')

        #   Find the test in the tree
        current_test = self.TestTree.findItems(test_index[0], QtCore.Qt.MatchStartsWith, 0)[0]
        while len(test_index) > 1:
            test_index[0:2] = [''.join(test_index[0] + '.' + test_index[1])]
            for child_index in range(current_test.childCount()):
                if current_test.child(child_index).text(0).startswith(test_index[0]):
                    current_test = current_test.child(child_index)
                    break

        # Update the test
        if status not in ["Aborted"]:
            for i in range(2):
                current_test.setBackground(i, colours[0])
                current_test.setForeground(i, colours[1])
            current_test.setText(1, status)
            current_test.setExpanded(True)

        # In case of an abort, update all remaining tests
        else:
            self.active_update("Aborting, please wait...")
            sub_finish = False
            original_test = current_test
            while current_test is not None:
                if current_test.text(1) in ["Queued"]:
                    for i in range(2):
                        current_test.setBackground(i, colours[0])
                        current_test.setForeground(i, colours[1])
                    current_test.setText(1, status)
                    current_test.setExpanded(False)
                if current_test.childCount() > 0 and not sub_finish:  # Go in a level
                    current_test = current_test.child(0)
                    sub_finish = False
                elif current_test.parent() is not None:
                    if current_test.parent().indexOfChild(
                            current_test) >= current_test.parent().childCount() - 1:  # Come out a level
                        sub_finish = True
                        current_test = current_test.parent()
                    else:
                        current_test = current_test.parent().child(
                            current_test.parent().indexOfChild(current_test) + 1)  # Same level
                        sub_finish = False
                else:  # Top level test, go to next test
                    current_test = self.TestTree.topLevelItem(self.TestTree.indexOfTopLevelItem(current_test) + 1)
                    sub_finish = False
            current_test = original_test

        # Check for last test in group
        while current_test.parent() is not None and (current_test.parent().indexOfChild(
                current_test) >= current_test.parent().childCount() - 1 or status in ["Aborted"]):
            parent_status = current_test.text(1)
            current_test = current_test.parent()
            for child_index in range(current_test.childCount()):  # Check status of all child tests
                check_status = current_test.child(child_index).text(1)
                if list(STATUS_PRIORITY.keys()).index(check_status) < list(STATUS_PRIORITY.keys()).index(parent_status):
                    parent_status = check_status
            colours = get_status_colours(parent_status)
            for i in range(2):
                current_test.setBackground(i, colours[0])
                current_test.setForeground(i, colours[1])
            current_test.setText(1, parent_status)
            if parent_status not in ["In Progress"]:
                current_test.setExpanded(False)

    def display_test(self, test_index, description):
        self.ActiveTest.setText("Test {}:".format(test_index))
        self.TestDescription.setText("{}".format(description))

    def active_update(self, msg, **kwargs):
        self.sig_active_update.emit(msg)

    def _active_update(self, message):
        self.ActiveEvent.append(message)
        self.ActiveEvent.verticalScrollBar().setValue(self.ActiveEvent.verticalScrollBar().maximum())

    def active_clear(self, **kwargs):
        self.sig_active_clear.emit()

    def _active_clear(self):
        self.ActiveEvent.clear()
        self.ActiveEvent.verticalScrollBar().setValue(self.ActiveEvent.verticalScrollBar().maximum())

    def history_update(self, message):
        self.Events.append(message)
        self.Events.verticalScrollBar().setValue(self.Events.verticalScrollBar().maximum())

    def history_clear(self):
        self.Events.clear()
        self.Events.verticalScrollBar().setValue(self.Events.verticalScrollBar().maximum())

    def error_update(self, message):
        self.Errors.append(message)
        self.Errors.verticalScrollBar().setValue(self.Errors.verticalScrollBar().maximum())

    def error_clear(self):
        self.Errors.clear()
        self.Errors.verticalScrollBar().setValue(self.Errors.verticalScrollBar().maximum())

    # def display_output(self, message, status):
    #     self.Events.append(message)
    #     self.Events.verticalScrollBar().setValue(self.Events.verticalScrollBar().maximum())
    #
    #     if status == "False":  # Print errors
    #         self.Errors.append(self.ActiveTest.text() + ' - ' + message[1:])
    #         self.Errors.verticalScrollBar().setValue(self.Errors.verticalScrollBar().maximum())
    #
    #     if status in ["Active", "False"]:
    #         self.ActiveEvent.append(message)
    #         self.ActiveEvent.verticalScrollBar().setValue(self.ActiveEvent.verticalScrollBar().maximum())

    def progress_update(self):
        self.ActiveEvent.clear()
        self.ProgressBar.setValue(self.worker.worker.get_current_task())
        if self.worker.worker.sequencer.tests_failed > 0 or self.worker.worker.sequencer.tests_errored > 0:
            self.ProgressBar.setStyleSheet(ERROR_STYLE)

    def get_input(self, message, choices):
        self.Events.append(message)
        self.ActiveEvent.append(message)
        self.Events.verticalScrollBar().setValue(self.Events.verticalScrollBar().maximum())
        self.ActiveEvent.verticalScrollBar().setValue(self.ActiveEvent.verticalScrollBar().maximum())
        if isinstance(choices, bool):
            pass
        elif len(choices) == 1:
            self.Button_2.setText(choices[0])
            self.Button_2.setShortcut(QtGui.QKeySequence(choices[0][0:1]))
            self.Button_2.setEnabled(True)
            self.Button_2.setDefault(True)
            self.Button_2.setFocus()
        elif len(choices) == 2:
            self.Button_1.setText(choices[0])
            self.Button_1.setShortcut(QtGui.QKeySequence(choices[0][0:1]))
            self.Button_1.setEnabled(True)
            self.Button_1.setDefault(True)
            self.Button_1.setFocus()
            self.Button_3.setText(choices[1])
            self.Button_3.setShortcut(QtGui.QKeySequence(choices[1][0:1]))
            self.Button_3.setEnabled(True)
        else:
            self.Button_1.setText(choices[0])
            self.Button_1.setShortcut(QtGui.QKeySequence(choices[0][0:1]))
            self.Button_1.setEnabled(True)
            self.Button_1.setDefault(True)
            self.Button_1.setFocus()
            self.Button_2.setText(choices[1])
            self.Button_2.setShortcut(QtGui.QKeySequence(choices[1][0:1]))
            self.Button_2.setEnabled(True)
            self.Button_3.setText(choices[2])
            self.Button_3.setShortcut(QtGui.QKeySequence(choices[2][0:1]))
            self.Button_3.setEnabled(True)

    def _seq_abort(self, exception=None):
        """
        This function ensures that sequence aborting is handled correctly if the sequencer is blocked waiting for input
        """

        # Release user input waiting loops
        if self.user_action_queue is not None:
            self.user_action_queue.put(False)
            self.user_action_queue = None
        if self.abort_queue is not None:
            self.abort_queue.put(True)
            self.abort_queue = None

        # Release sequence blocking calls
        if self.blocked:
            self.input_queue.put("ABORT_FORCE")

    def clean_up(self):
        """
        This function is the second one called for normal termination, and the first one called for unusual termination.
        Check for abnormal termination, and stop the sequencer if required; then stop and delete the thread
        """

        if self.worker_thread is None:  # This function has already run, therefore main already has the status code
            return

        # The following actions must be done in a specific order, be careful when making changes to this section
        self.abort_timer.stop()
        self.closing = True

        if self.status_code == -1:  # Unusual termination - The sequencer hasn't finished yet, stop it
            self.status_code = self.worker.worker.stop()

        self.unregister_events()  # Prevent interruption by pubsub messages

        self.worker.deleteLater()  # Schedule the thread worker for deletion
        self.worker = None  # Remove the reference to allow the GC to clean up

        self.worker_thread.exit(self.status_code)  # Exit the thread
        self.worker_thread.wait(2000)  # 2 seconds for the thread to exit
        self.worker_thread.terminate()  # Force quit the thread if it is still running, if so, this will throw a warning
        self.worker_thread.deleteLater()  # Schedule the thread for deletion
        self.worker_thread = None  # Remove the reference to allow the GC to clean up

        #   Now close the GUI thread, return to the controller in main
        self.application.exit(self.status_code)

    """User IO handlers, emit signals to trigger main thread updates via slots.
       These are run in the sequencer thread"""

    def event_output(self, message, status="True"):
        self.output_signal.emit(message, str(status))

    def gui_text_input(self, message):
        self.sig_text_input.emit(message)
        self.blocked = True
        result = self.input_queue.get(True)
        self.blocked = False
        self.sig_working.emit()
        return result

    def gui_choices(self, message, choices):
        self.sig_choices_input.emit(message, choices)
        self.blocked = True
        result = self.input_queue.get(True)
        self.blocked = False
        self.sig_working.emit()
        return result

    def gui_user_action_pass_fail(self, message, q, abort):
        """
        Non blocking user call
        :param message:
        :param q:
        :param abort:
        :return:
        """
        self.sig_choices_input.emit(message, ["PASS", "FAIL"])
        self.sig_timer.emit()
        self.user_action_queue = q
        self.abort_queue = abort

    def gui_user_action_fail(self, message, q, abort):
        self.sig_choices_input.emit(message, ["FAIL"])
        self.sig_timer.emit()
        self.user_action_queue = q
        self.abort_queue = abort

    def gui_user_input(self, message, choices=None, blocking=True):
        result = None
        if choices is not None:  # Button Prompt
            if blocking:
                self.sig_choices_input.emit(message, choices)
            else:
                self.sig_choices_input.emit(message, (choices[0],))
                self.sig_timer.emit()
        else:  # Text Prompt
            self.sig_text_input.emit(message)

        if blocking:  # Block sequencer until user responds
            self.blocked = True
            result = self.input_queue.get(True)
            self.blocked = False
            self.sig_working.emit()
        else:
            self.user_action_queue = choices[1]
            self.abort_queue = choices[2]
        return result

    """UI Event Handlers, process actions taken by the user on the GUI.
       These are run in the main thread """

    def text_input_submit(self):
        self.input_queue.put(self.UserInputBox.toPlainText())
        self.UserInputBox.clear()
        self.UserInputBox.setPlaceholderText("")
        self.UserInputBox.setEnabled(False)

    def button_1_click(self):
        self.input_queue.put(self.Button_1.text())
        self.button_reset()

    def button_2_click(self):
        if self.user_action_queue is not None:
            self.user_action_queue.put(self.Button_2.text())
            self.user_action_queue = None
            self.abort_timer.stop()
            self.abort_queue = None
        else:
            self.input_queue.put(self.Button_2.text())
        self.button_reset()

    def button_3_click(self):
        self.input_queue.put(self.Button_3.text())
        self.button_reset()

    def button_reset(self, fail_only=False):
        self.Button_2.setText("")
        self.Button_2.setEnabled(False)
        self.Button_2.setDefault(False)
        if not fail_only:
            self.Button_1.setText("")
            self.Button_3.setText("")
            self.Button_1.setEnabled(False)
            self.Button_3.setEnabled(False)
            self.Button_1.setDefault(False)
            self.Button_3.setDefault(False)

    """Thread listener, called from the sequencer thread"""

    def _completion_code(self, code):
        """This function is the first one called when the sequencer completes normally.
           Set the exit code, and signal the main thread."""
        self.status_code = code
        self.sig_finish.emit()

    """UI Callables, called from the sequencer thread"""

    def reformat_text(self, text_str, first_line_fill="", subsequent_line_fill=""):
        lines = []
        wrapper.initial_indent = first_line_fill
        wrapper.subsequent_indent = subsequent_line_fill
        for ind, line in enumerate(text_str.splitlines()):
            if ind != 0:
                wrapper.initial_indent = subsequent_line_fill
            lines.append(wrapper.fill(line))
        return '\n'.join(lines)

    # def _image(self, path, overlay):
    #     if self.closing:
    #         return
    #     self.update_image.emit(path, overlay)

    def _user_action(self, msg, q, abort):
        """
        This is for tests that aren't entirely dependant on the automated system.
        This works by monitoring a queue to see if the test completed successfully.
        Also while doing this it is monitoring if the escape key is pressed to signal to the system that the test fails.

        Use this in situations where you want the user to do something (like press all the keys on a keypad) where the
        system is automatically monitoring for success but has no way of monitoring failure.
        :param msg:
         Information for the user
        :param q:
         The queue object to put false if the user fails the test
        :param abort:
         The queue object to abort this monitoring as the test has already passed.
        :return:
        None
        """
        if self.closing:
            q.put(False)
            abort.put(True)
            return
        self.gui_user_input(self.reformat_text(msg), ("Fail", q, abort), False)

    def _user_ok(self, msg, q):
        """
        This can be replaced anywhere in the project that needs to implement the user driver
        The result needs to be put in the queue with the first part of the tuple as 'Exception' or 'Result' and the
        second part is the exception object or response object
        :param msg:
         Message for the user to understand what to do
        :param q:
         The result queue of type queue.Queue
        :return:
        """
        if self.closing:
            q.put("Result", None)
            return
        self.gui_user_input(msg, ("Continue",))
        q.put("Result", None)

    def _user_choices(self, msg, q, choices, target, attempts=5):
        """
        This can be replaced anywhere in the project that needs to implement the user driver
        Temporarily a simple input function.
        The result needs to be put in the queue with the first part of the tuple as 'Exception' or 'Result' and the
        second part is the exception object or response object
        This needs to be compatible with forced exit. Look to user action for how it handles a forced exit
        :param msg:
         Message for the user to understand what to input
        :param q:
         The result queue of type queue.Queue
        :param target:
         Optional
         Validation function to check if the user response is valid
        :param attempts:
        :return:
        """
        if self.closing:
            q.put(("Result", "ABORT_FORCE"))
            return

        for _ in range(attempts):
            # This will change based on the interface
            ret_val = self.gui_user_input(self.reformat_text(msg), choices)
            ret_val = target(ret_val, choices)
            if ret_val:
                q.put(('Result', ret_val))
                return
        q.put('Exception', UserInputError("Maximum number of attempts {} reached".format(attempts)))

    def _user_input(self, msg, q, target=None, attempts=5, kwargs=None):
        """
        This can be replaced anywhere in the project that needs to implement the user driver
        Temporarily a simple input function.
        The result needs to be put in the queue with the first part of the tuple as 'Exception' or 'Result' and the
        second part is the exception object or response object
        This needs to be compatible with forced exit. Look to user action for how it handles a forced exit
        :param msg:
         Message for the user to understand what to input
        :param q:
         The result queue of type queue.Queue
        :param target:
         Optional
         Validation function to check if the user response is valid
        :param attempts:

        :param kwargs:
        :return:
        """
        if self.closing:
            q.put(('Result', "ABORT_FORCE"))
            return

        msg = self.reformat_text(msg)
        wrapper.initial_indent = ""
        wrapper.subsequent_indent = ""
        for _ in range(attempts):
            # This will change based on the interface
            ret_val = self.gui_user_input(msg, None, True)
            if target is None or ret_val == "ABORT_FORCE":
                q.put(ret_val)
                return
            ret_val = target(ret_val, **kwargs)
            if ret_val:
                q.put(('Result', ret_val))
                return
        q.put('Exception', UserInputError("Maximum number of attempts {} reached".format(attempts)))

    def _user_display(self, msg):
        """
        :param msg:
        :return:
        """
        if self.closing:
            return

        self.history_update(self.reformat_text(msg))

    def _user_display_important(self, msg):
        """
        :param msg:
        :return:
        """
        if self.closing:
            return

        self.history_update("")
        self.history_update("!" * wrapper.width)
        self.active_update("!" * wrapper.width)
        self.history_update("")
        self.history_update(self.reformat_text(msg))
        self.active_update(self.reformat_text(msg))
        self.history_update("")
        self.history_update("!" * wrapper.width)
        self.active_update("!" * wrapper.width)

    def _print_sequence_end(self, status, passed, failed, error, skipped, sequence_status):
        if self.closing:
            return

        self.history_update("#" * wrapper.width)
        self.history_update(self.reformat_text("Sequence {}".format(sequence_status)))
        # self.history_update("Sequence {}".format(sequence_status))
        post_sequence_info = []
        if status == "PASSED":
            post_sequence_info.extend(RESOURCES["SEQUENCER"].context_data.get("_post_sequence_info_pass", []))
        elif status == "FAILED" or status == "ERROR":
            post_sequence_info.extend(RESOURCES["SEQUENCER"].context_data.get("_post_sequence_info_fail", []))
        post_sequence_info.extend(RESOURCES["SEQUENCER"].context_data.get("_post_sequence_info", []))

        if post_sequence_info:
            self.history_update("-" * wrapper.width)
            self.history_update("IMPORTANT INFORMATION")
            for itm in post_sequence_info:
                self.history_update(self.reformat_text(itm))
        self.history_update("-" * wrapper.width)
        self.history_update(self.reformat_text("Status: {}".format(status)))
        self.history_update("#" * wrapper.width)

    def _print_test_start(self, data, test_index):
        if self.closing:
            return

        self.sig_progress.emit()
        self.history_update("*" * wrapper.width)
        self.history_update(self.reformat_text("Test {}: {}".format(test_index, data.test_desc)))
        self.history_update("-" * wrapper.width)
        self.sig_label_update.emit(test_index, data.test_desc)
        self.sig_tree_update.emit(test_index, "In Progress")

    def _print_test_seq_start(self, data, test_index):
        if self.closing:
            return

        self.ProgressBar.setMaximum(self.worker.worker.get_task_count())
        self.sig_tree_init.emit(self.worker.worker.get_test_tree())
        self.sig_progress.emit()
        self._print_test_start(data, test_index)

    def _print_test_complete(self, data, test_index, status):
        if self.closing:
            return

        sequencer = RESOURCES["SEQUENCER"]
        self.history_update("-" * wrapper.width)
        self.history_update(
            self.reformat_text("Checks passed: {}, Checks failed: {}".format(sequencer.chk_pass, sequencer.chk_fail)))
        # self.history_update("Checks passed: {}, Checks failed: {}".format(sequencer.chk_pass, sequencer.chk_fail))
        self.history_update(self.reformat_text("Test {}: {}".format(test_index, status.upper())))
        # self.history_update("Test {}: {}".format(test_index, status.upper()))
        self.history_update("-" * wrapper.width)

        if status.upper() in ["ERROR", "SKIPPED"]:
            return

        if sequencer.chk_fail == 0:
            self.sig_tree_update.emit(test_index, "Passed")
        else:
            self.sig_tree_update.emit(test_index, "Failed")

    def _print_test_skip(self, data, test_index):
        if self.closing:
            return

        self.history_update("\nTest Marked as skip")
        self.sig_tree_update.emit(test_index, "Skipped")

    def _print_test_retry(self, data, test_index):
        if self.closing:
            return

        self.history_update(self.reformat_text("\nTest {}: Retry".format(test_index)))

    def _print_errors(self, exception, test_index):
        if self.closing:
            return

        if isinstance(exception, SequenceAbort):
            self.sig_tree_update.emit(test_index, "Aborted")
            status = True
        else:
            status = False
            self.sig_tree_update.emit(test_index, "Error")
        self.history_update("")
        self.history_update("!" * wrapper.width)
        self.active_update("!" * wrapper.width)
        self.history_update(
            self.reformat_text("Test {}: Exception Occurred, {} {}".format(test_index, type(exception), exception)))
        self.active_update(
            self.reformat_text("Test {}: Exception Occurred, {} {}".format(test_index, type(exception), exception)))
        self.history_update("!" * wrapper.width)
        self.active_update("!" * wrapper.width)
        # TODO self.history_update traceback into a debug log file
        if fixate.config.DEBUG:
            traceback.print_tb(exception.__traceback__, file=sys.stderr)

    def round_to_3_sig_figures(self, chk):
        """
        Tries to round elements to 3 significant figures for formatting
        :param chk:
        :return:
        """
        ret_dict = {}
        for element in ["_min", "_max", "test_val", "nominal", "tol"]:
            ret_dict[element] = getattr(chk, element, None)
            try:
                ret_dict[element] = "{:.3g}".format(ret_dict[element])
            except:
                pass
        return ret_dict

    def _print_comparisons(self, passes, chk, chk_cnt, context):
        if passes:
            status = "PASS"
        else:
            status = "FAIL"
        format_dict = self.round_to_3_sig_figures(chk)
        if chk._min is not None and chk._max is not None:
            msg = self.reformat_text(
                "\nCheck {chk_cnt}: {status} when comparing {test_val} {comparison} {_min} - {_max} : "
                "{description}".format(
                    status=status,
                    comparison=chk.target.__name__[1:].replace('_', ' '),
                    chk_cnt=chk_cnt,
                    description=chk.description, **format_dict))
            self.history_update(msg)
            if status == "FAIL":
                self.active_update(msg)
        elif chk.nominal is not None and chk.tol is not None:
            msg = self.reformat_text(
                "\nCheck {chk_cnt}: {status} when comparing {test_val} {comparison} {nominal} +- {tol}% : "
                "{description}".format(
                    status=status,
                    comparison=chk.target.__name__[1:].replace('_', ' '),
                    chk_cnt=chk_cnt,
                    description=chk.description, **format_dict))
            self.history_update(msg)
            if status == "FAIL":
                self.active_update(msg)
        elif chk._min is not None or chk._max is not None or chk.nominal is not None:
            # Grabs the first value that isn't none. Nominal takes priority
            comp_val = next(format_dict[item] for item in ["nominal", "_min", "_max"] if format_dict[item] is not None)
            msg = self.reformat_text("\nCheck {chk_cnt}: {status} when comparing {test_val} {comparison} {comp_val} : "
                                     "{description}".format(
                status=status,
                comparison=chk.target.__name__[1:].replace('_', ' '),
                comp_val=comp_val,
                chk_cnt=chk_cnt,
                description=chk.description, **format_dict))
            self.history_update(msg)
            if status == "FAIL":
                self.active_update(msg)
        else:
            if chk.test_val is not None:
                msg = self.reformat_text(
                    "\nCheck {chk_cnt}: {status}: {test_val} : {description}".format(
                        chk_cnt=chk_cnt,
                        description=chk.description,
                        status=status, **format_dict))
                self.history_update(msg)
                if status == "FAIL":
                    self.active_update(msg)
            else:
                msg = self.reformat_text(
                    "\nCheck {chk_cnt} : {status}: {description}".format(description=chk.description, chk_cnt=chk_cnt,
                                                                         status=status))
                self.history_update(msg)
                if status == "FAIL":
                    self.active_update(msg)