Ejemplo n.º 1
0
def run(optimiser):
    app = QApplication(sys.argv)

    # Thread for running slow parts of the optimiser without pausing GUI

    opt_worker = OptWorker(optimiser)
    opt_thread = QThread()

    opt_worker.moveToThread(opt_thread)

    app.aboutToQuit.connect(opt_thread.quit)

    opt_thread.start()

    # Queue and thread for updating text field
    queue = Queue()
    sys.stdout = WriteStream(queue)

    window = BayesOptWindow(optimiser, opt_worker)
    window.show()

    write_thread = QThread()
    receiver = Receiver(queue)
    receiver.signal.connect(window.write_to_textfield)
    receiver.moveToThread(write_thread)
    write_thread.started.connect(receiver.run)
    app.aboutToQuit.connect(write_thread.quit)

    write_thread.start()

    # app.exec_()

    sys.exit(app.exec_())
Ejemplo n.º 2
0
def main():
    # initialize
    app = QApplication([])

    # create connection to the server
    client = Client(address=('127.0.0.1', 8888), debug=True)
    client.client_connect()

    # create a main window
    window = MainWindow(window_width=400, window_height=600)
    window.setWindowTitle("Python chat")

    # waiting for messages
    global client_worker, network_thread  # TODO: refactor this
    network_thread = QThread()
    network_thread.setTerminationEnabled(True)
    client_worker = ClientWorker(client_socket=client.client())
    client_worker.recieved_message.connect(window.recieved_message_handler)
    client_worker.moveToThread(network_thread)
    network_thread.started.connect(client_worker.start)
    network_thread.start()

    window.show()

    return app.exec_()
Ejemplo n.º 3
0
class Window(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.clicksCount = 0
        self.setupUi()

    def setupUi(self):
        self.setWindowTitle("Freezing GUI")
        self.resize(300, 150)
        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)
        # Create and connect widgets
        self.clicksLabel = QLabel("Counting: 0 clicks", self)
        self.clicksLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self.stepLabel = QLabel("Long-Running Step: 0")
        self.stepLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self.countBtn = QPushButton("Click me!", self)
        self.countBtn.clicked.connect(self.countClicks)
        self.longRunningBtn = QPushButton("Long-Running Task!", self)
        self.longRunningBtn.clicked.connect(self.runLongTask)
        # Set the layout
        layout = QVBoxLayout()
        layout.addWidget(self.clicksLabel)
        layout.addWidget(self.countBtn)
        layout.addStretch()
        layout.addWidget(self.stepLabel)
        layout.addWidget(self.longRunningBtn)
        self.centralWidget.setLayout(layout)

    def countClicks(self):
        self.clicksCount += 1
        self.clicksLabel.setText(f"Counting: {self.clicksCount} clicks")

    def reportProgress(self, n):
        self.stepLabel.setText(f"Long-Running Step: {n}")

    def runLongTask(self):
        # 创建 QThread
        self.thread = QThread()
        # 创建 worker 对象
        self.worker = Worker()
        # 将 work 移到 thread
        self.worker.moveToThread(self.thread)
        # 连接信号和槽
        self.thread.started.connect(self.worker.run)
        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.worker.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)
        self.worker.progress.connect(self.reportProgress)

        # 启动线程
        self.thread.start()

        # 结束
        self.longRunningBtn.setEnabled(False)
        self.thread.finished.connect(
            lambda: self.longRunningBtn.setEnabled(True))
        self.thread.finished.connect(
            lambda: self.stepLabel.setText("Long-Running Step: 0"))
Ejemplo n.º 4
0
class ConstraintCleanerPlugin:
    name = 'Constraint Cleaner'
    description = 'Cleans up duplicate constraints\nand disables redundant constraints'
    hidden = True

    def __init__(self, api: PluginApi) -> None:
        self.api = api

    def load(self) -> None:
        #self.action_remove_duplicate = self.api.register_menu_entry('Remove duplicate constraints', self.slot_remove_duplicate)
        self.action_remove_redundant = self.api.register_menu_entry(
            'Remove redundant constraints', self.slot_remove_redundant)

    def unload(self) -> None:
        #self.api.remove_menu_entry(self.action_remove_duplicate)
        self.api.remove_menu_entry(self.action_remove_redundant)

    def slot_remove_duplicate(self) -> None:
        # TODO
        pass

    def slot_remove_redundant(self) -> None:
        '''
        Disables all constraints that only contain redundant information and don't create more relations
        '''

        progress_dialog = self.api.get_progress_dialog(
            'Constraint Cleaner', 'Removing redundant constraints...', False)
        progress_dialog.show()

        self.thread = QThread()
        self.worker = RemoveRedundantWorker()
        self.worker.moveToThread(self.thread)

        self.worker.signal_progress.connect(
            lambda progress: progress_dialog.set_progress(progress))
        self.worker.signal_done.connect(
            lambda: (  # https://stackoverflow.com/a/13672943
                self.thread.quit(), progress_dialog.close(),
                QMessageBox.information(
                    self.api.main_window, 'Constraint Cleaner',
                    'All redundant constraints are removed.')))
        self.worker.signal_fail.connect(lambda: (
            self.thread.quit(), progress_dialog.close(),
            QMessageBox.critical(
                self.api.main_window, 'Constraint Cleaner',
                'Failed to add a constraint.\nSee console for more information.'
            )))

        self.thread.started.connect(self.worker.process)
        self.thread.start()
Ejemplo n.º 5
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        #ui
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.ui.startButton.clicked.connect(self.handleClicked)
        self.checkboxes = [
            self.ui.checkBox, self.ui.checkBox_2, self.ui.checkBox_3,
            self.ui.checkBox_4, self.ui.checkBox_5, self.ui.checkBox_6
        ]

    def handleClicked(self):
        if not self.ui.locationInput.text():
            msgBox = QMessageBox()
            msgBox.setIcon(QMessageBox.Warning)
            msgBox.setText("지역을 설정해주세요.")

        else:
            filter = json.load(open("mod.json"))["filter"]
            filter_list = []
            for c in self.checkboxes:
                if c.isChecked():
                    filter_list.append(filter[c.text()])
            filter = ":".join(filter_list)
            #thread
            self.thread = QThread()
            self.worker = Worker(self.ui.locationInput.text(), filter)
            self.worker.moveToThread(self.thread)
            self.thread.started.connect(self.worker.run)
            self.worker.finished.connect(self.thread.quit)
            self.worker.finished.connect(self.worker.deleteLater)
            self.thread.finished.connect(self.thread.deleteLater)
            self.worker.progress.connect(self.ui.progressBar.setValue)
            self.thread.start()
            self.ui.startButton.setEnabled(False)
            self.thread.finished.connect(self.handleFinished)

    def handleFinished(self):
        self.ui.startButton.setEnabled(True)
        self.ui.progressBar.setValue(0)
        msgBox = QMessageBox()
        msgBox.setIcon(QMessageBox.Information)
        msgBox.setText("수집이 완료되었습니다.")
        msgBox.setDefaultButton(QMessageBox.Ok)
        ret = msgBox.exec_()
        if ret == QMessageBox.Ok:
            path = os.getcwd() + "/result"
            webbrowser.open('file:///' + path)
Ejemplo n.º 6
0
class RunThread:
    """
    Wraps the thread and worker setting up the log signals to pass
    the log messages to the main window thread

    :param on_start: Method to call before the transformation begins
    :param on_finish: Method to call after the transformation ends
    """
    def __init__(self, on_start, on_finish):
        self.on_start = on_start
        self.on_finish = on_finish

        self.worker = None
        """The controller runner"""

        self.thread = None
        """The thread the worker will be ran on"""

    def start(self, config, generic_log_handler, validation_log_handler):
        """
        Starts the thread connecting the log panels to the relevant signals

        :param config: Config for the transformation to run
        :param generic_log_handler: The handler to consume all log records
        :param validation_log_handler: The handler to consume validation log
            records
        """
        self.on_start()

        self.thread = QThread()
        self.worker = ControllerRunner(config)
        self.worker.moveToThread(self.thread)

        self.worker.finished.connect(self.on_finish)
        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.worker.deleteLater)

        self.thread.started.connect(self.worker.run)
        self.thread.finished.connect(self.thread.deleteLater)

        # connect the log handlers back to the main window
        self.worker.generic_log_record.connect(generic_log_handler.emit)
        self.worker.validation_log_record.connect(validation_log_handler.emit)

        self.thread.start()
Ejemplo n.º 7
0
class App(QWidget):
    def __init__(self):
        super(App, self).__init__()
        self.combo = None
        self.MainProcess = MainProcess()
        self.MainProcess.get_image_functions()
        self.fps_label = QLabel('FPS: XXX')
        self.initUI()
        self._thread = QThread(self)
        self.video_worker = VideoThread(self.MainProcess.list_of_filters)
        #self.combo.currentIndexChanged.connect(self.video_worker.new_filter_slot)
        self.combo.currentIndexChanged.connect(self._new_ind)

        self.video_worker.change_pixmap_signal.connect(self.update_image)
        self.video_worker.update_fps_signal.connect(self._new_fps)
        self.video_worker.moveToThread(self._thread)
        self.combo.setCurrentIndex(self.MainProcess.blank_index)
        # connect the buttons
        self.start_button.clicked.connect(self.video_worker.start)
        self.quit_button.clicked.connect(
            self.close)  # force this to run on current thread

        self._thread.start()

    @Slot()
    def _new_ind(self, ind):
        self.video_worker.new_filter_slot(ind)

    @Slot(int)
    def _new_fps(self, fps):
        self.fps_label.setText(f'FPS: {fps:.1f}')

    def initUI(self):

        self.cam = CamImage()
        self.start_button = QPushButton("Start")
        self.quit_button = QPushButton("Quit")
        controls = ControlWidget()
        self.combo = QComboBox(self)
        for it in self.MainProcess.list_of_filters:
            self.combo.addItem(it[0])

        hbox = QHBoxLayout()
        hbox.addWidget(controls)
        hbox.addStretch(1)
        hbuttons = QHBoxLayout()
        hbuttons.addWidget(self.combo)
        hbuttons.addWidget(self.start_button)
        hbuttons.addWidget(self.quit_button)
        vbutton = QVBoxLayout()
        vbutton.addLayout(hbuttons)
        vbutton.addWidget(self.fps_label)
        hbox.addLayout(vbutton)
        vbox = QVBoxLayout()
        vbox.addWidget(self.cam)
        vbox.addStretch(1)
        vbox.addLayout(hbox)

        self.setLayout(vbox)

        self.setGeometry(300, 300, 300, 150)
        self.setWindowTitle('Buttons')

        self.show()

    def closeEvent(self, event):
        logger.debug('Got Close Event')
        self.video_worker.stop()
        self._thread.wait()
        event.accept()

    @Slot(np.ndarray)
    def update_image(self, cv_img):
        """Updates the image_label with a new opencv image"""
        qt_img = self.convert_cv_qt(cv_img)
        self.cam.setPixmap(qt_img)

    def convert_cv_qt(self, cv_img):
        """Convert from an opencv image to QPixmap"""
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
        h, w, ch = rgb_image.shape
        bytes_per_line = ch * w
        convert_to_Qt_format = QImage(rgb_image.data, w, h, bytes_per_line,
                                      QImage.Format_RGB888)
        p = convert_to_Qt_format.scaled(self.cam.width(), self.cam.height(),
                                        Qt.KeepAspectRatio)
        return QPixmap.fromImage(p)
Ejemplo n.º 8
0
class MainWindow(QObject):
    def __init__(self):
        QObject.__init__(self)

    # -----------------------------------------------
    # GENERAL OBJECTS: signals, mutexs, etc
    # -----------------------------------------------

    # Static Info
    # Nothing to see

    # Signals To Send Data
    ## Signals for install button
    signalButtonInstall = Signal(bool)  # when pressed
    signalInstalledCoreBackend = Signal(bool)  # when finished task
    ## Signals for run button
    signalButtonRunCore = Signal(bool)  # when pressed
    signalCoreBackendRunning = Signal(bool)  # when finished task

    # -----------------------------------------------
    # HANDLERS OF FRONTEND FUNCTIONS
    # -----------------------------------------------
    # Function To Check and Start Install Core Backend
    @Slot()
    def installCoreBackend(self):
        """
        Code that execute when btnInstall is clicked
        """
        nameOfFunction = sys._getframe().f_code.co_name
        nameOfFile = __file__
        print(f'[{nameOfFile}][{nameOfFunction}] Started')
        # emit signal of task started
        self.signalButtonInstall.emit(True)
        # Clone the repository
        if not os.path.isdir(PATH_CORE_BACKEND):
            os.mkdir(PATH_CORE_BACKEND)
            self.runFunctionInstallCoreBackend()
        else:
            print(
                f'[{nameOfFile}][{nameOfFunction}] Path {PATH_CORE_BACKEND} already exist'
            )
            self.runFunctionInstallCoreBackendFinished()  # Force finish

        print(f'[{nameOfFile}][{nameOfFunction}] Finished')

    @Slot()
    def runCoreBackend(self):
        """
        Function that runs Core Python
        """
        nameOfFunction = sys._getframe().f_code.co_name
        nameOfFile = __file__
        print(f'[{nameOfFile}][{nameOfFunction}] Started')

        self.signalButtonRunCore.emit(True)

        self.runFunctionCoreBackend()

        self.signalCoreBackendRunning.emit(True)

        print(f'[{nameOfFile}][{nameOfFunction}] Finished')

    # -----------------------------------------------
    # THREADS SETUP
    # -----------------------------------------------
    def runFunctionInstallCoreBackend(self):
        """
        Script that run a class as Thread to don't freeze main app
        """
        nameOfFunction = sys._getframe().f_code.co_name
        nameOfFile = __file__
        print(f'[{nameOfFile}][{nameOfFunction}] Started')
        # Step 1: Create worker
        # Step 2: Create a QThread object
        self.thread = QThread()
        # Step 3: Create a worker object
        self.worker = WorkerBashCommand()
        # Step 4: Move worker to the thread
        self.worker.moveToThread(self.thread)
        # Step 5: Connect signals and slots
        self.thread.started.connect(
            self.worker.runBashCommand(COMMAND_GIT_CLONE_CORE_BACKEND))
        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.worker.deleteLater)
        self.thread.finished.connect(
            self.runFunctionInstallCoreBackendFinished)

        # Step 6: Start the thread
        self.thread.start()

        print(f'[{nameOfFile}][{nameOfFunction}] Finished')

    def runFunctionCoreBackend(self):
        """
        Script that run a class as Thread to don't freeze main app
        """
        nameOfFunction = sys._getframe().f_code.co_name
        nameOfFile = __file__
        print(f'[{nameOfFile}][{nameOfFunction}] Started')
        # Step 1: Create worker
        # Step 2: Create a QThread object
        self.thread2 = QThread()
        # Step 3: Create a worker object
        self.worker2 = WorkerBashCommand()
        # Step 4: Move worker to the thread
        self.worker2.moveToThread(self.thread2)
        # Step 5: Connect signals and slots
        self.thread2.started.connect(
            self.worker2.runBashCommand(COMMAND_RUN_CORE_BACKEND))
        self.worker2.finished.connect(self.thread2.quit)
        self.worker2.finished.connect(self.worker2.deleteLater)
        self.thread2.finished.connect(self.runFunctionCoreBackendFinished)

        # Step 6: Start the thread
        self.thread2.start()

        print(f'[{nameOfFile}][{nameOfFunction}] Finished')

    # -----------------------------------------------
    # END OF THREADS
    # -----------------------------------------------
    @Slot()
    def runFunctionInstallCoreBackendFinished(self):
        """
        Function that executes when a Thread finish
        """
        # emit signal of task finished
        self.signalInstalledCoreBackend.emit(True)  # Finish cloning core repo

    @Slot()
    def runFunctionCoreBackendFinished(self):
        """
        Function that executes when a Thread finish
        """
        # emit signal of task finished
        self.signalCoreBackendRunning.emit(True)  # Finish cloning core repo
Ejemplo n.º 9
0
                (_, type_names, path, filename) = event

                if filename[-3:] == 'qml' and 'IN_MODIFY' in type_names:
                    reload = True
                    break

            if reload:
                self.requestReload.emit()


if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    workerThread = QThread()
    workerThread.start()
    worker = Worker()
    worker.moveToThread(workerThread)

    master = Master()
    master.command.connect(worker.run)
    worker.requestReload.connect(master.reload)
    master.command.emit()

    # Stop application gracefully:
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    status = app.exec_()
    worker.stop()
    workerThread.quit()
    workerThread.wait()
    sys.exit(status)
Ejemplo n.º 10
0
class EvelynDesktop(QStackedWidget):
    INTERVAL_SECS = 30
    ALERT_SECS = 5

    signal_get_ping = Signal()
    signal_post_history = Signal(int, QDateTime)

    def __init__(
            self,
            config_file: str
    ) -> None:
        super().__init__()
        # load config
        try:
            self.config = Config(config_file)
        except Exception as e:
            QMessageBox.critical(self, 'Config error', str(e))
            QTimer.singleShot(0, self.close)
            return
        # load settings
        self.settings = Settings()
        # state
        self.state_key: Optional[int] = None
        # label widget
        self.label_ping = ClickableLabel('Loading ...', self.post_history)
        self.label_ping.setTextFormat(Qt.RichText)
        self.label_ping.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        layout_ping = QGridLayout()
        layout_ping.setContentsMargins(0, 0, 0, 0)
        layout_ping.addWidget(self.label_ping)
        self.widget_ping = QWidget()
        self.widget_ping.setLayout(layout_ping)
        self.addWidget(self.widget_ping)
        # alert widget
        self.label_alert = QLabel()
        self.label_alert.setWordWrap(True)
        self.label_alert.setAlignment(Qt.AlignCenter)
        self.label_alert.setStyleSheet(f'background: #dddddd;')
        self.addWidget(self.label_alert)
        # context menu
        self.action_report_done = QAction('Report done ...')
        self.action_report_done.triggered.connect(self.report_done)
        self.action_exit = QAction('Exit')
        self.action_exit.triggered.connect(self.close)
        self.action_frameless = QAction('Frameless window')
        self.action_frameless.setCheckable(True)
        self.action_frameless.triggered.connect(self.set_frameless_window)
        self.action_homepage = QAction('Open homepage')
        self.action_homepage.triggered.connect(self.open_homepage)
        self.context_menu = QMenu()
        self.context_menu.addAction(self.action_report_done)
        self.context_menu.addAction(self.action_exit)
        self.context_menu.addAction(self.action_frameless)
        self.context_menu.addAction(self.action_homepage)
        # threads
        self.thread_communication = QThread()
        self.thread_communication.start()
        # workers
        self.worker_communication = CommunicationWorker(
            netloc=self.config.netloc,
            base_path=self.config.base_path,
            api_key=self.config.api_key,
            guild=self.config.guild,
            member=self.config.member)
        self.worker_communication.moveToThread(self.thread_communication)
        # signals
        self.worker_communication.signal_get_ping_done.connect(self.get_ping_done)
        self.worker_communication.signal_post_history_done.connect(self.post_history_done)
        self.signal_get_ping.connect(self.worker_communication.get_ping)
        self.signal_post_history.connect(self.worker_communication.post_history)
        # get ping timer
        QTimer.singleShot(0, self.get_ping)
        self.timer_ping = QTimer()
        self.timer_ping.timeout.connect(self.get_ping)
        self.timer_ping.setTimerType(Qt.VeryCoarseTimer)
        self.timer_ping.start(self.INTERVAL_SECS * 1000)
        # switch label timer
        self.timer_label = QTimer()
        self.timer_label.timeout.connect(lambda: self.setCurrentWidget(self.widget_ping))
        self.timer_label.setSingleShot(True)
        self.timer_label.setTimerType(Qt.CoarseTimer)
        # window attributes
        size = self.settings.get('window', 'size', type_=QSize)
        if size is not None:
            self.resize(size)
        pos = self.settings.get('window', 'pos', type_=QPoint)
        if pos is not None:
            self.move(pos)
        frameless = self.settings.get('window', 'frameless', type_=bool)
        if frameless is not None and frameless:
            QTimer.singleShot(100, self.action_frameless.trigger)
        self.setWindowFlag(Qt.WindowStaysOnTopHint, self.config.window_stays_on_top)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setWindowTitle('Evelyn Reminder')

    def closeEvent(
            self,
            event: QCloseEvent
    ) -> None:
        # save settings
        with suppress_and_log_exception():
            self.settings.set('window', 'size', self.size())
            self.settings.set('window', 'pos', self.pos())
            self.settings.set('window', 'frameless', bool(self.windowFlags() & Qt.FramelessWindowHint))
        # stop communication thread
        with suppress_and_log_exception():
            self.thread_communication.quit()
            self.thread_communication.wait()
        # done
        super().closeEvent(event)

    def contextMenuEvent(
            self,
            event: QContextMenuEvent
    ) -> None:
        self.context_menu.exec_(event.globalPos())

    @Slot()
    def get_ping(self) -> None:
        logging.info('Get ping ...')
        self.signal_get_ping.emit()

    @Slot(int, str, str)
    def get_ping_done(
            self,
            key: int,
            text: str,
            color: str
    ) -> None:
        logging.info('Get ping done')
        if key == -1:
            self.state_key = None
            self.label_ping.setWordWrap(True)
        else:
            self.state_key = key
            self.label_ping.setWordWrap(False)
        self.label_ping.setText(text)
        self.widget_ping.setStyleSheet(f'background : {color}; ')

    @Slot()
    def post_history(
            self,
            date_time: QDateTime = QDateTime()
    ) -> None:
        # this method is called as Slot by ClickableLabel.mouseReleaseEvent() without arguments
        # this method is called directly by EvelynDesktop.report_done() with a date_time
        if self.state_key is None:
            return
        logging.info('Post history ...')
        self.label_alert.setText('Sending ...')
        self.label_alert.setStyleSheet(f'background: #dddddd;')
        self.setCurrentWidget(self.label_alert)
        self.signal_post_history.emit(self.state_key, date_time)

    @Slot(str, bool)
    def post_history_done(
            self,
            text: str,
            error: bool
    ) -> None:
        logging.info('Post history done')
        self.label_alert.setText(text)
        if error:
            self.label_alert.setStyleSheet(f'background: #dd4b4b;')
        self.timer_label.start(self.ALERT_SECS * 1000)
        # trigger instant ping update to avoid outdated info
        self.timer_ping.stop()
        self.timer_ping.start(self.INTERVAL_SECS * 1000)
        self.get_ping()

    @Slot()
    def report_done(self) -> None:
        self.timer_ping.stop()  # stop ping update while dialog is open
        report_done_dialog = ReportDoneDialog(self)
        response = report_done_dialog.exec()
        if response != QDialog.Accepted:
            self.timer_ping.start(self.INTERVAL_SECS * 1000)
            self.get_ping()
            return
        date_time = report_done_dialog.get_date_time()
        self.post_history(date_time)

    @Slot(bool)
    def set_frameless_window(
            self,
            value: bool
    ) -> None:
        pos = self.pos()
        self.setWindowFlag(Qt.FramelessWindowHint, value)
        # workaround: window goes invisible otherwise
        self.setVisible(True)
        # workaround: window would move up otherwise
        if value:
            QTimer.singleShot(100, lambda: self.move(pos))

    @Slot()
    def open_homepage(self) -> None:
        webbrowser.open('https://github.com/stefs/evelyn-reminder')
Ejemplo n.º 11
0
class MediaPlayer():
    def __init__(self, songQueue, marqueeFunc, parent=None):
        self.parent = parent
        self.player = vlc.MediaPlayer()
        self.songQueue = songQueue
        self.currentSong = None
        self.marqueeFunc = marqueeFunc
        self.thread = QThread()
        self.worker = Worker(self)
        self.worker.moveToThread(self.thread)
        self.worker.check.connect(self.songEndCheck)
        self.thread.started.connect(self.worker.run)
        self.thread.start()

    def restartSong(self):
        self.player.set_media(
            vlc.Media(self.parent.songPath + self.currentSong["song_path"]))
        self.player.play()
        self.updateMarquee()

    def pauseSong(self):
        self.player.pause()

    def skipSong(self):
        if len(self.songQueue.getQueue()) == 0:
            return
        self.currentSong = self.songQueue.popSong()
        if self.parent.content.__class__.__name__ == "WindowQueue":
            self.parent.content.queueList.updateQueue()
        self.restartSong()

    def swapTrack(self):
        self.player.audio_set_track(
            (self.player.audio_get_track() %
             (self.player.audio_get_track_count() - 1)) + 1)

    def updateMarquee(self):
        if len(self.songQueue.getQueue()) > 0:
            text = []
            text.append("正在播放/Now Playing:{} - {}".format(
                self.currentSong["artist_name"],
                self.currentSong["song_title"]))
            text.append("下一首歌/Next Song: {} - {}".format(
                self.songQueue.getQueue()[0]["artist_name"],
                self.songQueue.getQueue()[0]["song_title"]))
            self.marqueeFunc(text)
        else:
            self.marqueeFunc("正在播放/Now Playing:{} - {}".format(
                self.currentSong["artist_name"],
                self.currentSong["song_title"]))

    def start(self):
        self.currentSong = random.choice(DB.getSongTitles(""))
        self.restartSong()

    def songEndCheck(self):
        if self.player.get_state() == 6:
            if len(self.songQueue.getQueue()) == 0:
                self.start()
            else:
                self.skipSong()
Ejemplo n.º 12
0
class BridgeDock(QDockWidget):
    def __init__(self, parent, api: PluginApi) -> None:
        super().__init__('', parent)
        self.api = api
        self.ui = Ui_BridgeDock()
        self.ui.setupUi(self)
        self.server_thread = None

        self.observer = None
        self.modified_timer = None
        self.slot_server_running(False)

        self.ui.pushButtonStartServer.clicked.connect(self.slot_start_server)
        self.ui.pushButtonStopServer.clicked.connect(self.slot_stop_server)
        self.ui.toolButtonLoadFolder.clicked.connect(
            self.slot_edit_load_folder)
        self.ui.toolButtonSaveFolder.clicked.connect(
            self.slot_edit_save_folder)

        self.ui.labelConnectionStatus.setText('Server not yet running.')

        # Initially load from repo folder
        self.ui.lineEditLoadFolder.setText(settings.get_repo_location())

        self.visibilityChanged.connect(self.slot_visibility_changed)

    def slot_visibility_changed(self, visible: bool) -> None:
        if not visible and self.server_thread is not None:
            self.slot_stop_server()

    def slot_server_running(self, running: bool) -> None:
        if running:
            self.ui.pushButtonStartServer.setVisible(False)
            self.ui.pushButtonStopServer.setVisible(True)
        else:
            self.ui.pushButtonStartServer.setVisible(True)
            self.ui.pushButtonStopServer.setVisible(False)

    def slot_start_server(self) -> None:

        if self.ui.checkBoxCopySaves.isChecked(
        ) and self.ui.lineEditSaveFolder.text().strip() == '':
            self.api.show_error(
                'Entity Explorer Bridge',
                'You need to set the folder where to store the copies.')
            return

        self.server_thread = QThread()
        self.server_worker = ServerWorker()
        self.server_worker.signal_connected.connect(self.slot_connected)
        self.server_worker.signal_disconnected.connect(self.slot_disconnected)
        self.server_worker.signal_error.connect(self.slot_error)
        self.server_worker.signal_started.connect(self.slot_server_started)
        self.server_worker.signal_shutdown.connect(self.slot_server_stopped)
        self.server_worker.moveToThread(self.server_thread)
        self.server_thread.started.connect(self.server_worker.process)
        self.server_thread.start()
        self.slot_server_running(True)
        self.set_folders_active(False)

    def set_folders_active(self, active: bool) -> None:
        self.ui.lineEditLoadFolder.setEnabled(active)
        self.ui.toolButtonLoadFolder.setEnabled(active)
        self.ui.checkBoxCopySaves.setEnabled(active)
        self.ui.lineEditSaveFolder.setEnabled(active)
        self.ui.toolButtonSaveFolder.setEnabled(active)

    def slot_stop_server(self) -> None:
        # Shutdown needs to be triggered by the server thread, so send a request
        requests.get('http://localhost:10243/shutdown')
        self.set_folders_active(True)

    def slot_connected(self) -> None:
        self.ui.labelConnectionStatus.setText(
            'Connected to Entity Explorer instance.')

    def slot_disconnected(self) -> None:
        self.ui.labelConnectionStatus.setText(
            'Disconnected from Entity Explorer instance.')

    def slot_server_started(self) -> None:
        self.slot_server_running(True)
        self.ui.labelConnectionStatus.setText(
            'Server running. Please connect Entity Explorer instance.')
        self.start_watchdog()

    def slot_server_stopped(self) -> None:
        self.slot_server_running(False)
        self.server_thread.terminate()
        self.ui.labelConnectionStatus.setText('Server stopped.')
        self.stop_watchdog()

    def slot_error(self, error: str) -> None:
        self.slot_server_running(False)
        self.server_thread.terminate()
        self.api.show_error('Entity Explorer Bridge', error)

    def slot_edit_load_folder(self):
        dir = QFileDialog.getExistingDirectory(
            self, 'Folder in which the save states are stored by mGBA',
            self.ui.lineEditLoadFolder.text())
        print(dir)
        if dir is not None:
            self.ui.lineEditLoadFolder.setText(dir)

    def slot_edit_save_folder(self):
        dir = QFileDialog.getExistingDirectory(
            self, 'Folder in which all save states should be copied',
            self.ui.lineEditSaveFolder.text())
        if dir is not None:
            self.ui.lineEditSaveFolder.setText(dir)

    def start_watchdog(self):
        if self.observer is not None:
            print('Already observing')
            return
        patterns = [
            '*.ss0', '*.ss1', '*.ss2', '*.ss3', '*.ss4', '*.ss5', '*.ss6',
            '*.ss7', '*.ss8', '*.ss9', '*.State'
        ]
        ignore_patterns = None
        ignore_directories = False
        case_sensitive = True
        self.event_handler = PatternMatchingEventHandler(
            patterns, ignore_patterns, ignore_directories, case_sensitive)
        self.event_handler.on_modified = self.on_file_modified

        path = self.ui.lineEditLoadFolder.text()
        self.observer = Observer()
        self.observer.schedule(self.event_handler, path, recursive=False)
        self.observer.start()

    def stop_watchdog(self):
        if self.observer is not None:
            self.observer.stop()
            self.observer.join()
            self.observer = None

    # https://stackoverflow.com/a/66907107
    def debounce(wait_time):
        """
        Decorator that will debounce a function so that it is called after wait_time seconds
        If it is called multiple times, will wait for the last call to be debounced and run only this one.
        """
        def decorator(function):
            def debounced(*args, **kwargs):
                def call_function():
                    debounced._timer = None
                    return function(*args, **kwargs)

                # if we already have a call to the function currently waiting to be executed, reset the timer
                if debounced._timer is not None:
                    debounced._timer.cancel()

                # after wait_time, call the function provided to the decorator with its arguments
                debounced._timer = threading.Timer(wait_time, call_function)
                debounced._timer.start()

            debounced._timer = None
            return debounced

        return decorator

    @debounce(0.1)
    def on_file_modified(self, event):
        with open(event.src_path, 'rb') as file:
            bytes = file.read()
            self.server_worker.slot_send_save_state(event.src_path, bytes)

            if self.ui.checkBoxCopySaves.isChecked(
            ) and self.ui.lineEditSaveFolder.text():
                name = os.path.basename(event.src_path)
                name = datetime.now().strftime('%Y-%m-%d_%H_%M_%S_%f_') + name
                with open(
                        os.path.join(self.ui.lineEditSaveFolder.text(), name),
                        'wb') as output:
                    output.write(bytes)
Ejemplo n.º 13
0
# Step 2: Create a QThread object
thread = QThread()
# Step 3: Create a worker object
worker = Worker()
# Step 4: Move worker to the thread
worker.moveToThread(thread)

# Step 5: Connect signals and slots
thread.started.connect(worker.run)
worker.finished.connect(thread.quit)
worker.finished.connect(worker.deleteLater)
thread.finished.connect(thread.deleteLater)

# Step 6: Start the thread
thread.start()

app = QtWidgets.QApplication([])

app.setQuitOnLastWindowClosed(False)

# Adding an icon
icon = QtGui.QIcon(':/images/icon.png')

# Adding item on the menu bar
tray = QtWidgets.QSystemTrayIcon()
tray.setIcon(icon)
tray.setVisible(True)


# https://gist.github.com/for-l00p/3e33305f948659313127632ad04b4311
Ejemplo n.º 14
0
class BridgeDock(QDockWidget):
    def __init__(self, parent, api: PluginApi) -> None:
        super().__init__('', parent)
        self.api = api
        self.ui = Ui_BridgeDock()
        self.ui.setupUi(self)
        self.server_thread = None

        self.symbols = None
        self.rom = None
        self.data_extractor_plugin = None

        self.slot_server_running(False)

        self.ui.pushButtonStartServer.clicked.connect(self.slot_start_server)
        self.ui.pushButtonStopServer.clicked.connect(self.slot_stop_server)
        self.ui.pushButtonUpload.clicked.connect(self.slot_upload_function)
        self.ui.pushButtonDownload.clicked.connect(self.slot_download_function)
        self.ui.pushButtonCopyJs.clicked.connect(self.slot_copy_js_code)
        self.ui.pushButtonGoTo.clicked.connect(self.slot_goto)
        self.ui.pushButtonDecompile.clicked.connect(self.slot_decompile)
        self.ui.pushButtonGlobalTypes.clicked.connect(self.slot_global_types)
        self.ui.pushButtonUploadAndDecompile.clicked.connect(
            self.slot_upload_and_decompile)

        self.enable_function_group(False)
        self.ui.labelConnectionStatus.setText('Server not yet running.')
        self.visibilityChanged.connect(self.slot_close)

    def slot_close(self, visibility: bool) -> None:
        # TODO temporarily disable until a good way to detect dock closing is found
        pass
        #if not visibility and self.server_thread is not None:
        #    self.slot_stop_server()

    def slot_server_running(self, running: bool) -> None:
        if running:
            self.ui.pushButtonStartServer.setVisible(False)
            self.ui.pushButtonStopServer.setVisible(True)
        else:
            self.ui.pushButtonStartServer.setVisible(True)
            self.ui.pushButtonStopServer.setVisible(False)

    def slot_start_server(self) -> None:
        self.server_thread = QThread()
        self.server_worker = ServerWorker()
        self.server_worker.signal_connected.connect(self.slot_connected)
        self.server_worker.signal_disconnected.connect(self.slot_disconnected)
        self.server_worker.signal_error.connect(self.slot_error)
        self.server_worker.signal_c_code.connect(self.slot_received_c_code)
        self.server_worker.signal_started.connect(self.slot_server_started)
        self.server_worker.signal_shutdown.connect(self.slot_server_stopped)
        self.server_worker.signal_extract_data.connect(self.slot_extract_data)
        self.server_worker.signal_fetch_decompilation.connect(
            self.slot_fetch_decompilation)
        self.server_worker.signal_upload_function.connect(
            self.slot_download_requested)
        self.server_worker.moveToThread(self.server_thread)
        self.server_thread.started.connect(self.server_worker.process)
        self.server_thread.start()
        self.slot_server_running(True)

    def enable_function_group(self, enabled: bool) -> None:
        self.ui.lineEditFunctionName.setEnabled(enabled)
        self.ui.pushButtonUpload.setEnabled(enabled)
        self.ui.pushButtonDownload.setEnabled(enabled)
        self.ui.pushButtonGoTo.setEnabled(enabled)
        self.ui.pushButtonDecompile.setEnabled(enabled)
        self.ui.pushButtonUploadAndDecompile.setEnabled(enabled)

    def slot_stop_server(self) -> None:
        # Shutdown needs to be triggered by the server thread, so send a request
        requests.get('http://localhost:10241/shutdown')

    def slot_upload_function(self) -> None:
        self.upload_function(True)

    # Returns true if the user accepted the uploading
    def upload_function(self, include_function: bool) -> bool:
        # TODO try catch all of the slots?
        (err, asm, src,
         signature) = get_code(self.ui.lineEditFunctionName.text().strip(),
                               include_function)
        if err:
            self.api.show_error('CExplore Bridge', asm)
            return

        if NO_CONFIRMS:
            # For pros also directly go to the function in Ghidra and apply the signature
            self.slot_goto()
            #self.apply_function_signature(self.ui.lineEditFunctionName.text().strip(), signature)

        if NO_CONFIRMS or self.api.show_question(
                'CExplore Bridge',
                f'Replace code in CExplore with {self.ui.lineEditFunctionName.text().strip()}?'
        ):
            self.server_worker.slot_send_asm_code(extract_USA_asm(asm))
            self.server_worker.slot_send_c_code(src)
            if not NO_CONFIRMS:
                self.api.show_message(
                    'CExplore Bridge',
                    f'Uploaded code of {self.ui.lineEditFunctionName.text().strip()}.'
                )
            return True
        return False

    def slot_download_requested(self, name: str) -> None:
        self.ui.lineEditFunctionName.setText(name)
        self.slot_download_function()

    def slot_download_function(self) -> None:
        self.enable_function_group(False)
        self.server_worker.slot_request_c_code()

    def slot_received_c_code(self, code: str) -> None:

        self.enable_function_group(True)
        (includes, header, src) = split_code(code)
        dialog = ReceivedDialog(self)
        dialog.signal_matching.connect(self.slot_store_matching)
        dialog.signal_nonmatching.connect(self.slot_store_nonmatching)
        dialog.show_code(includes, header, src)

    def slot_store_matching(self, includes: str, header: str,
                            code: str) -> None:
        self.store(includes, header, code, True)

    def slot_store_nonmatching(self, includes: str, header: str,
                               code: str) -> None:
        self.store(includes, header, code, False)

    def store(self, includes: str, header: str, code: str,
              matching: bool) -> None:
        (err, msg) = store_code(self.ui.lineEditFunctionName.text().strip(),
                                includes, header, code, matching)
        if err:
            self.api.show_error('CExplore Bridge', msg)
            return
        if not NO_CONFIRMS:
            self.api.show_message(
                'CExplore Bridge',
                f'Sucessfully replaced code of {self.ui.lineEditFunctionName.text().strip()}.'
            )

    def slot_copy_js_code(self) -> None:
        QApplication.clipboard().setText(
            'javascript:var script = document.createElement("script");script.src = "http://localhost:10241/static/bridge.js";document.body.appendChild(script);'
        )
        self.api.show_message(
            'CExplore Bridge',
            'Copied JS code to clipboard.\nPaste it as the url to a bookmark.\nThen go open the CExplore instance and click on the bookmark to connect.'
        )

    def slot_connected(self) -> None:
        self.ui.labelConnectionStatus.setText(
            'Connected to CExplore instance.')
        self.enable_function_group(True)
        self.ui.pushButtonCopyJs.setVisible(False)

    def slot_disconnected(self) -> None:
        self.ui.labelConnectionStatus.setText(
            'Disconnected from CExplore instance.')
        self.enable_function_group(False)

    def slot_server_started(self) -> None:
        self.slot_server_running(True)
        self.ui.labelConnectionStatus.setText(
            'Server running. Please connect CExplore instance.')

    def slot_server_stopped(self) -> None:
        self.slot_server_running(False)
        self.server_thread.terminate()
        self.enable_function_group(False)
        self.ui.pushButtonCopyJs.setVisible(True)
        self.ui.labelConnectionStatus.setText('Server stopped.')

    def slot_error(self, error: str) -> None:
        self.slot_server_running(False)
        self.server_thread.terminate()
        self.enable_function_group(False)
        self.ui.pushButtonCopyJs.setVisible(True)
        self.api.show_error('CExplore Bridge', error)

    def slot_goto(self) -> None:
        try:
            r = requests.get('http://localhost:10242/goto/' +
                             self.ui.lineEditFunctionName.text().strip())
            if r.status_code != 200:
                self.api.show_error('CExplore Bridge', r.text)
                return
        except requests.exceptions.RequestException as e:
            self.api.show_error(
                'CExplore Bridge',
                'Could not reach Ghidra server. Did you start the script?')

    def slot_fetch_decompilation(self, name: str) -> None:
        self.ui.lineEditFunctionName.setText(name)
        self.slot_decompile()

    def slot_decompile(self) -> None:
        try:
            r = requests.get('http://localhost:10242/decompile/' +
                             self.ui.lineEditFunctionName.text().strip())
            if r.status_code != 200:
                self.api.show_error('CExplore Bridge', r.text)
                return
            result = r.text
            code = improve_decompilation(result)
            self.server_worker.slot_add_c_code(code)
        except requests.exceptions.RequestException as e:
            self.api.show_error(
                'CExplore Bridge',
                'Could not reach Ghidra server. Did you start the script?')
        except Exception as e:
            self.api.show_error('CExplore Bridge',
                                'An unknown error occured: ' + str(e))

    def slot_upload_and_decompile(self) -> None:
        # Upload, but don't include the function.
        if self.upload_function(False):
            # Now add the decompiled function.
            self.slot_decompile()

    def slot_global_types(self) -> None:
        globals = find_globals()
        success = True
        for definition in globals:
            if not self.apply_global_type(definition):
                success = False
                break

        # Also apply function signatures from file
        if success:
            signatures = read_signatures_from_file()
            for signature in signatures:
                if not self.apply_function_signature(signature.function,
                                                     signature.signature):
                    success = False
                    break

        if success:
            self.api.show_message('CExplore Bridge',
                                  'Applied all global types.')

    def apply_global_type(self, definition: TypeDefinition) -> bool:
        try:
            url = 'http://localhost:10242/globalType/' + definition.name + '/' + definition.dataType + '/' + definition.length
            print(url)
            r = requests.get(url)
            if r.status_code != 200:
                self.api.show_error('CExplore Bridge', r.text)
                return False
            return True
        except requests.exceptions.RequestException as e:
            self.api.show_error(
                'CExplore Bridge',
                'Could not reach Ghidra server. Did you start the script?')
            return False

    def apply_function_signature(self, name: str, signature: str) -> bool:
        try:
            url = 'http://localhost:10242/functionType/' + name + '/' + signature
            print(url)
            r = requests.get(url)
            if r.status_code != 200:
                self.api.show_error('CExplore Bridge', r.text)
                return False
            return True
        except requests.exceptions.RequestException as e:
            self.api.show_error(
                'CExplore Bridge',
                'Could not reach Ghidra server. Did you start the script?')
            return False

    def slot_extract_data(self, text: str) -> None:
        if self.symbols is None:
            # First need to load symbols
            self.symbols = get_symbol_database().get_symbols(RomVariant.CUSTOM)
            if self.symbols is None:
                self.server_worker.slot_extracted_data({
                    'status':
                    'error',
                    'text':
                    'No symbols for rom CUSTOM loaded'
                })
                return

        if self.data_extractor_plugin is None:
            self.data_extractor_plugin = get_plugin('data_extractor',
                                                    'DataExtractorPlugin')
            if self.data_extractor_plugin is None:
                self.server_worker.slot_extracted_data({
                    'status':
                    'error',
                    'text':
                    'Data Extractor plugin not loaded'
                })
                return

        if self.rom is None:
            self.rom = get_rom(RomVariant.CUSTOM)
            if self.rom is None:
                self.server_worker.slot_extracted_data({
                    'status':
                    'error',
                    'text':
                    'CUSTOM rom could not be loaded'
                })
                return

        try:
            result = self.data_extractor_plugin.instance.extract_data(
                text, self.symbols, self.rom)
            if result is not None:
                self.server_worker.slot_extracted_data({
                    'status': 'ok',
                    'text': result
                })
        except Exception as e:
            traceback.print_exc()
            self.server_worker.slot_extracted_data({
                'status': 'error',
                'text': str(e)
            })
Ejemplo n.º 15
0
class main_controller(Ui_MainWindow, QMainWindow):
    def __init__(self, DEBUG):
        super(main_controller, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.debugging = False

        self.debug_init(DEBUG)

        self.ui.actionAbout_Qt.triggered.connect(lambda: self.open_about_qt())
        self.ui.actionOpen.triggered.connect(lambda: self.open_file())
        self.ui.actionClose.triggered.connect(
            lambda: self.prepare_destroy_tracker())
        self.ui.playPauseButton.clicked.connect(lambda: self.play_pause())
        self.ui.actionQuit.triggered.connect(lambda: self.close())
        self.ui.actionProperty_Editor.toggled.connect(self.show_hide_pe)
        self.ui.actionAbout.triggered.connect(lambda: self.open_about())

        #Connect playhead signals and slots
        self.ui.playHeadSlider.sliderMoved.connect(
            lambda: self.update_playHead())
        self.ui.playHeadSlider.sliderReleased.connect(
            lambda: self.playHead_sliderUp())
        self.ui.playHeadSlider.sliderPressed.connect(
            lambda: self.playHead_sliderDown())

        self.playHeadChanged = False
        self.sliderIgnoreUpdates = False
        self.destroyOnLoad = False
        self.tempVideoFile = None
        self.tracker = None
        self.tracker_thread = None
        self.current_state = State.Nothing_Loaded
        self.pe = None

    def open_about_qt(self):
        QMessageBox.aboutQt(self, ui_strings.TITLE)

    def init_tracker(self, video_file):
        if self.current_state != State.Nothing_Loaded:
            self.prepare_destroy_tracker()
            self.destroyOnLoad = True
            self.tempVideoFile = video_file
        else:
            self.current_state = State.Initializing

            #Init tracker
            self.tracker = video_tracker(video_file, self.debugging)

            #Setup tracker thread
            self.tracker_thread = QThread(self)
            self.tracker.moveToThread(self.tracker_thread)

            #Setup Property Editor
            self.pe = pe_controller(self.ui.propertyEditor, self.tracker,
                                    self.debugging)

            #Connect signals and slots
            self.tracker.gen_start.connect(self.tracker.receive_signal)
            self.tracker.finished.connect(self.tracker_thread.quit)
            self.tracker.FrameSignal.connect(self.update_frames)
            self.tracker.stopped.connect(self.tracker_stopped)
            self.tracker.gen_history.connect(self.tracker.render_history)

            self.tracker_thread.start()
            self.current_state = State.Idle

            self.init_controls(self.tracker.video_data.num_frames)

    def open_file(self):
        video_file = str(
            QFileDialog.getOpenFileName(self, ui_strings.OPEN_VIDEO_FILE)[0])
        if video_file != "":
            self.init_tracker(video_file)

    def convert_frame(self, frame):
        qImage = None

        if len(frame.shape) == 3:
            height, width, byteValue = frame.shape
            byteValue = byteValue * width

            qImage = QImage(frame, width, height, byteValue,
                            QImage.Format_BGR888)
        else:
            #This is a mask
            height, width = frame.shape

            qImage = QImage(frame, width, height, QImage.Format_Grayscale8)

        return QPixmap(qImage)

    def update_frames(self, frame_num):
        if self.current_state == State.Preparing_History:
            self.pb.set_progress(self.pb.bar.value() + 1)
        else:
            self.debug_logger.print_debug_info(12, frame_num)
            self.ui.rawFrame.changePixmap(
                self.convert_frame(self.tracker.frame_cnt))
            self.ui.maskFrame.changePixmap(
                self.convert_frame(self.tracker.mask))

            if self.tracker.final is not None:
                self.ui.outputFrame.changePixmap(
                    self.convert_frame(self.tracker.final))

            if not self.sliderIgnoreUpdates:
                self.ui.playHeadSlider.setValue(
                    self.tracker.video_data.current_frame)

    def prepare_destroy_tracker(self):
        if self.current_state == State.Running:
            self.debug_logger.print_debug_info(13)
            self.current_state = State.Destroying
            self.tracker.running = False
        else:
            self.current_state = State.Destroying
            self.destroy_tracker()

    def destroy_tracker(self):
        self.tracker.destroy()
        self.tracker = None
        self.tracker_thread = None

        self.debug_logger.print_debug_info(15)

        #Clear image labels
        self.ui.rawFrame.clearPixmap()
        self.ui.maskFrame.clearPixmap()
        self.ui.outputFrame.clearPixmap()

        self.current_state = State.Nothing_Loaded
        self.reset_controls()

    def tracker_stopped(self):
        if self.current_state == State.Destroying:
            self.destroy_tracker()
            self.init_tracker(self.tempVideoFile)
            self.destroyOnLoad = False
            self.tempVideoFile = None
        elif self.current_state == State.Running:
            self.current_state = State.Stopped
            self.update_play_button()
        elif self.current_state == State.Preparing_History:
            self.pb.hide()
            self.pb.destroy()
            self.pb = None

            self.current_state == State.Stopped
            self.ui.playHeadSlider.setEnabled(True)
            self.play_pause()
        elif self.current_state == State.Slider_Moved_While_Running:
            self.sliderIgnoreUpdates = False
            self.update_history()

#region DEBUG_FUNCTIONS

    def debug_init(self, debug):
        self.debug_logger = logger(debug)

        if not debug:
            self.ui.menuDEBUG.setEnabled(False)
            self.ui.menuDEBUG.menuAction().setVisible(False)
        else:
            self.ui.actionRender_next_frame.triggered.connect(
                lambda: self.debug_render_next())
            self.ui.actionPrint_current_state.triggered.connect(
                lambda: self.debug_print_cur_state())
            self.ui.actionPrint_tracker_video_data.triggered.connect(
                lambda: self.debug_print_tracker_video_data())
            self.ui.actionPrint_tracker_data.triggered.connect(
                lambda: self.debug_print_tracker_data())
            self.ui.actionInit.triggered.connect(lambda: self.create_pb())
            self.ui.actionDestroy.triggered.connect(lambda: self.destroy_pb())
            self.ui.actionIncrement_5.triggered.connect(
                lambda: self.debug_pb_increment_5())
            self.ui.actionIncrement_50.triggered.connect(
                lambda: self.debug_pb_increment_50())
            self.ui.actionSet_Label_Message_1.triggered.connect(
                lambda: self.debug_pb_set_message_1())
            self.ui.actionSet_Label_Message_2.triggered.connect(
                lambda: self.debug_pb_set_message_2())
            self.ui.actionRemove_Label_Message.triggered.connect(
                lambda: self.pb.remove_message())
            self.ui.actionPrint_real_current_frame.triggered.connect(
                lambda: self.debug_print_real_current_frame())
            self.ui.actionShow_Object_Data_from_current_frame.triggered.connect(
                lambda: self.debug_show_obj_data_current_frame())
            self.debug_logger.print_debug_info(0)
            self.debugging = True

    def debug_render_next(self):
        if not self.current_state.Running:
            self.debug_logger.print_debug_info(18)
            self.tracker.gen_start.emit(False)

    def debug_print_cur_state(self):
        self.debug_logger.print_debug_info(1, self.current_state)

    def debug_print_tracker_video_data(self):
        self.debug_logger.print_debug_info(2, self.tracker.video_data)

    def debug_print_tracker_data(self):
        self.debug_logger.print_debug_info(3, self.tracker.data)

    def destroy_pb(self):
        self.pb.hide()
        self.pb.destroy()
        self.pb = None

    def create_pb(self):
        self.pb = ProgressBarLabel(self)
        self.ui.verticalLayout_2.addWidget(self.pb)

    def debug_pb_increment_5(self):
        self.pb.set_progress(self.pb.bar.value() + 5)

    def debug_pb_increment_50(self):
        self.pb.set_progress(self.pb.bar.value() + 50)

    def debug_pb_set_message_1(self):
        self.pb.set_message("Hello World!")

    def debug_pb_set_message_2(self):
        self.pb.set_message("This is the second debug message")

    def debug_print_real_current_frame(self):
        self.debug_logger.print_debug_info(
            23, self.tracker.get_real_current_frame())

    def debug_show_obj_data_current_frame(self):
        objs_in_frame = self.tracker.video_data.get_object_points(
            self.tracker.video_data.current_frame - 1)
        total_objs = len(objs_in_frame)

        msg = QMessageBox()
        msg.setIcon(QMessageBox.Information)
        msg.setText("Total objects in frame: " + str(total_objs) + "\n\n" +
                    "First three objects:\n" + "(" + str(objs_in_frame[0][0]) +
                    ", " + str(objs_in_frame[0][1]) + ") Width: " +
                    str(objs_in_frame[0][2]) + ", Height: " +
                    str(objs_in_frame[0][3]) + "\n" + "(" +
                    str(objs_in_frame[1][0]) + ", " +
                    str(objs_in_frame[1][1]) + ") Width: " +
                    str(objs_in_frame[1][2]) + ", Height: " +
                    str(objs_in_frame[1][3]) + "\n" + "(" +
                    str(objs_in_frame[2][0]) + ", " +
                    str(objs_in_frame[2][1]) + ") Width: " +
                    str(objs_in_frame[2][2]) + ", Height: " +
                    str(objs_in_frame[2][3]))
        msg.setWindowTitle("Objects in current frame")
        msg.setStandardButtons(QMessageBox.Ok)
        msg.exec_()

#endregion

    def reset_controls(self):
        self.ui.playHeadSlider.setValue(0)
        self.ui.playHeadSlider.setEnabled(False)
        self.update_play_button()
        self.ui.playPauseButton.setEnabled(False)

        self.pe.destroy()
        self.pe = None

        #Check if we have a progress bar
        try:
            self.pb.hide()
            self.pb.destroy()
            self.pb = None
        except:
            #Do nothing
            pass

    def closeEvent(self, event):
        if self.current_state is not State.Nothing_Loaded:
            self.current_state = State.Destroying
            self.destroy_tracker()

    def play_pause(self):
        if self.current_state == State.Running:
            self.ui.playPauseButton.setEnabled(False)
            self.ui.playPauseButton.setText(ui_strings.PAUSING)
            self.tracker.running = False
        else:
            self.current_state = State.Running
            self.update_play_button()
            if self.playHeadChanged:
                self.update_history()
            else:
                self.debug_logger.print_debug_info(
                    16, int(self.ui.playHeadSlider.value()))
                self.tracker.video_data.current_frame = int(
                    self.ui.playHeadSlider.value())
                self.debug_logger.print_debug_info(18)
                self.tracker.gen_start.emit(True)

    def update_play_button(self):
        if self.current_state == State.Running:
            self.ui.playPauseButton.setEnabled(True)
            self.ui.playPauseButton.setText(ui_strings.PAUSE)
        else:
            if self.current_state == State.Stopped:
                self.ui.playPauseButton.setEnabled(True)
                self.ui.playPauseButton.setText(ui_strings.PLAY)
            if self.current_state == State.Nothing_Loaded:
                self.ui.playPauseButton.setText(ui_strings.PLAY)

    def init_controls(self, video_length):
        self.ui.playHeadSlider.setMaximum(video_length)
        self.ui.playHeadSlider.setEnabled(True)
        self.ui.playPauseButton.setEnabled(True)

    def update_history(self):
        self.ui.playHeadSlider.setEnabled(False)
        self.ui.playPauseButton.setEnabled(False)
        self.playHeadChanged = False

        self.current_state = State.Preparing_History
        self.pb = ProgressBarLabel(self)
        self.ui.verticalLayout_2.addWidget(self.pb)
        self.pb.bar.setMaximum(self.tracker.data.history_frames)
        self.pb.set_message(ui_strings.PREPARE_HISTORY)
        self.tracker.gen_history.emit(self.ui.playHeadSlider.value())

    def update_playHead(self):
        self.playHeadChanged = True

    def playHead_sliderUp(self):
        if self.current_state == State.Running:
            self.current_state = State.Slider_Moved_While_Running
            self.tracker.running = False
        else:
            self.sliderIgnoreUpdates = False

    def playHead_sliderDown(self):
        self.sliderIgnoreUpdates = True

    def show_hide_pe(self, checked):
        if checked:
            self.ui.propertyDockWidget.show()
        else:
            self.ui.propertyDockWidget.hide()

    def open_about(self):
        about = about_controller()
        about.exec_()
Ejemplo n.º 16
0
class TableClothGenerator(QMainWindow):
    def __init__(self, parent=None):

        super().__init__(parent)

        # Main UI settings
        self.setWindowTitle('Tablecloth Generator')
        self.setWindowIcon(QIcon('icon.ico'))
        self.centralWidget = QWidget()

        self.setCentralWidget(self.centralWidget)
        self.resize(350, 350)
        self.center()

        self._createMenuBar()

        self.MainUI()

    def MainUI(self):

        # Obtain the configs
        fp_config = open(THISDIR + "\\config\\config.json", "r",
                            encoding="utf-8")
        self.config = json.loads(fp_config.read())
        fp_config.close()

        # Obtain and List the teams
        fp_teams = open(THISDIR + "\\config\\teams.json", "r",
                            encoding="utf-8")
        conf_teams = json.loads(fp_teams.read())
        fp_teams.close()
        self.teams = conf_teams["teams"]
        self.players = conf_teams["players"]

        # Obtain all images needed to create the tablecloth
        self.background = Image.open(THISDIR + "\\images\\mat.png")
        self.table_border = Image.open(THISDIR + "\\images\\table_border.png")
        self.tech_lines = Image.open(THISDIR + "\\images\\technical_lines.png")

        # Check if there's no configuration set up
        # and prompt to create/import one
        if self.config["total_teams"] == 0:
            self.no_config = QMessageBox.question(self, "No configuration",
            "No configuration has been found. Do you wish to set up a new one?",
            QMessageBox.Yes | QMessageBox.No)

            if self.no_config == QMessageBox.Yes:
                self.CreateTeamsWindow()

        self.bg_image = self.config["image_route"]
        self.players_combobox = QComboBox()
        self.UpdatePlayersList()
        self.players_combobox.setEditable(True)
        self.players_combobox.completer()\
                             .setCompletionMode(QCompleter.PopupCompletion)
        self.players_combobox.setInsertPolicy(QComboBox.NoInsert)

        # Set up the GUI
        self.statusBar().showMessage("Remember: Rig responsibly.")
        # Bottom (EAST)
        self.label_east = QLabel(self)
        self.label_east.setText("<h1>East Seat</h1>")
        self.label_east.setAlignment(QtCore.Qt.AlignCenter)
        self.image_east = QLabel(self)
        self.image_east.setPixmap(QPixmap("images/logos/team1.png")\
                                  .scaled(100,100))
        self.image_east.setAlignment(QtCore.Qt.AlignCenter)
        self.search_east = QLineEdit()
        self.search_east.setAlignment(QtCore.Qt.AlignCenter)
        self.search_east.editingFinished.connect(
            lambda: self.searchPlayer(self.search_east.text(),
                                      self.cloth_east))
        self.cloth_east = QComboBox()
        self.cloth_east.setModel(self.players_combobox.model())
        self.cloth_east.currentIndexChanged.connect(
            lambda: self.SwitchImage(self.cloth_east, self.image_east))
        # Right (SOUTH)
        self.label_south = QLabel(self)
        self.label_south.setText("<h1>South Seat</h1>")
        self.label_south.setAlignment(QtCore.Qt.AlignCenter)
        self.image_south = QLabel(self)
        self.image_south.setPixmap(QPixmap("images/logos/team1.png")\
                                   .scaled(100,100))
        self.image_south.setAlignment(QtCore.Qt.AlignCenter)
        self.image_south.show()
        self.search_south = QLineEdit()
        self.search_south.setAlignment(QtCore.Qt.AlignCenter)
        self.search_south.editingFinished.connect(
            lambda: self.searchPlayer(self.search_south.text(),
                                      self.cloth_south))
        self.cloth_south = QComboBox()
        self.cloth_south.setModel(self.players_combobox.model())
        self.cloth_south.currentIndexChanged.connect(
            lambda: self.SwitchImage(self.cloth_south, self.image_south))
        # Top (WEST)
        self.label_west = QLabel(self)
        self.label_west.setText("<h1>West Seat</h1>")
        self.label_west.setAlignment(QtCore.Qt.AlignCenter)
        self.image_west = QLabel(self)
        self.image_west.setPixmap(QPixmap("images/logos/team1.png")\
                                  .scaled(100,100))
        self.image_west.setAlignment(QtCore.Qt.AlignCenter)
        self.image_west.show()
        self.cloth_west = QComboBox()
        self.search_west = QLineEdit()
        self.search_west.setAlignment(QtCore.Qt.AlignCenter)
        self.search_west.editingFinished.connect(
            lambda: self.searchPlayer(self.search_west.text(),
                                      self.cloth_west))
        self.cloth_west.setModel(self.players_combobox.model())
        self.cloth_west.currentIndexChanged.connect(
            lambda: self.SwitchImage(self.cloth_west, self.image_west))
        # Left (NORTH)
        self.label_north = QLabel(self)
        self.label_north.setText("<h1>North Seat</h1>")
        self.label_north.setAlignment(QtCore.Qt.AlignCenter)
        self.image_north = QLabel(self)
        self.image_north.setPixmap(QPixmap("images/logos/team1.png")\
                                   .scaled(100,100))
        self.image_north.setAlignment(QtCore.Qt.AlignCenter)
        self.image_north.show()
        self.cloth_north = QComboBox()
        self.search_north = QLineEdit()
        self.search_north.setAlignment(QtCore.Qt.AlignCenter)
        self.search_north.editingFinished.connect(
            lambda: self.searchPlayer(self.search_north.text(),
                                      self.cloth_north))
        self.cloth_north.setModel(self.players_combobox.model())
        self.cloth_north.currentIndexChanged.connect(
            lambda: self.SwitchImage(self.cloth_north, self.image_north))
        # Technical lines
        self.technical_lines = QCheckBox("Show Technical lines", self)
        # Generate button
        self.generate = QPushButton(self)
        self.generate.setText("Generate Tablecloth")
        self.generate.clicked.connect(self.GeneratePreview)
        # Add custom mat
        self.custom_mat = QPushButton(self)
        self.custom_mat.setText("Add Mat")
        self.custom_mat.clicked.connect(self.MatDialog)

        # Create the layout
        grid_layout = QGridLayout()
        grid_layout.setAlignment(QtCore.Qt.AlignCenter)
        grid_layout.setAlignment(QtCore.Qt.AlignTop)
        # Labels East, West
        grid_layout.addWidget(self.label_east, 1, 1)
        grid_layout.addWidget(self.label_west, 1, 2)
        # Image preview East, West
        grid_layout.addWidget(self.image_east, 2, 1)
        grid_layout.addWidget(self.image_west, 2, 2)
        # Search player East, West
        grid_layout.addWidget(self.search_east, 3, 1)
        grid_layout.addWidget(self.search_west, 3, 2)
        # Player combobox East, West
        grid_layout.addWidget(self.cloth_east, 4, 1)
        grid_layout.addWidget(self.cloth_west, 4, 2)
        # Labes South, North
        grid_layout.addWidget(self.label_south, 5, 1)
        grid_layout.addWidget(self.label_north, 5, 2)
        # Image preview South, North
        grid_layout.addWidget(self.image_south, 6, 1)
        grid_layout.addWidget(self.image_north, 6, 2)
        # Search player South, North
        grid_layout.addWidget(self.search_south, 7, 1)
        grid_layout.addWidget(self.search_north, 7, 2)
        # Player combobox South, North
        grid_layout.addWidget(self.cloth_south, 8, 1)
        grid_layout.addWidget(self.cloth_north, 8, 2)
        # Technical lines
        grid_layout.addWidget(self.technical_lines, 9, 1)
        # Custom mat/bg
        grid_layout.addWidget(self.custom_mat, 10, 1)
        # Generate
        grid_layout.addWidget(self.generate, 10, 2)

        self.centralWidget.setLayout(grid_layout)

        # Create the window
        self.show()

    def _createMenuBar(self):

        # Settings and stuff for the toolbar
        menubar = QMenuBar(self)

        file_menu = QMenu("&File", self)
        file_menu.addAction("Create Team(s)", self.CreateTeamsWindow)
        file_menu.addAction("Edit Team(s)", self.EditTeamsWindow)
        file_menu.addAction("Exit", self.close)
        settings_menu = QMenu("&Settings", self)
        settings_menu.addAction("Version", self.SeeVersion)
        settings_menu.addAction("Help", self.GetHelp)

        menubar.addMenu(file_menu)
        menubar.addMenu(settings_menu)

        self.setMenuBar(menubar)

    def _createProgressBar(self):

        self.progress_bar = QProgressBar()
        self.progress_bar.minimum = 0
        self.progress_bar.maximum = 100
        self.progress_bar.setValue(0)
        self.progress_bar.setTextVisible(False)
        self.progress_bar.setGeometry(50, 50, 10, 10)
        self.progress_bar.setAlignment(QtCore.Qt.AlignRight)
        self.progress_bar.adjustSize()
        self.statusBar().addPermanentWidget(self.progress_bar)
        self.ChangeAppStatus(False)

    def SwitchImage(self, cloth, image):
        # It shows you the team logo. No way you can miss those, right?
        team_id = self.SearchTeamID(cloth, True)
        image.setPixmap(QPixmap(
            "images/logos/team%d.png" % team_id).scaled(100,100))

    def searchPlayer(self, text, combobox):
        # It even searches the player for you. What more could you want?
        search_index = combobox.findText(text, QtCore.Qt.MatchContains)
        if search_index == -1:
            QMessageBox.warning(self, "Error", "No player found")
        else:
            combobox.setCurrentIndex(search_index)

    def CreateTeamsWindow(self):

        self.teamcreation_wid = EditionWidget()
        self.teamcreation_wid.resize(400, 200)
        self.teamcreation_wid.setWindowTitle("Teams configuration")

        self.new_config = {}

        id_label = QLabel(self)
        id_label.setText("Team ID: ")
        self.num_id = QLabel(self)
        current_id = str(self.config["total_teams"] + 1)
        self.num_id.setText(current_id)
        name_label = QLabel(self)
        name_label.setText("Team Name:")
        name_label.setFocus()
        self.name_input = QLineEdit(self)
        members_label = QLabel(self)
        members_label.setText("Members (write and press enter):")
        members_input = QLineEdit(self)
        members_input.editingFinished.connect(
            lambda: self.AddMember(members_input))
        self.members_list = QListWidget(self)

        import_image = QPushButton(self)
        import_image.setText("Import Team Image")
        import_image.clicked.connect(self.ImportTeamImage)

        add_team = QPushButton(self)
        add_team.setText("Add Team")
        add_team.clicked.connect(
            lambda: self.addTeamFunction(self.name_input.text(),
                self.members_list))
        import_config = QPushButton(self)
        import_config.setText("Import configuration")
        import_config.clicked.connect(self.importTeamFunction)

        config_lay = QGridLayout()
        config_lay.addWidget(id_label, 1, 0)
        config_lay.addWidget(self.num_id, 1, 1)
        config_lay.addWidget(name_label, 2, 0)
        config_lay.addWidget(self.name_input, 2, 1)
        config_lay.addWidget(members_label, 3, 0)
        config_lay.addWidget(members_input, 3, 1)
        config_lay.addWidget(self.members_list, 4, 0, 2, 2)
        config_lay.addWidget(add_team, 6, 0)
        config_lay.addWidget(import_image, 6, 1)
        config_lay.addWidget(import_config, 7, 0, 1, 2)
        self.teamcreation_wid.setLayout(config_lay)

        self.teamcreation_wid.setWindowModality(QtCore.Qt.ApplicationModal)
        self.teamcreation_wid.activateWindow()
        self.teamcreation_wid.raise_()
        self.teamcreation_wid.show()

    def addTeamFunction(self, name, members):
        fp_teams = open(THISDIR + "\\config\\teams.json", "r",
                            encoding="utf-8")
        current_teams = json.loads(fp_teams.read())
        fp_teams.close()
        team = {}
        current_teams["teams"].append(name)
        current_teams["players"][name] = [str(self.members_list.item(i).text())\
                                    for i in range(self.members_list.count())]
        new_team = open(THISDIR + "\\config\\teams.json", "w+",
                            encoding="utf-8")
        add_config = open(THISDIR + "\\config\\config.json", "w+",
                            encoding="utf-8")
        self.teams = current_teams["teams"]
        self.players = current_teams["players"]
        self.config["total_teams"] += 1
        new_id = self.config["total_teams"] + 1
        self.num_id.setText(str(new_id))
        add_config.write(json.dumps(self.config, indent=4))
        new_team.write(json.dumps(current_teams, indent=4))
        new_team.close()

        self.name_input.clear()

        self.members_list.clear()

        self.UpdatePlayersList()

    def ImportTeamImage(self):

        image_dialog = QFileDialog(self)
        image_dialog = QFileDialog.getOpenFileName(filter="Images (*.png)",
            selectedFilter="Images (*.png)")

        if image_dialog[0] != "":
            new_team_logo = Image.open(image_dialog[0]).convert("RGBA")
            if new_team_logo.size != (250, 250):
                new_team_logo.resize((250, 250))
            new_team_logo.save(THISDIR+"\\images\\logos\\team%s.png"\
                % self.num_id.text())

            QMessageBox.information(self, "Team Image", "Team image added.")

    def importTeamFunction(self):
        file_dialog = QFileDialog(self)
        file_dialog = QFileDialog.getOpenFileName(
                                    filter="Team Files (*.json *.zip)",
                                    selectedFilter="Team Files (*.json *.zip)")

        if file_dialog[0] != "":
            if is_zipfile(file_dialog[0]):
                with ZipFile(file_dialog[0]) as zip_import:
                    list_of_files = zip_import.namelist()
                    for fimp in list_of_files:
                        if fimp.startswith('logos'):
                            zip_import.extract(fimp, path=THISDIR+'\\images\\')
                    imported_teams = zip_import.read('teams.json')
                    imported_teams = imported_teams.decode('utf-8')
            else:
                imported_teams = open(file_dialog[0], "r",
                                encoding="utf-8").read()
            json_teams = json.loads(imported_teams)
            self.teams = json_teams["teams"]
            self.players = json_teams["players"]

            new_teams = open(THISDIR + "\\config\\teams.json", "w+",
                            encoding="utf-8")
            new_teams.write(json.dumps(json_teams, indent=4))
            new_teams.close()

            old_config = open(THISDIR + "\\config\\config.json", "r",
                                encoding="utf-8").read()
            old_config = json.loads(old_config)
            old_config["total_teams"] = len(json_teams["teams"])
            self.config = old_config
            new_config = open(THISDIR + "\\config\\config.json", "w+",
                                encoding="utf-8")
            new_config.write(json.dumps(self.config, indent=4))
            new_config.close()

            self.UpdatePlayersList()

            self.image_east.setPixmap(QPixmap("images/logos/team1.png")\
                                       .scaled(100,100))
            self.cloth_east.setModel(self.players_combobox.model())
            self.image_south.setPixmap(QPixmap("images/logos/team1.png")\
                                       .scaled(100,100))
            self.cloth_south.setModel(self.players_combobox.model())
            self.image_west.setPixmap(QPixmap("images/logos/team1.png")\
                                       .scaled(100,100))
            self.cloth_west.setModel(self.players_combobox.model())
            self.image_north.setPixmap(QPixmap("images/logos/team1.png")\
                                       .scaled(100,100))
            self.cloth_north.setModel(self.players_combobox.model())
            self.statusBar().showMessage("Teams imported successfully.")
            self.teamcreation_wid.close()


    def AddMember(self, member):
        self.members_list.addItem(member.text())
        member.clear()

    def EditTeamsWindow(self):

        self.teamedit_wid = EditionWidget()
        self.teamedit_wid.resize(400, 320)
        self.teamedit_wid.setWindowTitle("Edit Teams")

        self.teams_list = QComboBox(self)
        self.teams_list.addItem("--- Select a team ---")
        for team in self.teams:
            self.teams_list.addItem(team)
        self.teams_list.currentIndexChanged.connect(self.UpdateTeamInfo)

        team_id_label = QLabel(self)
        team_id_label.setText("Team ID: ")
        self.config_team_id = QLabel(self)
        team_name_label = QLabel(self)
        team_name_label.setText("Team name: ")
        self.config_team_name = QLabel(self)
        team_members_label = QLabel(self)
        team_members_label.setText("Team members: ")
        self.config_team_members = QListWidget(self)
        add_member_label = QLabel(self)
        add_member_label.setText("Add new member: ")
        add_member_input = QLineEdit(self)
        add_member_input.editingFinished.connect(self.AddNewMember)

        delete_member = QPushButton(self)
        delete_member.setText("Delete member")
        delete_member.clicked.connect(self.DeleteMember)
        delete_team = QPushButton(self)
        delete_team.setText("Delete Team")
        delete_team.clicked.connect(self.DeleteTeam)
        save_changes = QPushButton(self)
        save_changes.setText("Save changes")
        save_changes.clicked.connect(self.SaveEdits)
        export_config = QPushButton(self)
        export_config.setText("Export Configuration")
        export_config.clicked.connect(self.ExportTeams)

        config_lay = QGridLayout()
        config_lay.addWidget(self.teams_list, 1, 0)
        config_lay.addWidget(team_id_label, 2, 0)
        config_lay.addWidget(self.config_team_id, 2, 1)
        config_lay.addWidget(team_name_label, 3, 0)
        config_lay.addWidget(self.config_team_name, 3, 1, 1, 2)
        config_lay.addWidget(team_members_label, 4, 0)
        config_lay.addWidget(self.config_team_members, 5, 0)
        config_lay.addWidget(add_member_label, 6, 0)
        config_lay.addWidget(add_member_input, 6, 1, 1, 2)
        config_lay.addWidget(delete_member, 7, 0)
        config_lay.addWidget(delete_team, 7, 1)
        config_lay.addWidget(save_changes, 8, 0)
        config_lay.addWidget(export_config, 8, 1)

        self.teamedit_wid.setLayout(config_lay)
        self.teamedit_wid.setWindowModality(QtCore.Qt.ApplicationModal)
        self.teamedit_wid.activateWindow()
        self.teamedit_wid.raise_()
        self.teamedit_wid.show()

    def UpdateTeamInfo(self):

        sender = self.sender()
        if sender.currentIndex() > 0:
            team_id = sender.currentIndex()
            self.config_team_id.setText(str(team_id))
            self.config_team_name.setText(sender.currentText())
            if self.config_team_members.count() > 0:
                self.config_team_members.clear()
            self.config_team_members.addItems(
                self.players[sender.currentText()])

    def AddNewMember(self):

        sender = self.sender()
        self.config_team_members.addItem(sender.text())
        sender.clear()

    def DeleteMember(self):

        list_members = self.config_team_members.selectedItems()
        if len(list_members) == 0:
            QMessageBox.warning(self, "Error", "No player selected")
        else:
            for member in list_members:
                self.config_team_members.takeItem(
                                           self.config_team_members.row(member))

    def DeleteTeam(self):
        team_id = int(self.config_team_id.text())
        is_last_item = self.teams[self.teams.index(
            self.config_team_name.text())] == (self.teams[len(self.teams)-1])
        self.teams.pop(self.teams.index(self.config_team_name.text()))
        self.players.pop(self.config_team_name.text())
        new_teamlist = {}
        new_teamlist["teams"] = self.teams
        new_teamlist["players"] = self.players
        current_teams = open(THISDIR + "\\config\\teams.json", "w+",
                        encoding="utf-8")
        current_teams.write(json.dumps(new_teamlist, indent=4))
        current_teams.close()

        if is_last_item == True:
            self.teams_list.setCurrentIndex(1)
        else:
            self.teams_list.setCurrentIndex(team_id+1)
        self.teams_list.removeItem(team_id)
        self.UpdatePlayersList()
        self.cloth_east.setModel(self.players_combobox.model())
        self.cloth_south.setModel(self.players_combobox.model())
        self.cloth_west.setModel(self.players_combobox.model())
        self.cloth_north.setModel(self.players_combobox.model())

    def ExportTeams(self):

        export_dir = self.config["save_route"] if self.config["save_route"] \
                                                    is not None else THISDIR
        exported_file = QFileDialog.getSaveFileName(self, "Save File",
            export_dir, "Save files (*.zip)")

        if exported_file[0] != "":
            export_filename = exported_file[0]
            if export_filename.endswith(".zip") is False:
                export_filename += ".zip"
            files_to_export = []
            files_to_export.append("config\\teams.json")

            for root, directories, files in os.walk(THISDIR+"\\images\\logos"):
                for filename in files:
                    filepath = os.path.join(root, filename)
                    files_to_export.append(filepath)

            with ZipFile(export_filename, "w") as export_zip:
                for exp_file in files_to_export:
                    export_name = exp_file
                    if exp_file.endswith(".json"):
                        split_name = exp_file.split("\\")
                        export_name = split_name[-1]
                    if exp_file.endswith(".png"):
                        split_name = exp_file.split("\\")
                        export_name = "\\logos\\" + split_name[-1]
                    export_zip.write(exp_file, arcname=export_name)
                export_zip.close()

            if os.path.exists(export_filename):
                QMessageBox.information(self, "Export",
                    "The export was successful")

    def SaveEdits(self):

        list_members = [str(self.config_team_members.item(i).text()) for i in \
                                        range(self.config_team_members.count())]
        self.players[self.config_team_name.text()] = list_members
        new_teamlist = {}
        new_teamlist["teams"] = self.teams
        new_teamlist["players"] = self.players
        current_teams = open(THISDIR + "\\config\\teams.json", "w+",
                        encoding="utf-8")
        current_teams.write(json.dumps(new_teamlist, indent=4))
        current_teams.close()
        self.teamedit_wid.close()
        self.statusBar().showMessage("Settings saved.")

    def MatDialog(self):

        mat_dialog = QFileDialog(self)
        mat_dialog = QFileDialog.getOpenFileName(filter="Images (*.png *.jpg)",
            selectedFilter="Images (*.png *.jpg)")

        if mat_dialog[0] != "":
            self.GenerateMat(mat_dialog[0])

    def GenerateMat(self, image):

        self.background = image
        background = Image.open(self.background).resize((2048,2048))\
                                                .convert("RGBA")

        self.mat_thread = QThread()
        east_id = self.SearchTeamID(self.cloth_east, True)
        south_id = self.SearchTeamID(self.cloth_south, True)
        west_id = self.SearchTeamID(self.cloth_west, True)
        north_id = self.SearchTeamID(self.cloth_north, True)

        if self.config["save_route"] is None:
            save_to_route = THISDIR
        else:
            save_to_route = self.config["save_route"]
        self._createProgressBar()

        self.mat_worker = GenerateImageThread(background, self.table_border,
            east_id, south_id, west_id, north_id,
            self.technical_lines.isChecked(), save_to_route,
            self.bg_image, True)
        self.mat_worker.moveToThread(self.mat_thread)
        self.mat_thread.started.connect(self.mat_worker.run)
        self.mat_worker.update_progress.connect(self.UpdateStatus)
        self.mat_worker.finished.connect(self.mat_thread.quit)
        self.mat_worker.finished.connect(self.mat_worker.deleteLater)
        self.mat_thread.finished.connect(self.mat_thread.deleteLater)
        self.mat_thread.finished.connect(self.MatPreviewWindow)
        self.mat_thread.start()


    def MatPreviewWindow(self):

        self.statusBar().showMessage('Mat preview generated.')
        self.statusBar().removeWidget(self.progress_bar)
        # Now you can go back to rigging
        self.ChangeAppStatus(True)

        self.mat_wid = QWidget()
        self.mat_wid.resize(600, 600)
        self.mat_wid.setWindowTitle("Background preview")

        mat_preview_title = QLabel(self)
        mat_preview_title.setText("Selected image (1/4 scale)")
        mat_preview = QLabel(self)
        mat_preview.setPixmap(QPixmap(tempfile.gettempdir()+"\\Table_Dif.jpg")\
                                .scaled(512,512))
        confirm = QPushButton(self)
        confirm.setText("Confirm")
        confirm.clicked.connect(
            lambda: self.ChangeMatImage(self.background))

        vbox = QVBoxLayout()
        vbox.setAlignment(QtCore.Qt.AlignCenter)
        vbox.addWidget(mat_preview_title)
        vbox.addWidget(mat_preview)
        vbox.addWidget(confirm)
        self.mat_wid.setLayout(vbox)

        self.mat_wid.setWindowModality(QtCore.Qt.ApplicationModal)
        self.mat_wid.activateWindow()
        self.mat_wid.raise_()
        self.mat_wid.show()

    def ChangeMatImage(self, image):

        new_bg = Image.open(image)

        if new_bg.size != (2048, 2048):
            new_bg = new_bg.resize((2048, 2048))
        if new_bg.mode != "RGBA":
            new_bg = new_bg.convert("RGBA")

        if self.config["save_route"] is not None:
            new_bg.save(self.config["save_route"]+"\\images\\mat.png")
            self.bg_image = self.config["save_route"]+"\\images\\mat.png"
        else:
            new_bg.save(THISDIR+"\\images\\mat.png")
            self.bg_image = THISDIR+"\\images\\mat.png"

        self.background = new_bg
        self.config["image_route"] = self.bg_image

        new_file = open(THISDIR + "\\config\\config.json", "w+",
                                        encoding="utf-8")
        new_file.write(json.dumps(self.config, indent=4))
        new_file.close()

        self.statusBar().showMessage('New background added.')
        self.statusBar().removeWidget(self.progress_bar)
        self.ChangeAppStatus(True)

        self.mat_wid.close()

    def GeneratePreview(self):

        self.preview_thread = QThread()
        east_id = self.SearchTeamID(self.cloth_east, True)
        south_id = self.SearchTeamID(self.cloth_south, True)
        west_id = self.SearchTeamID(self.cloth_west, True)
        north_id = self.SearchTeamID(self.cloth_north, True)

        if self.config["save_route"] is None:
            save_to_route = THISDIR
        else:
            save_to_route = self.config["save_route"]
        self._createProgressBar()

        self.preview_worker = GenerateImageThread(self.background,
            self.table_border, east_id, south_id, west_id, north_id,
            self.technical_lines.isChecked(), save_to_route,
            self.bg_image, True)
        self.preview_worker.moveToThread(self.preview_thread)
        self.preview_thread.started.connect(self.preview_worker.run)
        self.preview_worker.update_progress.connect(self.UpdateStatus)
        self.preview_worker.finished.connect(self.preview_thread.quit)
        self.preview_worker.finished.connect(self.preview_worker.deleteLater)
        self.preview_thread.finished.connect(self.preview_thread.deleteLater)
        self.preview_thread.finished.connect(self.PreviewWindow)
        self.preview_thread.start()

    def PreviewWindow(self):

        self.statusBar().showMessage('Tablecloth preview generated.')
        self.statusBar().removeWidget(self.progress_bar)
        # Now you can go back to rigging
        self.ChangeAppStatus(True)

        self.preview_wid = QWidget()
        self.preview_wid.resize(600, 600)
        self.preview_wid.setWindowTitle("Tablecloth preview")

        tablecloth = QPixmap(tempfile.gettempdir()+"\\Table_Dif.jpg")

        tablecloth_preview_title = QLabel(self)
        tablecloth_preview_title.setText("Tablecloth preview (1/4 scale)")
        tablecloth_preview = QLabel(self)
        tablecloth_preview.setPixmap(tablecloth.scaled(512,512))
        confirm = QPushButton(self)
        confirm.setText("Confirm")
        confirm.clicked.connect(self.GenerateImage)
        confirm.clicked.connect(self.preview_wid.close)

        vbox = QVBoxLayout()
        vbox.setAlignment(QtCore.Qt.AlignCenter)
        vbox.addWidget(tablecloth_preview_title)
        vbox.addWidget(tablecloth_preview)
        vbox.addWidget(confirm)
        self.preview_wid.setLayout(vbox)

        self.preview_wid.setWindowModality(QtCore.Qt.ApplicationModal)
        self.preview_wid.activateWindow()
        self.preview_wid.raise_()
        self.preview_wid.show()

    def GeneratedDialog(self):

        self.statusBar().showMessage('Tablecloth generated. Happy rigging!')
        self.statusBar().removeWidget(self.progress_bar)
        # Now you can go back to rigging
        self.ChangeAppStatus(True)

        mbox = QMessageBox()

        mbox.setWindowTitle("Tablecloth Generator")
        mbox.setText("Tablecloth Generated!")
        mbox.setStandardButtons(QMessageBox.Ok)

        mbox.exec()

    def UpdateStatus(self, status):
        self.progress_bar.setValue(status)

    def GenerateImage(self):

        self.statusBar().showMessage('Generating image...')
        self._createProgressBar()

        if self.config["save_route"] is None:
            self.config["save_route"] = THISDIR

        save_to_route = QFileDialog.getExistingDirectory(self,
            "Where to save the image", self.config["save_route"],
            QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)

        if self.config["save_route"] != save_to_route:
            temp_file = open(THISDIR + "\\config\\config.json", "r",
                                            encoding="utf-8")
            fp_teams = json.loads(temp_file.read())
            fp_teams["save_route"] = save_to_route
            fp_teams["image_route"] = self.bg_image
            new_file = open(THISDIR + "\\config\\config.json", "w+",
                                            encoding="utf-8")
            new_file.write(json.dumps(fp_teams, indent=4))
            new_file.close()

        self.background = Image.open(THISDIR + "\\images\\mat.png")
        self.table_border = Image.open(THISDIR + "\\images\\table_border.png")
        self.tech_lines = Image.open(THISDIR + "\\images\\technical_lines.png")

        self.thread = QThread()
        east_id = self.SearchTeamID(self.cloth_east, True)
        south_id = self.SearchTeamID(self.cloth_south, True)
        west_id = self.SearchTeamID(self.cloth_west, True)
        north_id = self.SearchTeamID(self.cloth_north, True)
        self.worker = GenerateImageThread(self.background, self.table_border,
            east_id, south_id, west_id, north_id,
            self.technical_lines.isChecked(), save_to_route, self.bg_image)
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.worker.update_progress.connect(self.UpdateStatus)
        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.worker.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)
        self.thread.finished.connect(self.GeneratedDialog)
        self.thread.start()

    def ChangeAppStatus(self, status):
        # True for enable, False for disable.
        self.cloth_east.setEnabled(status)
        self.search_east.setEnabled(status)
        self.cloth_south.setEnabled(status)
        self.search_south.setEnabled(status)
        self.cloth_west.setEnabled(status)
        self.search_west.setEnabled(status)
        self.cloth_north.setEnabled(status)
        self.search_north.setEnabled(status)
        self.generate.setEnabled(status)

    def SearchTeamID(self, cloth, plus_one=False):
        team_id = self.teams.index(cloth.itemData(cloth.currentIndex()))
        if plus_one:
            team_id += 1
        return team_id

    def UpdatePlayersList(self):
        for team, members in self.players.items():
            for member in members:
                self.players_combobox.addItem(member, team)

    def center(self):
        qr = self.frameGeometry()
        cp = QScreen().availableGeometry().center()
        qr.moveCenter(cp)

    def SeeVersion(self):

        git_url = "https://raw.githubusercontent.com/vg-mjg/tablecloth-"
        git_url += "generator/main/version.txt"
        with urllib.request.urlopen(git_url) as response:
            url_version = response.read().decode("utf-8")

        version = "Your version is up to date!"
        if url_version != VERSION:
            version = "Your version is outdated."
            version += "Please check the <a href='https://github.com/vg-mjg/"
            version += "tablecloth-generator/releases'>Github page</a>"
            version +=" for updates."
        version_message = QMessageBox(self)
        version_message.setWindowTitle("Checking version")
        version_message.setText("""<h1>Tablecloth generator</h1>
            <br>
            <b>Current Version:</b> %s<br>
            <b>Your Version:</b> %s<br>
            <i>%s</i>
            """ % (url_version, VERSION, version))

        version_message.exec()

    def GetHelp(self):
        webbrowser.open("https://github.com/vg-mjg/tablecloth-generator/wiki")
Ejemplo n.º 17
0
class Controller:
    def __init__(self):

        # gui
        self.app = QApplication([])
        self.main_window = MainWindow(controller=self)

        # device
        self.device = Device()

        # fps stats
        self.fps_timer = QTimer()
        self.fps_timer.timeout.connect(self.update_ui_fps)
        self.spf = 1  # seconds per frame
        self.timestamp_last_capture = 0

        # acquisition thread
        self.continuous_acquisition = False
        self.worker_wait_condition = QWaitCondition()
        self.acquisition_worker = AcquisitionWorker(self.worker_wait_condition,
                                                    device=self.device)
        self.acquisition_thread = QThread()
        self.acquisition_worker.moveToThread(self.acquisition_thread)
        self.acquisition_thread.started.connect(self.acquisition_worker.run)
        self.acquisition_worker.finished.connect(self.acquisition_thread.quit)
        # self.acquisition_worker.finished.connect(self.acquisition_thread.deleteLater)
        # self.acquisition_thread.finished.connect(self.acquisition_worker.deleteLater)
        self.acquisition_worker.data_ready.connect(self.data_ready_callback)
        self.acquisition_thread.start()

        # default timebase
        self.set_timebase("20 ms")

        # on app exit
        self.app.aboutToQuit.connect(self.on_app_exit)

    def run_app(self):
        self.main_window.show()
        return self.app.exec_()

    def get_ports_names(self):
        return [p.device for p in serial.tools.list_ports.comports()]

    def update_ui_fps(self):
        fps = 1 / self.spf
        self.main_window.control_panel.stats_panel.fps_label.setText(
            f"{fps:.2f} fps")

    def set_timebase(self, timebase: str):
        # send timebase to device
        self.device.timebase = timebase
        if self.is_device_connected():
            self.device.write_timebase()
        # adjust timebase in the screen
        seconds_per_sample = (float(timebase.split()[0]) / 10 * {
            "ms": 1e-3,
            "us": 1e-6
        }[timebase.split()[1]])
        self.data_time_array = (np.arange(0, self.device.BUFFER_SIZE) *
                                seconds_per_sample)
        self.main_window.screen.setXRange(0,
                                          self.device.BUFFER_SIZE *
                                          seconds_per_sample,
                                          padding=0.02)
        self.main_window.screen.setYRange(0, 5)

    def set_trigger_state(self, on):
        self.device.trigger_on = on
        if self.is_device_connected():
            self.device.write_trigger_state()

    def set_trigger_slope(self, slope):
        self.device.trigger_slope = slope
        if self.is_device_connected():
            self.device.write_trigger_slope()

    def connect_to_device(self, port):
        if port == "":
            QMessageBox.about(
                self.main_window,
                "Connection failed",
                "Could not connect to device. No port is selected.",
            )
        elif port not in self.get_ports_names():
            QMessageBox.about(
                self.main_window,
                "Connection failed",
                f"Could not connect to device. Port {port} not available. Refresh and try again.",
            )
        else:
            self.device.connect(port)

    def disconnect_device(self):
        self.device.disconnect()

    def is_device_connected(self):
        return self.device.is_connected()

    def show_no_connection_message(self):
        QMessageBox.about(
            self.main_window,
            "Device not connected",
            "No device is connected. Connect a device first.",
        )

    def oscilloscope_single_run(self):
        if self.device.is_connected():
            self.continuous_acquisition = False
            self.device.clean_buffers()
            self.worker_wait_condition.notify_one()
            return True
        else:
            self.show_no_connection_message()
            return False

    def oscilloscope_continuous_run(self):
        if self.device.is_connected():
            self.timestamp_last_capture = time.time()
            self.spf = 1
            self.fps_timer.start(500)
            self.continuous_acquisition = True
            self.device.clean_buffers()
            self.worker_wait_condition.notify_one()
            return True
        else:
            self.show_no_connection_message()
            return False

    def oscilloscope_stop(self):
        self.continuous_acquisition = False
        self.fps_timer.stop()

    def data_ready_callback(self):
        curr_time = time.time()
        self.spf = 0.9 * (curr_time -
                          self.timestamp_last_capture) + 0.1 * self.spf
        self.timestamp_last_capture = curr_time
        self.main_window.screen.update_ch(self.data_time_array,
                                          self.acquisition_worker.data)
        if self.continuous_acquisition == True:
            self.worker_wait_condition.notify_one()

    def on_app_exit(self):
        print("exiting")
Ejemplo n.º 18
0
class GameDisplay(QWidget):
    default_font = 'Sans Serif,9,-1,5,50,0,0,0,0,0'
    rules_path: typing.Optional[str] = None

    move_needed = Signal(int, np.ndarray)  # active_player, board
    move_made = Signal(np.ndarray)  # board
    game_ended = Signal(np.ndarray)  # final_board

    def __init__(self, start_state: GameState):
        super().__init__()
        self.start_state = start_state
        self.mcts_workers: typing.Dict[int, MctsWorker] = {}
        self.worker_thread: typing.Optional[QThread] = None
        self.current_state = self.start_state
        self.valid_moves = self.start_state.get_valid_moves()
        self._show_coordinates = False
        self.log_display = LogDisplay()
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.is_reviewing = False

    @property
    def show_coordinates(self):
        return self._show_coordinates

    @show_coordinates.setter
    def show_coordinates(self, value):
        self._show_coordinates = value
        scene = self.scene()
        size = QSize(scene.width(), scene.height())
        self.resizeEvent(QResizeEvent(size, size))

    @property
    def mcts_players(self):
        return [worker.player for worker in self.mcts_workers.values()]

    @mcts_players.setter
    def mcts_players(self, players: typing.Sequence[MctsPlayer]):
        self.stop_workers()

        self.log_display = LogDisplay()
        self.mcts_workers = {player.player_number: MctsWorker(player)
                             for player in players}
        if not self.mcts_workers:
            self.worker_thread = None
        else:
            self.worker_thread = QThread()
            for worker in self.mcts_workers.values():
                worker.move_chosen.connect(self.make_move)  # type: ignore
                worker.move_analysed.connect(self.analyse_move)  # type: ignore
                # noinspection PyUnresolvedReferences
                self.move_needed.connect(worker.choose_move)  # type: ignore
                # noinspection PyUnresolvedReferences
                self.move_made.connect(worker.analyse_move)  # type: ignore
                worker.moveToThread(self.worker_thread)
            self.worker_thread.start()

    def get_player(self, player_number: int) -> typing.Optional[MctsPlayer]:
        worker = self.mcts_workers.get(player_number)
        if worker:
            return worker.player
        return None

    @abstractmethod
    def update_board(self, board: GameState):
        """ Update self.scene, based on the state in board.

        It's probably also helpful to override resizeEvent().

        :param board: the state of the game to display.
        """

    def resizeEvent(self, event: QResizeEvent):
        self.update_board(self.current_state)

    @property
    def credit_pairs(self) -> typing.Iterable[typing.Tuple[str, str]]:
        """ Return a list of label and detail pairs.

        These are displayed in the about box.
        """
        return ()

    def choose_active_text(self):
        active_player = self.current_state.get_active_player()
        if active_player in self.mcts_workers:
            return 'thinking'
        return 'to move'

    @Slot(int)  # type: ignore
    def make_move(self, move: int):
        self.log_display.record_move(self.current_state, move)
        # noinspection PyUnresolvedReferences
        self.move_made.emit(self.current_state)  # type: ignore
        self.current_state = self.current_state.make_move(move)
        self.update_board(self.current_state)
        if self.current_state.is_ended():
            # noinspection PyUnresolvedReferences
            self.game_ended.emit(self.current_state)  # type: ignore

        forced_move = self.get_forced_move()
        if forced_move is None:
            self.request_move()
        else:
            self.make_move(forced_move)

    def get_forced_move(self) -> typing.Optional[int]:
        """ Override this method if some moves should be forced.

        Look at self.valid_moves and self.current_board to decide.
        :return: move number, or None if there is no forced move.
        """
        return None

    @Slot(GameState, int, list)  # type: ignore
    def analyse_move(
            self,
            board: GameState,
            analysing_player: int,
            move_probabilities: typing.List[typing.Tuple[str,
                                                         float,
                                                         int,
                                                         float]]):
        self.log_display.analyse_move(board,
                                      analysing_player,
                                      move_probabilities)

    def request_move(self):
        if self.current_state.is_ended():
            return
        player = self.current_state.get_active_player()
        # noinspection PyUnresolvedReferences
        self.move_needed.emit(player, self.current_state)

    def close(self):
        self.stop_workers()

    def stop_workers(self):
        if self.worker_thread is not None:
            self.worker_thread.quit()

    def can_move(self):
        if self.is_reviewing:
            return False
        return not self.current_state.get_active_player() in self.mcts_workers
Ejemplo n.º 19
0
class BridgeDock(QDockWidget):
    def __init__(self, parent, api: PluginApi) -> None:
        super().__init__('', parent)
        self.api = api
        self.ui = Ui_BridgeDock()
        self.ui.setupUi(self)
        self.server_thread = None

        self.observer = None
        self.modified_timer = None
        self.slot_server_running(False)

        self.ui.pushButtonStartServer.clicked.connect(self.slot_start_server)
        self.ui.pushButtonStopServer.clicked.connect(self.slot_stop_server)

        self.ui.labelConnectionStatus.setText('Server not yet running.')

        self.visibilityChanged.connect(self.slot_visibility_changed)

    def slot_visibility_changed(self, visible: bool) -> None:
        if not visible and self.server_thread is not None:
            self.slot_stop_server()

    def slot_server_running(self, running: bool) -> None:
        if running:
            self.ui.pushButtonStartServer.setVisible(False)
            self.ui.pushButtonStopServer.setVisible(True)
        else:
            self.ui.pushButtonStartServer.setVisible(True)
            self.ui.pushButtonStopServer.setVisible(False)

    def slot_start_server(self) -> None:
        self.server_thread = QThread()
        self.server_worker = ServerWorker()
        self.server_worker.signal_connected.connect(self.slot_connected)
        self.server_worker.signal_error.connect(self.slot_error)
        self.server_worker.signal_started.connect(self.slot_server_started)
        self.server_worker.signal_shutdown.connect(self.slot_server_stopped)
        self.server_worker.signal_script_addr.connect(self.slot_script_addr)
        self.server_worker.moveToThread(self.server_thread)
        self.server_thread.started.connect(self.server_worker.process)
        self.server_thread.start()
        self.slot_server_running(True)

    def slot_stop_server(self) -> None:
        # Shutdown needs to be triggered by the server thread, so send a request
        requests.get('http://*****:*****@') or stripped.endswith(':'):
                output += f'{line}\n'
                continue

            if '.ifdef' in stripped:
                if not ifdef_stack[-1]:
                    ifdef_stack.append(False)
                    output += f'{line}\n'
                    continue
                # TODO check variant
                is_usa = stripped.split(' ')[1] == 'USA'
                ifdef_stack.append(is_usa)
                output += f'{line}\n'
                continue
            if '.ifndef' in stripped:
                if not ifdef_stack[-1]:
                    ifdef_stack.append(False)
                    output += f'{line}\n'
                    continue
                is_usa = stripped.split(' ')[1] == 'USA'
                ifdef_stack.append(not is_usa)
                output += f'{line}\n'
                continue
            if '.else' in stripped:
                if ifdef_stack[-2]:
                    # If the outermost ifdef is not true, this else does not change the validiness of this ifdef
                    ifdef_stack[-1] = not ifdef_stack[-1]
                output += f'{line}\n'
                continue
            if '.endif' in stripped:
                ifdef_stack.pop()
                output += f'{line}\n'
                continue

            if not ifdef_stack[-1]:
                # Not defined for this variant
                output += f'{line}\n'
                continue

            if current_instruction >= len(instructions):
                # TODO maybe even not print additional lines?
                output += f'{line}\n'
                continue
            addr = instructions[current_instruction].addr
            prefix = ''
            if addr == script_offset:
                prefix = '>'
            output += f'{addr:03d}| {prefix}{line}\t\n'
            current_instruction += 1
            if stripped.startswith('SCRIPT_END'):
                break
        self.ui.labelCode.setText(output)
Ejemplo n.º 20
0
class ShiftabilityTesterPlugin:
    name = 'Shiftability Tester'
    description = 'Tests whether a rom with .space inside it was\nshifted correctly.'
    hidden = True

    def __init__(self, api: PluginApi) -> None:
        self.api = api
        self.locations = []

    def load(self) -> None:
        self.action_test_shiftability = self.api.register_menu_entry(
            'Test Shiftability', self.slot_test_shiftability)
        self.action_next_location = self.api.register_menu_entry(
            'Next Location', self.slot_next_location)
        self.action_next_location.setShortcut(QKeySequence(Qt.Key_F4))

    def unload(self) -> None:
        self.api.remove_menu_entry(self.action_test_shiftability)
        self.api.remove_menu_entry(self.action_next_location)

    def slot_test_shiftability(self) -> None:
        progress_dialog = self.api.get_progress_dialog(
            'Shiftability Tester', 'Testing shiftability...', False)
        progress_dialog.show()

        self.thread = QThread()
        self.worker = TestShiftabilityWorker()
        self.worker.moveToThread(self.thread)

        self.worker.signal_progress.connect(
            lambda progress: progress_dialog.set_progress(progress))
        self.worker.signal_done.connect(
            lambda: (self.thread.quit(), progress_dialog.close(),
                     self.api.show_message(
                         'Shiftability Tester',
                         'Test complete. See console for more information.')))
        self.worker.signal_fail.connect(
            lambda message: (self.thread.quit(), progress_dialog.close(
            ), self.api.show_error('Shiftability Tester', message)))
        self.worker.signal_locations.connect(self.slot_set_locations)

        self.thread.started.connect(self.worker.process)
        self.thread.start()

    def slot_set_locations(self, locations) -> None:
        self.locations = locations

    def slot_next_location(self) -> None:
        if len(self.locations) == 0:
            self.api.show_error(
                'Shiftability Tester',
                'Shiftability not tested yet or all locations visited.')
            return
        location = self.locations.pop(
            0) - 2  # as we shift by 0x10000, this should be moved

        # TODO add this in better to the plugin api: find linked usa controller

        controller = None
        for contrl in self.api.main_window.dock_manager.hex_viewer_manager.controllers:
            if contrl.rom_variant == RomVariant.USA and contrl.is_linked == True:
                controller = contrl
                break
        if controller is None:
            self.api.show_error('Shiftability Tester',
                                'Need a USA hex viewer that is linked')
            return

        controller.update_cursor(
            controller.address_resolver.to_virtual(location))
Ejemplo n.º 21
0
class Importador(QWidget):
    """GUI class for the BGG -> Ludopedia importer"""
    enable_editables = Signal(bool)
    alternative_chosen = Signal(object)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.thread = QThread()
        self.worker = None
        grid_layout = QGridLayout(self)
        login_group_box = self.create_login_group()
        data_group_box = self.create_data_group()
        self.enable_editables.connect(login_group_box.setEnabled)
        self.enable_editables.connect(data_group_box.setEnabled)
        self.import_button = QPushButton('Importar', self)
        self.import_button.setEnabled(False)
        self.enable_editables.connect(self.import_button.setEnabled)
        self.import_button.clicked.connect(self.enable_editables)
        self.import_button.clicked.connect(self.load_data)
        self.bgg_user_line_edit.textChanged.connect(self.enable_import)
        self.ludo_mail_line_edit.textChanged.connect(self.enable_import)
        self.ludo_pass_line_edit.textChanged.connect(self.enable_import)
        grid_layout.addWidget(login_group_box, 1, 1, 1, 2)
        grid_layout.addWidget(data_group_box, 2, 1, 1, 2)
        grid_layout.addWidget(self.import_button, 8, 2)
        self.log_widget = QTextEdit(self)
        self.log_widget.setReadOnly(True)
        grid_layout.addWidget(self.log_widget, 9, 1, 30, 2)

    def create_qlineedit(self, text):
        """Creates a label with the given text and an accompanying line edit"""
        line_edit = QLineEdit(self)
        line_edit_label = QLabel(text, line_edit)
        line_edit_label.setBuddy(line_edit)
        return (line_edit, line_edit_label)

    def create_login_group(self):
        """Create labels and line edits for providing BGG and ludopedia login information"""
        (self.bgg_user_line_edit,
         bgg_user_label) = self.create_qlineedit('Usuario BoardGameGeek:')
        (self.ludo_mail_line_edit,
         ludo_mail_label) = self.create_qlineedit('E-mail Ludopedia:')
        (self.ludo_pass_line_edit,
         ludo_pass_label) = self.create_qlineedit('Senha Ludopedia:')
        self.ludo_pass_line_edit.setEchoMode(QLineEdit.PasswordEchoOnEdit)
        group_box = QGroupBox('Login')
        grid_layout = QGridLayout(group_box)
        grid_layout.addWidget(bgg_user_label, 1, 1)
        grid_layout.addWidget(self.bgg_user_line_edit, 1, 2)
        grid_layout.addWidget(ludo_mail_label, 2, 1)
        grid_layout.addWidget(self.ludo_mail_line_edit, 2, 2)
        grid_layout.addWidget(ludo_pass_label, 3, 1)
        grid_layout.addWidget(self.ludo_pass_line_edit, 3, 2)
        group_box.setLayout(grid_layout)
        return group_box

    def create_data_group(self):
        """Creates group for holding specific choice data selection"""
        button_group = QButtonGroup(self)
        button_group.setExclusive(True)
        colecao_radio_button = QRadioButton('Coleção')
        self.partidas_radio_button = QRadioButton('Partidas')
        colecao_radio_button.setChecked(True)
        button_group.addButton(colecao_radio_button)
        button_group.addButton(self.partidas_radio_button)
        (self.min_date_picker,
         min_date_label) = create_date_picker('À Partir de:', self)
        (self.max_date_picker,
         max_date_label) = create_date_picker('Até:', self)
        self.min_date_picker.dateChanged.connect(
            self.max_date_picker.setMinimumDate)
        colecao_radio_button.toggled.connect(self.min_date_picker.setDisabled)
        colecao_radio_button.toggled.connect(self.max_date_picker.setDisabled)
        self.map_users_button = QPushButton(
            'Ver mapa de usuarios BGG -> Ludopedia', self)
        self.map_users_button.setEnabled(False)
        self.map_users_button.clicked.connect(self.user_map)
        colecao_radio_button.toggled.connect(self.map_users_button.setDisabled)
        group_box = QGroupBox('Dados')
        grid_layout = QGridLayout(group_box)
        grid_layout.addWidget(colecao_radio_button, 1, 1)
        grid_layout.addWidget(self.partidas_radio_button, 1, 2)
        grid_layout.addWidget(min_date_label, 2, 1)
        grid_layout.addWidget(self.min_date_picker, 2, 2)
        grid_layout.addWidget(max_date_label, 3, 1)
        grid_layout.addWidget(self.max_date_picker, 3, 2)
        grid_layout.addWidget(self.map_users_button, 4, 1, 1, 2)
        group_box.setLayout(grid_layout)
        return group_box

    def enable_import(self):
        """Slot to toggle state of the import button"""
        self.import_button.setDisabled(not self.bgg_user_line_edit.text()
                                       or not self.ludo_mail_line_edit.text()
                                       or not self.ludo_pass_line_edit.text())

    def log_text(self, message_type, text):
        """Logs the given text to the QPlainTextWidget"""
        current_time = QTime.currentTime().toString()
        if message_type == MessageType.ERROR:
            self.log_widget.insertHtml(
                f'[{current_time}] {ERROR_HTML}{text}<br>')
        elif message_type == MessageType.GENERIC:
            self.log_widget.insertHtml(f'[{current_time}] {text}<br>')
        elif message_type == MessageType.DEBUG and ENABLE_DEBUG:
            self.log_widget.insertHtml(
                f'[{current_time}] {DEBUG_HTML}{text}<br>')

        self.log_widget.moveCursor(QTextCursor.End)
        if ENABLE_DEBUG:
            print(text)

    def disconnect_thread(self):
        """Disconnect the started signal from the thread"""
        self.thread.started.disconnect()

    def configure_thread(self, worker):
        """Does basic thread startup and cleanup configuration"""
        worker.finished.connect(self.thread.quit)
        worker.moveToThread(self.thread)
        self.thread.started.connect(worker.run)
        worker.message.connect(self.log_text)
        worker.finished.connect(self.disconnect_thread)
        worker.exit_on_error.connect(self.thread.quit)
        worker.exit_on_error.connect(lambda: self.enable_editables.emit(True))

    def load_data(self):
        """Load data from bgg"""
        try:
            (session, ludo_user_id) = self.login_ludopedia()
            bgg_user = self.bgg_user_line_edit.text()

            if self.partidas_radio_button.isChecked():
                current_date = format_qdate(QDate.currentDate())
                min_date = parse_date(
                    format_qdate(self.min_date_picker.date()), current_date)
                max_date = parse_date(
                    format_qdate(self.max_date_picker.date()), min_date)
                self.worker = BGGPlayFetcher(bgg_user, min_date, max_date)
                self.configure_thread(self.worker)
                self.worker.finished.connect(lambda plays: self.post_plays(
                    session, plays, bgg_user, ludo_user_id))
            else:
                self.worker = BGGColectionFetcher(bgg_user)
                self.configure_thread(self.worker)
                self.worker.finished.connect(
                    lambda bgg_collection: self.import_collection(
                        session, bgg_collection))
            self.thread.start()
        except InputError:
            self.enable_editables.emit(True)

    def show_play_table(self, plays):
        """Shows a table with all the plays to be imported, allowing user to select some to skip"""
        tree_model = PlayTableModel(plays)
        table_widget = QTableView()
        table_widget.setModel(tree_model)
        table_widget.verticalHeader().setVisible(False)
        table_view_header = table_widget.horizontalHeader()
        table_view_header.setStretchLastSection(True)
        for column in range(tree_model.columnCount()):
            column_size = tree_model.data(tree_model.index(0, column),
                                          PlayTableModel.SIZE_ROLE)
            table_view_header.resizeSection(column, column_size)
        table_widget_dialog = QDialog(self)
        table_widget_dialog.setModal(True)
        grid_layout = QGridLayout(table_widget_dialog)
        grid_layout.addWidget(table_widget, 1, 1)
        table_widget_dialog.resize(800, 600)
        table_widget_dialog.exec_()
        skipped_plays = tree_model.get_skipped_plays()
        return [play for play in plays if play.id not in skipped_plays]

    def post_plays(self, session, plays, bgg_user, ludo_user_id):
        """Receives plays from the Play Fetched thread and start the Ludopedia Logger"""
        user_map = self.get_bgg_to_ludo_users()
        if bgg_user not in user_map:
            user_map[bgg_user] = ludo_user_id

        selected_plays = self.show_play_table(plays)

        self.worker = LudopediaPlayLogger(session, selected_plays, bgg_user,
                                          user_map)
        self.worker.request_search.connect(
            self.request_search_and_show_alternatives,
            Qt.BlockingQueuedConnection)
        self.worker.request_alternative.connect(self.request_alternative,
                                                Qt.BlockingQueuedConnection)
        self.alternative_chosen.connect(self.worker.receive_alternative,
                                        Qt.DirectConnection)
        self.configure_thread(self.worker)
        self.worker.finished.connect(lambda: self.enable_editables.emit(True))
        self.thread.start()

    def user_map(self):
        """Slot to show user map from bgg to ludopedia"""
        user_map_dialog = QDialog(self)
        user_map_dialog.setModal(True)
        bgg_to_ludo = self.get_bgg_to_ludo_users()
        user_list = [f'{key} -> {value}' for key, value in bgg_to_ludo.items()]
        list_widget = QListWidget(user_map_dialog)
        list_widget.addItems(user_list)
        list_widget.setResizeMode(QListView.Adjust)
        list_widget.sortItems()
        grid_layout = QGridLayout(user_map_dialog)
        grid_layout.addWidget(list_widget, 1, 1)
        user_map_dialog.resize(400, 400)
        user_map_dialog.show()

    def login_ludopedia(self):
        """Logins into Ludopedia manually and returns the session and user_id"""
        self.log_text(MessageType.GENERIC, 'Obtendo dados do Ludopedia')
        payload = {
            'email': self.ludo_mail_line_edit.text(),
            'pass': self.ludo_pass_line_edit.text()
        }

        session = requests.Session()
        session_request = session.post(LUDOPEDIA_LOGIN_URL, data=payload)

        if 'senha incorretos' in session_request.text:
            self.log_text(
                MessageType.ERROR,
                'Não foi possível logar na Ludopedia com as informações fornecidas'
            )
            raise InputError

        user_re = re.search(r'id_usuario=(\d+)', session_request.text)
        user_id = user_re.group(1) if user_re else None

        return (session, user_id)

    def import_collection(self, session, collection):
        """Imports a given collection into Ludopedia"""
        self.worker = LudopediaCollectionLogger(session, collection)
        self.configure_thread(self.worker)
        self.worker.finished.connect(lambda: self.enable_editables.emit(True))
        self.thread.start()

    def show_alternatives_dialog(self, bgg_play, data):
        """Show alternative games to use as the game to log a play"""
        alternatives_dialog = QInputDialog(self)
        alternatives_list = [
            f'{item["nm_jogo"]} ({item["ano_publicacao"]})' for item in data
        ]
        alternatives_dialog.setComboBoxItems(alternatives_list)
        alternatives_dialog.setOption(QInputDialog.UseListViewForComboBoxItems)
        game_str = f'{bgg_play.game_name} ({bgg_play.year_published})'
        alternatives_dialog.setLabelText(
            f'Escolha uma alternativa para o jogo "{game_str}"')
        if alternatives_dialog.exec_():
            selected_index = alternatives_list.index(
                alternatives_dialog.textValue())
            return data[selected_index]
        return None

    def request_search_and_show_alternatives(self, session, bgg_play):
        """Request a new string to use for game search and then show results to be picked"""
        new_search_dialog = QInputDialog(self)
        game_str = f'{bgg_play.game_name} ({bgg_play.year_published})'
        new_search_dialog.setLabelText(
            f'Jogo "{game_str}" não encontrado\nBuscar por:')
        new_search_dialog.setInputMode(QInputDialog.TextInput)
        if new_search_dialog.exec_():
            data = search_ludopedia_games(session,
                                          new_search_dialog.textValue())
            data = self.show_alternatives_dialog(bgg_play, data)
            self.alternative_chosen.emit(data)

    def request_alternative(self, bgg_play, data):
        """Request an alternative from user and emit choice"""
        alternative = self.show_alternatives_dialog(bgg_play, data)
        self.alternative_chosen.emit(alternative)

    def get_bgg_to_ludo_users(self):
        """Reads usuarios.txt file to map a bgg user to its corresponding ludopedia one"""
        try:
            parser = ConfigParser()
            with open("usuarios.txt") as lines:
                lines = chain(("[top]", ), lines)
                parser.read_file(lines)
                bgg_to_ludo_user = dict(parser['top'])
                bgg_to_ludo_user_id = dict()
                for bgg_user, ludo_user in bgg_to_ludo_user.items():
                    if is_invalid_bgg_user(bgg_user):
                        self.log_text(
                            MessageType.ERROR,
                            f'Usuário do BGG "{bgg_user}" inválido'
                            f' no mapa de usuários')
                        continue

                    if ludo_user.isdigit():
                        bgg_to_ludo_user_id[bgg_user] = ludo_user
                        self.log_text(
                            MessageType.DEBUG,
                            f'Usuário do BGG "{bgg_user}" já mapeado'
                            f' ao id ludopedia: {ludo_user}')
                    else:
                        ludo_user_id = get_ludo_user_id(ludo_user)
                        if ludo_user_id:
                            self.log_text(MessageType.DEBUG,
                                          f'{ludo_user_id} para {ludo_user}')
                            bgg_to_ludo_user_id[bgg_user] = ludo_user_id
                        else:
                            self.log_text(
                                MessageType.ERROR,
                                f'Falha ao buscar id de usuario da'
                                f' ludopedia para "{ludo_user}"')
                return bgg_to_ludo_user_id
        except FileNotFoundError:
            self.log_error(
                MessageType.ERROR,
                'Não foi possível encontrar o arquivo "usuarios.txt')
            return {}
Ejemplo n.º 22
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.adf = ""
        self.bbdf = ""
        self.andf = ""
        self.ofile = ""
        self.ofileadd = ""
        self.ofilerem = ""

        self.aboutAction = QAction('&关于', self)
        self.aboutText = """程序版本:1.2\[email protected]\nAll rights reserved\n2021-01-20"""
        self.aboutAction.triggered.connect(
            lambda: self.showDialog(self.aboutText))

        # 添加事件
        self.ui.checkBox.stateChanged.connect(self.updateState)
        self.ui.pushButton_jwc.clicked.connect(lambda: self.chooseFile("all"))
        self.ui.pushButton_bb.clicked.connect(lambda: self.chooseFile("bb"))
        self.ui.pushButton_jwc_new.clicked.connect(
            lambda: self.chooseFile("all-new"))
        self.ui.pushButton_start.clicked.connect(self.saveFile)
        self.ui.pushButton_start.clicked.connect(self.genData)

        self.ui.menuBar.addAction(self.aboutAction)

        # 初始化日志窗口
        self.logger(u"程序已就绪,请选择文件进行处理。")
        self.logger(textHelp)

        self.updateSb(u"请选择文件以运行程序")

    def logger(self, c):
        return self.ui.textBrowser.append(c)

    def updateSb(self, c):
        return self.ui.statusbar.showMessage(c)

    def showDialog(self, text):
        msgbox = QMessageBox()
        msgbox.setWindowTitle(u'关于')
        msgbox.setText(text)
        msgbox.exec_()

    def updateState(self):
        if self.ui.checkBox.isChecked():
            self.ui.lineEdit_jwc_new.setEnabled(True)
            self.ui.pushButton_jwc_new.setEnabled(True)
            self.ui.pushButton_start.clicked.disconnect(self.genData)
            self.ui.pushButton_start.clicked.connect(self.genComp)
        else:
            self.andf = ""
            self.ui.lineEdit_jwc_new.setText("")
            self.ui.lineEdit_jwc_new.setEnabled(False)
            self.ui.pushButton_jwc_new.setEnabled(False)
            self.ui.pushButton_start.clicked.disconnect(self.genComp)
            self.ui.pushButton_start.clicked.connect(self.genData)
        self.updateButton()

    def chooseFile(self, bname):
        fileName, _ = QFileDialog.getOpenFileName(self, "选择xlsx文件", "",
                                                  "Excel文件 (*.xlsx)")

        if bname == "all":
            if fileName:
                self.adf = fileName
                self.ui.lineEdit_jwc.setText(fileName)
            else:
                self.adf = ""
                self.ui.lineEdit_jwc.setText("")
            self.updateButton()
            return
        if bname == "bb":
            if fileName:
                self.bbdf = fileName
                self.ui.lineEdit_bb.setText(fileName)
            else:
                self.bbdf = ""
                self.ui.lineEdit_bb.setText("")
            self.updateButton()
            return
        if bname == "all-new":
            if fileName:
                self.andf = fileName
                self.ui.lineEdit_jwc_new.setText(fileName)
            else:
                self.andf = ""
                self.ui.lineEdit_jwc_new.setText("")
            self.updateButton()
            return

    def saveFile(self):
        fileName, _ = QFileDialog.getSaveFileName(self, "选择输出文件名", "",
                                                  "CVS文件 (*.csv)")
        if fileName:
            self.ofile = fileName
            if self.ui.checkBox.isChecked():
                (pname, ext) = os.path.splitext(fileName)
                self.ofileadd = pname + "-新增名单" + ext
                self.ofilerem = pname + "-删减名单" + ext
                self.logger(u"新增学生名单将保存为:" + self.ofileadd)
                self.logger(u"删减学生名单将保存为:" + self.ofilerem)
            else:
                self.logger(u"注册名单将保存为:" + fileName)
        else:
            self.ofile = ""
            self.logger("用户取消操作")

    def updateButton(self):
        if self.adf and self.bbdf and not (bool(self.ui.checkBox.isChecked())
                                           != bool(self.andf)):
            self.ui.pushButton_start.setEnabled(True)
        else:
            self.ui.pushButton_start.setEnabled(False)

    def writeFile(self, content, path):
        try:
            content.to_csv(path,
                           index=False,
                           header=False,
                           encoding='utf_8_sig')
        except PermissionError:
            self.logger("文件写入失败,请检查是否已经打开了同目录下的同名文件!")

    @Slot()
    def genData(self):

        if self.ofile == "":
            return

        self.thread = QThread()
        self.worker = Worker(self.adf, self.bbdf)
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.updateSb(u"正在处理数据")
        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.worker.deleteLater)
        self.worker.finished.connect(lambda: self.updateSb(u"处理完成"))
        self.worker.finished.connect(
            lambda: self.logger(u"数据处理完成,请检查生成的文件。\n"))
        self.thread.finished.connect(self.thread.deleteLater)
        self.worker.progress.connect(lambda p: self.ui.progressBar.setValue(p))
        self.worker.output.connect(lambda c: self.logger(c))

        self.worker.content.connect(
            lambda content: self.writeFile(content, self.ofile))

        self.thread.start()

        self.ui.pushButton_start.setEnabled(False)
        self.thread.finished.connect(
            lambda: self.ui.pushButton_start.setEnabled(True))

    @Slot()
    def genComp(self):

        if self.ofile == "":
            return

        self.file1 = pd.DataFrame()
        self.file2 = pd.DataFrame()

        def updateFile(f, id):
            if id == 1:
                self.file1 = f
            if id == 2:
                self.file2 = f

        self.p = 0
        self.lock1 = False
        self.lock2 = False

        def checkLock(i):
            if i == 1:
                self.lock1 = True
            if i == 2:
                self.lock2 = True

            if self.lock1 and self.lock2:
                self.updateSb(u"正在处理数据")
                self.th3 = QThread()
                self.comp = Comparer(self.file1, self.file2)
                self.comp.moveToThread(self.th3)
                self.th3.started.connect(self.comp.run)
                self.comp.finished.connect(self.th3.quit)
                self.comp.finished.connect(self.comp.deleteLater)
                self.th3.finished.connect(self.th3.deleteLater)
                self.comp.progress.connect(lambda p: up(p))
                self.comp.output.connect(lambda c: self.logger(c))
                self.comp.content1.connect(
                    lambda content: self.writeFile(content, self.ofileadd))
                self.comp.content2.connect(
                    lambda content: self.writeFile(content, self.ofilerem))
                self.th3.finished.connect(
                    lambda: self.ui.progressBar.setValue(100))
                self.comp.finished.connect(
                    lambda: self.logger(u"数据处理完成,请检查生成的文件。\n"))
                self.comp.finished.connect(lambda: self.updateSb(u"处理完成"))
                self.th3.finished.connect(
                    lambda: self.ui.pushButton_start.setEnabled(True))
                self.th3.start()

        def up(wp):
            self.p += 0.4 * wp
            self.ui.progressBar.setValue(self.p)

        self.th1 = QThread()
        self.th2 = QThread()

        self.worker1 = Worker(self.adf, self.bbdf)
        self.worker2 = Worker(self.andf, self.bbdf)

        self.worker1.moveToThread(self.th1)
        self.worker2.moveToThread(self.th2)

        self.th1.started.connect(self.worker1.run)
        self.worker1.content.connect(lambda f: updateFile(f, 1))
        self.worker1.finished.connect(self.th1.quit)
        self.worker1.finished.connect(self.worker1.deleteLater)
        self.worker1.finished.connect(lambda: checkLock(1))
        self.th1.finished.connect(self.th1.deleteLater)

        self.th2.started.connect(self.worker2.run)
        self.worker2.content.connect(lambda f: updateFile(f, 2))
        self.worker2.finished.connect(self.th2.quit)
        self.worker2.finished.connect(self.worker2.deleteLater)
        self.worker2.finished.connect(lambda: checkLock(2))
        self.th2.finished.connect(self.th2.deleteLater)

        self.worker1.progress.connect(lambda p: up(p))
        self.worker2.progress.connect(lambda p: up(p))

        # self.worker1.output.connect(lambda c: self.logger(c))
        # self.worker2.output.connect(lambda c: self.logger(c))

        self.th1.start()
        self.th2.start()

        self.ui.pushButton_start.setEnabled(False)