Ejemplo n.º 1
0
class NPGPull(ZMQPull):

    def __init__(self, host, port, imgModel, table, opts, flags):
        ZMQPull.__init__(self, host, port, opts=[], flags=flags)
        self.socketTimer = QTimer()
        self.socketTimer.timeout.connect(self.receive)
        self.imgModel = imgModel
        self.table = table

    def start(self):
        self.connect()
        self.socketTimer.start(100)

    def stop(self):
        self.close()
        self.socketTimer.stop()

    def receive(self):

        try:
            data = self.puller.recv_json(flags=zmq.NOBLOCK)
            fn = str(data['fn'].strip())
            path = str(data['path'])
            index = int(data['index'])
            total = int(data['total'])
            N = int(data['processed'])
            hit = int(data['hit'])
            self.imgModel.updateData(fn, path, int(index))
            self.table.progress((total, N, hit))
            return
        except zmq.error.Again:
            return
Ejemplo n.º 2
0
class Main(APIS):

    sequence = 2    # 插件加载顺序(重要,顺序不同可能导致界面混乱)
    name = ProgressUi.__name__    # 模块名
    author = __Author__    # 作者
    version = __Version__    # 版本
    description = "自定义标题栏"    # 描述

    def __init__(self, parent = None):
        super(Main, self).__init__(parent)
        self.progressui = ProgressUi(self.parent)
        self.progressui.setVisible(False)    # 默认不可见
        self.setStyleSheet(self.progressui, self.name)

        # 测试加载进度
        self.i = 0
        self.progressui.setVisible(True)    # 测试改为可见
        self.timer = QTimer(timeout = self.onTimeout)
        self.timer.start(50)

    def onTimeout(self):
        self.i += 1
        self.progressui.setValue(self.i)
        if self.i > 100:
            self.timer.stop()
            self.progressui.setValue(0)
            self.progressui.setVisible(False)

    def run(self):
        self.parent.vLayout.insertWidget(self.parent.vLayout.count() - 1, self.progressui)

    def stop(self):
        self.timer.stop()
        self.progressui.close()
        self.parent.vLayout.removeWidget(self.progressui)
Ejemplo n.º 3
0
class GenericTerminalOutputBox(QtWidgets.QLineEdit):
    def __init__(self) -> None:
        super().__init__()
        self.animate = False
        self.timer = QTimer(self)
        self.timer.setInterval(5)
        self.timer.timeout.connect(self._add_character)
        self.buffer = ''

    def set_timer_interval(self, num: int) -> None:
        self.timer.setInterval(num)

    def _add_character(self) -> None:
        if not self.buffer:
            self.timer.stop()
            return
        super().setText(self.text() + self.buffer[0])
        self.buffer = self.buffer[1:]

    def setText(self, text: str) -> None:
        if not self.animate or not text:
            super().setText(text)
            return
        super().setText(text[0])
        if len(text) > 1:
            self.buffer = text[1:]
            self.timer.start()
Ejemplo n.º 4
0
Archivo: base.py Proyecto: gpa14/enki
def _processPendingEvents():
    """Process pending application events."""

    # Create an event loop to run in. Otherwise, we need to use the
    # QApplication main loop, which may already be running and therefore
    # unusable.
    qe = QEventLoop()

    # Create a single-shot timer. Could use QTimer.singleShot(),
    # but can't cancel this / disconnect it.
    timer = QTimer()
    timer.setSingleShot(True)
    timer.timeout.connect(qe.quit)
    timer.start(1)

    # Wait for an emitted signal.
    qe.exec_()

    # Clean up: don't allow the timer to call qe.quit after this
    # function exits, which would produce "interesting" behavior.
    timer.stop()
    # Stopping the timer may not cancel timeout signals in the
    # event queue. Disconnect the signal to be sure that loop
    # will never receive a timeout after the function exits.
    timer.timeout.disconnect(qe.quit)
Ejemplo n.º 5
0
class _StatusBar(QStatusBar):
    """Extended status bar. Supports HTML messages
    """

    def __init__(self, *args):
        QStatusBar.__init__(self, *args)
        self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Expanding)
        self.setSizeGripEnabled(False)
        self.setStyleSheet("QStatusBar {border: 0} QStatusBar::item {border: 0}")
        self._label = QLabel(self)
        self._label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self._label.setStyleSheet("color: red")
        self.addWidget(self._label)
        self._timer = QTimer()
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self.clearMessage)

    def term(self):
        self._timer.stop()

    def showMessage(self, text, timeout=0):
        """QStatusBar.showMessage()
        """
        self._label.setText(text)
        self._timer.stop()
        if timeout > 0:
            self._timer.start(timeout)

    def clearMessage(self):
        """QStatusBar.clearMessage()
        """
        self._label.clear()

    def currentMessage(self):
        return self._label.text()
Ejemplo n.º 6
0
Archivo: gost.py Proyecto: fheeger/gost
class QtWaitForSelection(QMessageBox):
    """Window to wait for the user to select an object"""
    def __init__(self, parent=None):
        super(QtWaitForSelection, self).__init__(QMessageBox.Question, "Warte auf Auswahl",
                                                 "Bitte wählen sie ein Object im 3d view  aus",
                                                 buttons=QMessageBox.Cancel,
                                                 parent=parent)
        self.move(0,0)
        #create a timer
        self.timer = QTimer(self)
        self.timer.setInterval(50)
        #connect the timer to checking is anything was selected
        self.timer.timeout.connect(self.pollSelection)
        self.timer.start()
        self.selected = None

    def pollSelection(self):
        """Check if anything was selected"""
        try:
            #get the selected object
            nowSelected = getSelectedObject()
        except ValueError:
            QMessageBox.critical(self, "Mehrer Objekt Ausgewählt", 
                                 "Es darf nicht mehr als ein Objekt ausgewählt sein.")
            for obj in bpy.data.objects:
                obj.select = False
        else:
            if not nowSelected is None:
                #stotre the selected object
                self.selected = nowSelected
                #stop the timer and close the window
                self.timer.stop()
                self.accept()
Ejemplo n.º 7
0
class ConnectionHandler(QObject):

    value_changed = pyqtSignal(bool)

    def __init__(self, frequency_check, time_before_time_out, parent=None):
        super(ConnectionHandler, self).__init__(parent)
        self.frequency_check = frequency_check
        self.time_before_time_out = time_before_time_out
        self.internet_ok = None
        self.timer = QTimer(self)

    def start(self):
        self.timer.setInterval(self.frequency_check)
        self.timer.timeout.connect(self.update)
        self.timer.start()

    def stop(self):
        self.timer.stop()

    def emit_if_changed(self, new_value):
        if not self.internet_ok is new_value:
            self.internet_ok = new_value
            self.value_changed.emit(new_value)

    def check_internet_call(self):
        urllib2.urlopen('http://www.demerio.com', timeout=self.time_before_time_out)

    @pyqtSlot()
    def update(self):
        try:
            self.check_internet_call()
        except Exception as e:
            self.emit_if_changed(False)
        else:
            self.emit_if_changed(True)
Ejemplo n.º 8
0
class Runner(object):
    """Useful class for running jobs with a delay"""

    def __init__(self, delay=2000):
        self._timer = QTimer()
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self._execute_job)
        self._delay = delay

        self._job = None
        self._args = []
        self._kw = {}

    def cancel(self):
        """Cancel the current job"""
        self._timer.stop()
        self._job = None
        self._args = []
        self._kw = {}

    def run(self, job, *args, **kw):
        """Request a job run. If there is a job, cancel and run the new job
        with a delay specified in __init__"""
        self.cancel()
        self._job = job
        self._args = args
        self._kw = kw
        self._timer.start(self._delay)

    def _execute_job(self):
        """Execute job after the timer has timeout"""
        self._timer.stop()
        self._job(*self._args, **self._kw)
Ejemplo n.º 9
0
def run_gui_tests(tstcls, gui_test_bag):
    assert tstcls.app is None, "Should only encounter every class once. Check Sorting"
    tst_queue = queue.Queue()
    app = tstcls.app = QApplication([])
    app.setQuitOnLastWindowClosed(False)
    ThreadRouter.app_is_shutting_down = False
    app.thread_router = ThreadRouter(app)
    tstcls.shell = launchShell(None, [], [])

    platform_str = platform.platform().lower()
    if 'ubuntu' in platform_str or 'fedora' in platform_str:
        QApplication.setAttribute(Qt.AA_X11InitThreads, True)

    if ilastik.config.cfg.getboolean("ilastik", "debug"):
        QApplication.setAttribute(Qt.AA_DontUseNativeMenuBar, True)

    # Note on the class test execution lifecycle
    # pytest infers that finalizer teardown_class should be called when
    # nextitem is None
    for item, nextitem in pairwise(gui_test_bag, tail=None):
        tst_queue.put((item, nextitem))

    # Spawn a suite runner as a interval task
    suite = GuiTestSuite(tst_queue, tstcls.shell)
    timer = QTimer()
    # This timer will fire only after application is started running
    timer.timeout.connect(suite.poll)
    timer.start(100)  # Every 100 ms

    app.exec_()
    timer.stop()

    tst_queue.join()
class DeviceReader(QThread):
    """Used for polling data from the Input layer during configuration"""
    raw_axis_data_signal = pyqtSignal(object)
    raw_button_data_signal = pyqtSignal(object)
    mapped_values_signal = pyqtSignal(object)

    def __init__(self, input):
        QThread.__init__(self)

        self._input = input
        self._read_timer = QTimer()
        self._read_timer.setInterval(25)

        self._read_timer.timeout.connect(self._read_input)

    def stop_reading(self):
        """Stop polling data"""
        self._read_timer.stop()

    def start_reading(self):
        """Start polling data"""
        self._read_timer.start()

    def _read_input(self):
        [rawaxis, rawbuttons, mapped_values] = self._input.read_raw_values()
        self.raw_axis_data_signal.emit(rawaxis)
        self.raw_button_data_signal.emit(rawbuttons)
        self.mapped_values_signal.emit(mapped_values)
Ejemplo n.º 11
0
class TimerObject(object):
    def __init__(self, interval, callback, times):
        self.times = times
        self.callback = callback
        self._timer = QTimer()
        # logger.info("register timer %s" % self)
        self._timer.timeout.connect(self.expired)
        self._timer.start(interval * 1000)

    def expired(self):
        # logger.info("callback from %s" % self)
        self.callback()
        self.times -= 1
        if self._timer is None:
            # logger.info("dead timer %s called, ignore" % self)
            return
        if self.times == 0:
            self._timer.stop()
            # logger.info("delete timer %s" % self)
            self._timer = None

    def cancel(self):
        # logger.info("cancel timer %s" % self)
        if self._timer:
            self._timer.stop()
            self._timer = None
Ejemplo n.º 12
0
def askPasswordDialog(parent, title, prompt, timeout = None):
    if parent is None:
        app = qttools.createQApplication()
        translator = qttools.translator()
        app.installTranslator(translator)

    import icon
    dialog = QInputDialog()

    timer = QTimer()
    if not timeout is None:
        timer.timeout.connect(dialog.reject)
        timer.setInterval(timeout * 1000)
        timer.start()

    dialog.setWindowIcon(icon.BIT_LOGO)
    dialog.setWindowTitle(title)
    dialog.setLabelText(prompt)
    dialog.setTextEchoMode(QLineEdit.Password)
    QApplication.processEvents()

    ret = dialog.exec_()

    timer.stop()
    if ret:
        password = dialog.textValue()
    else:
        password = ''
    del(dialog)

    return(password)
Ejemplo n.º 13
0
class _GlobalUpdateWordSetTimer:
    """Timer updates word set, when editor is idle. (5 sec. after last change)
    Timer is global, for avoid situation, when all instances
    update set simultaneously
    """
    _IDLE_TIMEOUT_MS = 1000

    def __init__(self):
        self._timer = QTimer()
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self._onTimer)
        self._scheduledMethods = []

    def schedule(self, method):
        if not method in self._scheduledMethods:
            self._scheduledMethods.append(method)
        self._timer.start(self._IDLE_TIMEOUT_MS)

    def cancel(self, method):
        """Cancel scheduled method
        Safe method, may be called with not-scheduled method"""
        if method in self._scheduledMethods:
            self._scheduledMethods.remove(method)

        if not self._scheduledMethods:
            self._timer.stop()

    def _onTimer(self):
        method = self._scheduledMethods.pop()
        method()
        if self._scheduledMethods:
            self._timer.start(self._IDLE_TIMEOUT_MS)
Ejemplo n.º 14
0
class Clipboard(QObject):
    changed = pyqtSignal(dict)

    def __init__(self):
        QObject.__init__(self)
        clipboard = QGuiApplication.clipboard()
        clipboard.changed.connect(self.onClipboardChanged)

        self.clipboardTimer = QTimer()
        self.clipboardTimer.setInterval(500)
        self.clipboardTimer.setSingleShot(True)
        self.clipboardTimer.timeout.connect(self.onClipboardChangedAfterDelay)

        self.selectionTimer = QTimer()
        self.selectionTimer.setInterval(1000)
        self.selectionTimer.setSingleShot(True)
        self.selectionTimer.timeout.connect(self.onSelectionChangedAfterDelay)

        self.formats = set([mimeText])

    def setFormats(self, formats):
        self.formats = set(formats)

    def onClipboardChanged(self, mode):
        if mode == QClipboard.Clipboard:
            if not QGuiApplication.clipboard().ownsClipboard():
                self.clipboardTimer.start()
            else:
                self.clipboardTimer.stop()
        elif mode == QClipboard.Selection:
            if not QGuiApplication.clipboard().ownsSelection():
                self.selectionTimer.start()
            else:
                self.selectionTimer.stop()

    def onClipboardChangedAfterDelay(self):
        self.emitChanged(QClipboard.Selection)

    def onSelectionChangedAfterDelay(self):
        self.emitChanged(QClipboard.Selection)

    def emitChanged(self, mode):
        clipboard = QGuiApplication.clipboard()
        mimeData = clipboard.mimeData()
        data = {}
        for format in self.formats:
            if mimeData.hasFormat(format):
                data[format] = mimeData.data(format)
        self.changed.emit(data)

    @pyqtProperty(str)
    def text(self):
        clipboard = QGuiApplication.clipboard()
        return clipboard.text()

    @text.setter
    def text(self, text):
        clipboard = QGuiApplication.clipboard()
        return clipboard.setText(text)
Ejemplo n.º 15
0
class Alarm(QWidget):
    def __init__(self):
        super(Alarm, self).__init__()
        self.ui = Ui_Form()
        self.ui.setupUi(self)
        self.ui.lineEdit.textChanged.connect(self.onTextChanged)
        self.ui.timeEdit.timeChanged.connect(self.onTimeChanged)
        self.ui.groupBox.toggled.connect(self.onGBToggle)
        self.ui.toolButton.clicked.connect(self.onToolButton)
        
        self.go = True
        self.ui.timeEdit.setTime(QTime.currentTime().addSecs(60))
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.__alarm)
        self.timer.start(Alarm.__get_diff(self.ui.timeEdit.time()))
        
        self.music = "alarm.mp3"
        self.ui.track_label.setText("DEFAULT")
        
    def __get_diff(time):
        return QTime.currentTime().msecsTo(time)
        
    def onTextChanged(self, text):
        self.ui.groupBox.setTitle(text)
        
    def onTimeChanged(self, time):
        c_time = QTime.currentTime()
        if (c_time > time):
            self.ui.timeEdit.setTime(c_time)
            return
        diff = Alarm.__get_diff(self.ui.timeEdit.time().addSecs(60))
        print("TIME CHANGED", time, diff)
        self.timer.timer.start(diff + (60*1000))
        
    def onGBToggle(self, checked):
        print("ALARM ON IS", checked)
        self.go = checked
        
    def __alarm(self):
        if not self.go:
            return;
        self.timer.stop()
        player = QMediaPlayer(self);
        player.setMedia(QMediaContent(QUrl.fromLocalFile(self.music)));
        player.setVolume(100);
        player.play();
        self.setEnabled(False)
        QMessageBox.critical(self, "ALERTA", "TIME TO DIE<br>" + self.ui.groupBox.title(), QMessageBox.Yes)
        self.setEnabled(True)
        player.stop()
        player.deleteLater()
        
    def onToolButton(self):
        self.music = QFileDialog.getOpenFileName(self, "Open sound", "", "Sound (*.mp3 *.flac *.ogg)")[0]
        self.ui.track_label.setText(QFileInfo(self.music).baseName())
        print(self.music)

        
Ejemplo n.º 16
0
class MessageView(QWidget):

    """Widget which stacks error/warning/info messages."""

    update_geometry = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self._vbox = QVBoxLayout(self)
        self._vbox.setContentsMargins(0, 0, 0, 0)
        self._vbox.setSpacing(0)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

        self._clear_timer = QTimer()
        self._clear_timer.timeout.connect(self._clear_messages)
        self._set_clear_timer_interval()
        objreg.get('config').changed.connect(self._set_clear_timer_interval)

        self._last_text = None
        self._messages = []

    def sizeHint(self):
        """Get the proposed height for the view."""
        height = sum(label.sizeHint().height() for label in self._messages)
        # The width isn't really relevant as we're expanding anyways.
        return QSize(-1, height)

    @config.change_filter('ui', 'message-timeout')
    def _set_clear_timer_interval(self):
        """Configure self._clear_timer according to the config."""
        self._clear_timer.setInterval(config.get('ui', 'message-timeout'))

    @pyqtSlot()
    def _clear_messages(self):
        """Hide and delete all messages."""
        for widget in self._messages:
            self._vbox.removeWidget(widget)
            widget.hide()
            widget.deleteLater()
        self._messages = []
        self._last_text = None
        self.hide()
        self._clear_timer.stop()

    @pyqtSlot(usertypes.MessageLevel, str)
    def show_message(self, level, text):
        """Show the given message with the given MessageLevel."""
        if text == self._last_text:
            return

        widget = Message(level, text, parent=self)
        self._vbox.addWidget(widget)
        widget.show()
        self._clear_timer.start()
        self._messages.append(widget)
        self._last_text = text
        self.show()
        self.update_geometry.emit()
Ejemplo n.º 17
0
class MusicPosition(plugin.ViewSpacePlugin):
    def __init__(self, space):
        self._timer = QTimer(singleShot=True, timeout=self.slotTimeout)
        self._waittimer = QTimer(singleShot=True, timeout=self.slotTimeout)
        self._label = QLabel()
        space.status.layout().insertWidget(1, self._label)
        self._view = lambda: None
        space.viewChanged.connect(self.slotViewChanged)
        view = space.activeView()
        if view:
            self.slotViewChanged(view)

    def slotViewChanged(self, view):
        old = self._view()
        if old:
            self.disconnectView(old)
        self._view = weakref.ref(view)
        self.connectView(view)
        self.startTimer()

    def connectView(self, view):
        view.cursorPositionChanged.connect(self.startTimer)
        view.document().contentsChanged.connect(self.startWaitTimer)

    def disconnectView(self, view):
        view.cursorPositionChanged.disconnect(self.startTimer)
        view.document().contentsChanged.disconnect(self.startWaitTimer)

    def startWaitTimer(self):
        """Called when the document changes, waits longer to prevent stutter."""
        self._waittimer.start(900)
        self._timer.stop()

    def startTimer(self):
        """Called when the cursor moves."""
        if not self._waittimer.isActive():
            self._timer.start(100)

    def slotTimeout(self):
        """Called when one of the timers fires."""
        view = self._view()
        if view:
            d = view.document()
            c = view.textCursor()
            import documentinfo
            m = documentinfo.music(d)
            import ly.duration
            if c.hasSelection():
                cursortools.strip_selection(c)
                length = m.time_length(c.selectionStart(), c.selectionEnd())
                text = _("Length: {length}").format(
                    length=ly.duration.format_fraction(length)) if length is not None else ''
            else:
                pos = m.time_position(c.position())
                text = _("Pos: {pos}").format(
                    pos=ly.duration.format_fraction(pos)) if pos is not None else ''
            self._label.setText(text)
            self._label.setVisible(bool(text))
class AnimatedClock(QGraphicsView):
    def __init__(self, parent=None):
        QGraphicsView.__init__(self, parent)
        self.updateSecs = 0.5
        # Border
        self.setLineWidth(0)
        self.setFrameShape(QtWidgets.QFrame.NoFrame)
        # Size
        sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        sizePolicy.setHeightForWidth(True)
        self.setSizePolicy(sizePolicy)
        # No scrollbars
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        # Scene
        self.scene = QGraphicsScene()
        self.setScene(self.scene)
        self.setBackgroundBrush(QColor("black"))
        # Text of clock
        self.textItem = QGraphicsTextItem()
        self.textItem.color = QColor(QColor("black"))
        self.textItem.setFont(QFont("Segoe UI", 80))
        self.textItem.setDefaultTextColor(QColor("white"))
        self.textItem.setHtml("")
        self.textItem.setZValue(20)
        self.scene.addItem(self.textItem)
        # Start ticking
        self.start()

    def sizeHint(self):
        return QSize(300, 150)

    def start(self):
        self.updateTimer = QTimer()
        self.updateTimer.setInterval(self.updateSecs * 990)
        self.updateTimer.timeout.connect(self.updateClock)
        self.updateTimer.start()
        print("Animated clock - starting")

    def stop(self):
        if self.updateTimer != None:
            self.updateTimer.stop()
        print("Animated clock - stopping")

    def updateClock(self):
        localtime = time.localtime()
        timeString = time.strftime("%H:%M:%S", localtime)
        self.textItem.setHtml(timeString)
        width = self.frameGeometry().width()
        self.textItem.setFont(QFont("Segoe UI", width / 8))
        self.textItem.update()

    def heightForWidth(self, width):
        return width * .32

    def keyPressEvent(self, event): #QKeyEvent
        event.ignore()
Ejemplo n.º 19
0
class GalleryDownloaderItem(QObject):
	"""
	Receives a HenItem
	"""
	d_item_ready = pyqtSignal(object)
	def __init__(self, hitem):
		super().__init__()
		assert isinstance(hitem, pewnet.HenItem)
		self.d_item_ready.connect(self.done)
		self.item = hitem
		url = self.item.gallery_url

		self.profile_item = QTableWidgetItem(self.item.name)
		self.profile_item.setData(Qt.UserRole+1, hitem)
		self.profile_item.setToolTip(url)
		def set_profile(item):
			self.profile_item.setIcon(QIcon(item.thumb))
		self.item.thumb_rdy.connect(set_profile)

		# status
		self.status_item = QTableWidgetItem('In queue...')
		self.status_item.setToolTip(url)
		def set_finished(item):
			self.status_item.setText('Finished!')
			self.d_item_ready.emit(self)
		self.item.file_rdy.connect(set_finished)

		# other
		self.cost_item = QTableWidgetItem(self.item.cost)
		self.cost_item.setToolTip(url)
		self.size_item = QTableWidgetItem(self.item.size)
		self.size_item.setToolTip(url)
		type = 'Archive' if hitem.download_type == 0 else 'Torrent'
		self.type_item = QTableWidgetItem(type)
		self.type_item.setToolTip(url)

		self.status_timer = QTimer()
		self.status_timer.timeout.connect(self.check_progress)
		self.status_timer.start(500)

	def check_progress(self):
		if self.item.current_state == self.item.DOWNLOADING:
			btomb = 1048576
			self.status_item.setText("{0:.2f}/{1:.2f} MB".format(self.item.current_size/btomb,
															  self.item.total_size/btomb))
			self.size_item.setText("{0:.2f} MB".format(self.item.total_size/btomb))
		elif self.item.current_state == self.item.CANCELLED:
			self.status_item.setText("Cancelled!")
			self.status_timer.stop()

	def done(self):
		self.status_timer.stop()
		if self.item.download_type == 0:
			self.status_item.setText("Sent to library!")
		else:
			self.status_item.setText("Sent to torrent client!")
Ejemplo n.º 20
0
class MessageView(QWidget):

    """Widget which stacks error/warning/info messages."""

    reposition = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self._vbox = QVBoxLayout(self)
        self._vbox.setContentsMargins(0, 0, 0, 0)
        self._vbox.setSpacing(0)

        self._clear_timer = QTimer()
        self._clear_timer.timeout.connect(self._clear_messages)
        self._set_clear_timer_interval()
        objreg.get('config').changed.connect(self._set_clear_timer_interval)

        self._last_text = None
        self._messages = []

    @config.change_filter('ui', 'message-timeout')
    def _set_clear_timer_interval(self):
        """Configure self._clear_timer according to the config."""
        self._clear_timer.setInterval(config.get('ui', 'message-timeout'))

    def message_height(self):
        """Get the total height of all messages."""
        return sum(label.sizeHint().height() for label in self._messages)

    @pyqtSlot()
    def _clear_messages(self):
        """Hide and delete all messages."""
        for widget in self._messages:
            self._vbox.removeWidget(widget)
            widget.hide()
            widget.deleteLater()
        self._messages = []
        self._last_text = None
        self.hide()
        self._clear_timer.stop()

    @pyqtSlot(usertypes.MessageLevel, str)
    def show_message(self, level, text):
        """Show the given message with the given MessageLevel."""
        if text == self._last_text:
            return

        widget = Message(level, text, parent=self)
        self._vbox.addWidget(widget)
        widget.show()
        self._clear_timer.start()
        self._messages.append(widget)
        self._last_text = text
        self.show()
        self.reposition.emit()
Ejemplo n.º 21
0
class LogviewDlgHelper(object):

    def __init__(self, logfilename, dlg, dlgUI):
        self.logfilename = logfilename
        self.dlgUI = dlgUI
        self.qtDlg = dlg

        self.readlog()
        self.update()

        self.dlgUI.logPTE.setReadOnly(True)
        self.dlgUI.closeBtn.clicked.connect(self.close)
        self.dlgUI.clearBtn.clicked.connect(self.clear)

        self.refresh_timer = QTimer(self.qtDlg)

        # noinspection PyUnresolvedReferences
        self.refresh_timer.timeout.connect(self.refresh)
        self.refresh_timer.start(1000) # update every 1 seconds


    def refresh(self):
        self.readlog()
        self.update()


    def readlog(self):
        with open(self.logfilename, 'r') as fl:
            self.logtext = fl.read()


    def close(self):
        self.refresh_timer.stop()
        self.qtDlg.accept()


    def clear(self):
        with open(self.logfilename, 'w'):
            pass
        self.readlog()
        self.update()


    def update(self):
        prev_cur_pos = self.dlgUI.logPTE.verticalScrollBar().value()

        self.dlgUI.logPTE.setPlainText(self.logtext)

        if QApplication.activeWindow() != self.qtDlg:
            self.dlgUI.logPTE.moveCursor(QTextCursor.End)
            self.dlgUI.logPTE.ensureCursorVisible()
        else:
            self.dlgUI.logPTE.moveCursor(QTextCursor.End)
            new_cur_pos = self.dlgUI.logPTE.verticalScrollBar().setValue(prev_cur_pos)
Ejemplo n.º 22
0
class OperatorViewWindow(QDialog):

    def __init__(self, *args, **kwargs):
        super(OperatorViewWindow, self).__init__(*args)
        warning = kwargs.get('warn', [0,0,0])
        failure = kwargs.get('fail', [0,0,0])
        tail = kwargs.get('tail', 10)
        remove_old = kwargs.get('remove_old', False)
        descriptions = kwargs.get('descriptions', [None] * 3)
        self.timer = None
        self.ui = form_class()
        self.ui.setupUi(self)
        self.status_bar = QStatusBar(self)
        self.ui.windowLayout.addWidget(self.status_bar)
        self.engine = kwargs['callback']
        self.graphs = [DynamicRiskCanvas(self, coordinate=i + 1, warning=warning[i], failure=failure[i],
                                         tail=tail, remove_old=remove_old, description=descriptions[i])
                       for i in range(3)]
        for graph in self.graphs:
            self.ui.y_layout.addWidget(graph)

    def initial_graphics_fill(self, real_values, predicted_values, risk_values, time_ticks):
        for i, graph in enumerate(self.graphs):
            graph.compute_initial_figure(real_values.T[i], predicted_values[i], risk_values[i], time_ticks)

    def update_graphics(self, real_value, predicted_values, risk_values, forecast_ticks):
        for i, graph in enumerate(self.graphs):
            graph.update_figure(real_value[i], predicted_values[i], risk_values[i], forecast_ticks)

    def closeEvent(self, event):
        if self.timer and self.timer.isActive():
            self.timer.stop()
            self.timer.disconnect()
            self.timer.deleteLater()
        super(QDialog, self).closeEvent(event)

    @pyqtSlot()
    def manipulate_timer(self):
        if not self.timer:
            self.ui.start_button.setText('Pause')
            self.timer = QTimer(self)
            self.timer.timeout.connect(self.execute_iteration)
            self.timer.start(50)
        elif self.timer.isActive():
            self.ui.start_button.setText('Continue')
            self.timer.stop()
        else:
            self.ui.start_button.setText('Pause')
            self.timer.start()

    @pyqtSlot()
    def execute_iteration(self):
        self.engine.launch()
Ejemplo n.º 23
0
class AutoSave:
    def __init__(self, application):
        self._application = application
        self._application.getPreferences().preferenceChanged.connect(self._triggerTimer)

        self._global_stack = None

        self._application.getPreferences().addPreference("cura/autosave_delay", 1000 * 10)

        self._change_timer = QTimer()
        self._change_timer.setInterval(int(self._application.getPreferences().getValue("cura/autosave_delay")))
        self._change_timer.setSingleShot(True)

        self._enabled = True
        self._saving = False

    def initialize(self):
        # only initialise if the application is created and has started
        self._change_timer.timeout.connect(self._onTimeout)
        self._application.globalContainerStackChanged.connect(self._onGlobalStackChanged)
        self._onGlobalStackChanged()
        self._triggerTimer()

    def _triggerTimer(self, *args):
        if not self._saving:
            self._change_timer.start()

    def setEnabled(self, enabled: bool) -> None:
        self._enabled = enabled
        if self._enabled:
            self._change_timer.start()
        else:
            self._change_timer.stop()

    def _onGlobalStackChanged(self):
        if self._global_stack:
            self._global_stack.propertyChanged.disconnect(self._triggerTimer)
            self._global_stack.containersChanged.disconnect(self._triggerTimer)

        self._global_stack = self._application.getGlobalContainerStack()

        if self._global_stack:
            self._global_stack.propertyChanged.connect(self._triggerTimer)
            self._global_stack.containersChanged.connect(self._triggerTimer)

    def _onTimeout(self):
        self._saving = True # To prevent the save process from triggering another autosave.
        Logger.log("d", "Autosaving preferences, instances and profiles")

        self._application.saveSettings()

        self._saving = False
class StateSetting(Setting):
    """Stores the last state of application.

    The state after start-up is determined programmatically;
    the value set during configuration loading will be ignored.
    """
    name = 'state_on'
    state = None

    require = {
        ModeSettings,
        EnableNightMode
    }

    @property
    def value(self):
        if self.mode_settings.mode == 'manual':
            return self.enable_night_mode.value
        else:
            return self.mode_settings.is_active

    @value.setter
    def value(self, value):
        pass

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # check the state every 60 seconds
        # (maybe a bit suboptimal, but the most reliable)
        from aqt import mw as main_window
        self.timer = QTimer(main_window)
        self.timer.setInterval(60 * 100)  # 1000 milliseconds
        self.timer.timeout.connect(self.maybe_enable_maybe_disable)

    def on_load(self):
        if self.value:
            self.app.on()

        self.update_state()
        self.timer.start()

    def on_save(self):
        self.timer.stop()

    def maybe_enable_maybe_disable(self):
        if self.value != self.state:
            self.app.refresh()
            self.update_state()

    def update_state(self):
        self.state = self.value
Ejemplo n.º 25
0
class gameWidget(QWidget):
    updateScore = pyqtSignal(int, name='updateScore')
    updateCurrentScore = pyqtSignal(int, name='updateCurrentScore')

    started = pyqtSignal()
    finished = pyqtSignal()

    def __init__(self, cfg):
        super(gameWidget, self).__init__()

        self.cfg = cfg
        self.totalScore = 0
        self.curScore = 0
        self.questionsAsked = 0
        self.runState = 0
        self.correct = 0
        self.wrong = 0

        self.timer = QTimer()
        self.timer.setInterval(500)
        self.timer.setSingleShot(False)

        self.startTime = time.time()

        self.timer.timeout.connect(self.updateTimer)
        
    def updateTimer(self):
        dt = time.time() - self.startTime
        if self.cfg['level'] < 4:
            dt = 0
        if dt > self.cfg['maxTime']:
            self.timer.stop()
        curScore = max(self.cfg['minPoints'], int((self.cfg['maxTime']-dt)*self.cfg['pointsPerSec']))
        self.updateCurrentScore.emit(curScore)

    def startGame(self):
    	raise RuntimeError('Game objects must implement start')

    def start(self):
        startScore = self.cfg['pointsPerSec']*self.cfg['maxTime']
        self.updateCurrentScore.emit(startScore)
        self.started.emit()
        self.startGame()

    def finishGame(self):
        raise RuntimeError('Game objects must implement finishGame')

    def finish(self):
        self.timer.stop()
        self.finished.emit()
        self.finishGame()
Ejemplo n.º 26
0
class VariableManager(plugin.DocumentPlugin):
    """Caches variables in the document and monitors for changes.

    The changed() Signal is emitted some time after the list of variables has been changed.
    It is recommended to not change the document itself in response to this signal.

    """
    changed = signals.Signal() # without argument

    def __init__(self, doc):
        self._updateTimer = QTimer(singleShot=True, timeout=self.slotTimeout)
        self._variables = self.readVariables()
        if doc.__class__ == document.EditorDocument:
            doc.contentsChange.connect(self.slotContentsChange)
            doc.closed.connect(self._updateTimer.stop) # just to be sure

    def slotTimeout(self):
        variables = self.readVariables()
        if variables != self._variables:
            self._variables = variables
            self.changed()

    def slotContentsChange(self, position, removed, added):
        """Called if the document changes."""
        if (self.document().findBlock(position).blockNumber() < _LINES or
            self.document().findBlock(position + added).blockNumber() > self.document().blockCount() - _LINES):
            self._updateTimer.start(500)

    def variables(self):
        """Returns the document variables (cached) as a dictionary. This method is recommended."""
        if self._updateTimer.isActive():
            # an update is pending, force it
            self._updateTimer.stop()
            self.slotTimeout()
        return self._variables

    def readVariables(self):
        """Reads the variables from the document and returns a dictionary. Internal."""
        count = self.document().blockCount()
        blocks = [self.document().firstBlock()]
        if count > _LINES * 2:
            blocks.append(self.document().findBlockByNumber(count - _LINES))
            count = _LINES
        def lines(block):
            for i in range(count):
                yield block.text()
                block = block.next()
        variables = {}
        for block in blocks:
            variables.update(m.group(1, 2) for n, m in positions(lines(block)))
        return variables
Ejemplo n.º 27
0
                class EvMessageBox(QMessageBox):
                    """ QMessageBox with timer.

                    Parameters
                    ----------
                    text : string
                        Text of message.
                    title : string
                        Title of message window.
                    wicon : QIcon object
                        Icon of message window.
                    icon : QMessageBox.Icon int
                        Icon of message body.
                    timeout : int
                        Time for message has being shown.

                    Useful attributes
                    -----------------
                    timer : QTimer object
                        Timer attached to message.

                    """
                    def __init__(self, text, title, wicon, icon, timeout):
                        super().__init__()
                        self.timeout = timeout
                        self.setText(text)
                        self.setWindowTitle(title)
                        self.setWindowIcon(wicon)
                        self.setIcon(icon)
                        self.addButton(QPushButton(conf.lang.REPEAT.format(
                                                   parseSeconds(conf.tdelta))),
                                       QMessageBox.YesRole)
                        self.addButton(QPushButton(conf.lang.CLOSE),
                                       QMessageBox.NoRole)
                        self.setWindowFlags(Qt.WindowStaysOnTopHint)
                        # self.setTextFormat(Qt.RichText)
                        self.timer = QTimer()
                        self.timer.timeout.connect(self.timerTick)

                    def showEvent(self, event):
                        """ Start timer on message showEvent. """
                        self.currentTime = 0
                        self.timer.start(1000)

                    def timerTick(self):
                        """ Done message on timeout. """
                        self.currentTime += 1
                        if self.currentTime >= self.timeout:
                            self.timer.stop()
                            self.done(-1)
Ejemplo n.º 28
0
class CountDownWidget(QWidget):
    '''
    classdocs
    '''
    def __init__(self, parent=None):
        '''
        Constructor
        '''
        QWidget.__init__(self, parent=parent)
        self.interval = 10
        self.setup_ui()
    '''
    実際の初期化関数
    '''
    def setup_ui(self):
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.timer = QTimer(parent=self)
        self.timer.setInterval(self.interval)
        self.timer.timeout.connect(self.do_countdown)
        self.lcd_number = QLCDNumber(parent=self)
        self.lcd_number.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.lcd_number.setFrameStyle(QFrame.NoFrame)
        self.lcd_number.setSegmentStyle(QLCDNumber.Flat)
        self.lcd_number.setDigitCount(6)

        layout = QVBoxLayout()
        layout.addWidget(self.lcd_number)
        self.setLayout(layout)

        self.reset_count()

    def update_display(self):
        self.lcd_number.display("%6.2f" % (self.count / 100))
        self.lcd_number.update()
    def do_countdown(self):
        self.count -= 1
        self.update_display()
        if self.count <= 0:
            self.stopcountdown()
    def start_countdown(self):
        if self.count > 0:
            self.timer.start()
    def stop_countdown(self):
        self.timer.stop()
    def reset_count(self):
        self.count = 18000
        self.update_display()
Ejemplo n.º 29
0
class MemUsageDialog(QDialog):
    def __init__(self, parent=None, update=True):
        QDialog.__init__(self, parent=parent)
        layout = QVBoxLayout()
        self.tree = QTreeWidget()
        layout.addWidget(self.tree)
        self.setLayout(layout)

        self._mgr = CacheMemoryManager()

        self._tracked_caches = {}

        # tree setup code
        self.tree.setHeaderLabels(
            ["cache", "memory", "roi", "dtype", "type", "info", "id"])
        self._idIndex = self.tree.columnCount() - 1
        self.tree.setColumnHidden(self._idIndex, True)
        self.tree.setSortingEnabled(True)
        self.tree.clear()

        self._root = TreeNode()

        # refresh every x seconds (see showEvent())
        self.timer = QTimer(self)
        if update:
            self.timer.timeout.connect(self._updateReport)

    def _updateReport(self):
        # we keep track of dirty reports so we just have to update the tree
        # instead of reconstructing it
        reports = []
        for c in self._mgr.getFirstClassCaches():
            r = MemInfoNode()
            c.generateReport(r)
            reports.append(r)
        self._root.handleChildrenReports(
            reports, root=self.tree.invisibleRootItem())

    def hideEvent(self, event):
        self.timer.stop()

    def showEvent(self, show):
        # update once so we don't have to wait for initial report
        self._updateReport()
        # update every 5 sec.
        self.timer.start(5*1000)
Ejemplo n.º 30
0
class WorkThread(QObject):
    trigger = pyqtSignal()

    def __init__(self):
        super(WorkThread, self).__init__()
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.timeOut)

    def beginRun(self, in_parameters):
        self.p = Process(target=mainProcess, kwargs=in_parameters)
        self.p.start()
        self.timer.start(1000)

    def timeOut(self):
        if not self.p.is_alive():  # 当前子进程完成后
            self.trigger.emit()  # 发出处理完成信息
            self.timer.stop()  # 并停止计时器,否则在同一进程下再次运行会导致过早判断子进程的状态
Ejemplo n.º 31
0
class EventRequestManager(QNetworkAccessManager):
    """
    The EventRequestManager class handles the events connection over which important events in Tribler are pushed.
    """

    received_search_result_channel = pyqtSignal(object)
    received_search_result_torrent = pyqtSignal(object)
    tribler_started = pyqtSignal()
    upgrader_tick = pyqtSignal(str)
    upgrader_started = pyqtSignal()
    upgrader_finished = pyqtSignal()
    new_version_available = pyqtSignal(str)
    discovered_channel = pyqtSignal(object)
    discovered_torrent = pyqtSignal(object)
    torrent_finished = pyqtSignal(object)
    received_market_ask = pyqtSignal(object)
    received_market_bid = pyqtSignal(object)
    expired_market_ask = pyqtSignal(object)
    expired_market_bid = pyqtSignal(object)
    market_transaction_complete = pyqtSignal(object)
    market_payment_received = pyqtSignal(object)
    market_payment_sent = pyqtSignal(object)
    market_iom_input_required = pyqtSignal(object)
    events_started = pyqtSignal(object)
    low_storage_signal = pyqtSignal(object)

    def __init__(self, api_port):
        QNetworkAccessManager.__init__(self)
        url = QUrl("http://localhost:%d/events" % api_port)
        self.request = QNetworkRequest(url)
        self.failed_attempts = 0
        self.connect_timer = QTimer()
        self.current_event_string = ""
        self.tribler_version = "Unknown"
        self.reply = None
        self.emitted_tribler_started = False  # We should only emit tribler_started once
        self.shutting_down = False
        self._logger = logging.getLogger('TriblerGUI')

    def on_error(self, error, reschedule_on_err):
        self._logger.info("Got Tribler core error: %s" % error)
        if error == QNetworkReply.ConnectionRefusedError:
            if self.failed_attempts == 40:
                raise RuntimeError(
                    "Could not connect with the Tribler Core within 20 seconds"
                )

            self.failed_attempts += 1

            if reschedule_on_err:
                # Reschedule an attempt
                self.connect_timer = QTimer()
                self.connect_timer.setSingleShot(True)
                self.connect_timer.timeout.connect(self.connect)
                self.connect_timer.start(500)

    def on_read_data(self):
        if self.receivers(self.finished) == 0:
            self.finished.connect(lambda reply: self.on_finished())
        self.connect_timer.stop()
        data = self.reply.readAll()
        self.current_event_string += data
        if len(self.current_event_string
               ) > 0 and self.current_event_string[-1] == '\n':
            for event in self.current_event_string.split('\n'):
                if len(event) == 0:
                    continue
                json_dict = json.loads(str(event))

                received_events.insert(0, (json_dict, time.time()))
                if len(received_events
                       ) > 100:  # Only buffer the last 100 events
                    received_events.pop()

                if json_dict["type"] == "search_result_channel":
                    self.received_search_result_channel.emit(
                        json_dict["event"]["result"])
                elif json_dict["type"] == "search_result_torrent":
                    self.received_search_result_torrent.emit(
                        json_dict["event"]["result"])
                elif json_dict[
                        "type"] == "tribler_started" and not self.emitted_tribler_started:
                    self.tribler_started.emit()
                    self.emitted_tribler_started = True
                elif json_dict["type"] == "new_version_available":
                    self.new_version_available.emit(
                        json_dict["event"]["version"])
                elif json_dict["type"] == "upgrader_started":
                    self.upgrader_started.emit()
                elif json_dict["type"] == "upgrader_finished":
                    self.upgrader_finished.emit()
                elif json_dict["type"] == "upgrader_tick":
                    self.upgrader_tick.emit(json_dict["event"]["text"])
                elif json_dict["type"] == "channel_discovered":
                    self.discovered_channel.emit(json_dict["event"])
                elif json_dict["type"] == "torrent_discovered":
                    self.discovered_torrent.emit(json_dict["event"])
                elif json_dict["type"] == "events_start":
                    self.events_started.emit(json_dict["event"])
                    self.tribler_version = json_dict["event"]["version"]
                    if json_dict["event"][
                            "tribler_started"] and not self.emitted_tribler_started:
                        self.tribler_started.emit()
                        self.emitted_tribler_started = True
                elif json_dict["type"] == "torrent_finished":
                    self.torrent_finished.emit(json_dict["event"])
                elif json_dict["type"] == "market_ask":
                    self.received_market_ask.emit(json_dict["event"])
                elif json_dict["type"] == "market_bid":
                    self.received_market_bid.emit(json_dict["event"])
                elif json_dict["type"] == "market_ask_timeout":
                    self.expired_market_ask.emit(json_dict["event"])
                elif json_dict["type"] == "market_bid_timeout":
                    self.expired_market_bid.emit(json_dict["event"])
                elif json_dict["type"] == "market_transaction_complete":
                    self.market_transaction_complete.emit(json_dict["event"])
                elif json_dict["type"] == "market_payment_received":
                    self.market_payment_received.emit(json_dict["event"])
                elif json_dict["type"] == "market_payment_sent":
                    self.market_payment_sent.emit(json_dict["event"])
                elif json_dict["type"] == "market_iom_input_required":
                    self.market_iom_input_required.emit(json_dict["event"])
                elif json_dict["type"] == "signal_low_space":
                    self.low_storage_signal.emit(json_dict["event"])
                elif json_dict["type"] == "tribler_exception":
                    raise RuntimeError(json_dict["event"]["text"])
            self.current_event_string = ""

    def on_finished(self):
        """
        Somehow, the events connection dropped. Try to reconnect.
        """
        if self.shutting_down:
            return
        self._logger.warning(
            "Events connection dropped, attempting to reconnect")
        self.failed_attempts = 0

        self.connect_timer = QTimer()
        self.connect_timer.setSingleShot(True)
        self.connect_timer.timeout.connect(self.connect)
        self.connect_timer.start(500)

    def connect(self, reschedule_on_err=True):
        self._logger.info("Will connect to events endpoint")
        self.reply = self.get(self.request)

        self.reply.readyRead.connect(self.on_read_data)
        self.reply.error.connect(lambda error: self.on_error(
            error, reschedule_on_err=reschedule_on_err))
Ejemplo n.º 32
0
class Player(QMainWindow):
    """A simple Media Player using VLC and Qt
    """
    def __init__(self, master=None):
        QMainWindow.__init__(self, master)
        self.setWindowTitle("Video Annotation Tool")

        # creating a basic vlc instance
        self.instance = vlc.Instance()
        # creating an empty vlc media player
        self.mediaplayer = self.instance.media_player_new()

        self.createUI()
        self.isPaused = False

        self.setMouseTracking(True)
        self.size = None

    def createUI(self):
        """Set up the user interface, signals & slots
        """
        self.widget = QWidget(self)
        self.setCentralWidget(self.widget)

        # In this widget, the video will be drawn
        if sys.platform == "darwin":  # for MacOS
            from PyQt5.QtWidgets import QMacCocoaViewContainer
            self.videoframe = QMacCocoaViewContainer(0)
        else:
            self.videoframe = QFrame()
        self.palette = self.videoframe.palette()
        self.palette.setColor(QPalette.Window, QColor(0, 0, 0))
        self.videoframe.setPalette(self.palette)
        self.videoframe.setAutoFillBackground(True)

        self.positionslider = QSlider(Qt.Horizontal, self)
        self.positionslider.setToolTip("Position")
        self.positionslider.setMaximum(1000)
        self.positionslider.sliderMoved.connect(self.setPosition)

        self.hbuttonbox = QHBoxLayout()
        self.playbutton = QPushButton("Play")
        self.hbuttonbox.addWidget(self.playbutton)
        self.playbutton.clicked.connect(self.PlayPause)

        self.stopbutton = QPushButton("Stop")
        self.hbuttonbox.addWidget(self.stopbutton)
        self.stopbutton.clicked.connect(self.Stop)

        self.time_label = QLabel("<center>0</center>")
        self.coordinates = QLabel("<center>x, y</center>")
        self.hbuttonbox.addWidget(self.time_label)
        self.hbuttonbox.addWidget(self.coordinates)

        self.hbuttonbox.addStretch(1)
        self.volumeslider = QSlider(Qt.Horizontal, self)
        self.volumeslider.setMaximum(100)
        self.volumeslider.setValue(self.mediaplayer.audio_get_volume())
        self.volumeslider.setToolTip("Volume")
        self.hbuttonbox.addWidget(self.volumeslider)
        self.volumeslider.valueChanged.connect(self.setVolume)

        self.vboxlayout = QVBoxLayout()
        self.vboxlayout.addWidget(self.videoframe)
        self.vboxlayout.addWidget(self.positionslider)
        self.vboxlayout.addLayout(self.hbuttonbox)

        self.widget.setLayout(self.vboxlayout)

        open = QAction("&Open", self)
        open.triggered.connect(self.OpenFile)
        exit = QAction("&Exit", self)
        exit.triggered.connect(sys.exit)
        menubar = self.menuBar()
        filemenu = menubar.addMenu("&File")
        filemenu.addAction(open)
        filemenu.addSeparator()
        filemenu.addAction(exit)

        self.timer = QTimer(self)
        self.timer.setInterval(100)
        self.timer.timeout.connect(self.updateUI)

    def PlayPause(self):
        """Toggle play/pause status
        """
        if self.mediaplayer.is_playing():
            self.mediaplayer.pause()
            self.playbutton.setText("Play")
            self.isPaused = True
        else:
            if self.mediaplayer.play() == -1:
                self.OpenFile()
                return
            self.mediaplayer.play()
            self.playbutton.setText("Pause")
            self.timer.start()
            self.isPaused = False

    def Stop(self):
        """Stop player
        """
        self.mediaplayer.stop()
        self.playbutton.setText("Play")

    def OpenFile(self, filename=None):
        """Open a media file in a MediaPlayer
        """
        if filename is None:
            filename = QFileDialog.getOpenFileName(self, "Open File",
                                                   os.path.expanduser('~'))[0]
        if not filename:
            filename = QFileDialog.getOpenFileName(self, "Open File",
                                                   os.path.expanduser('~'))[0]
            #return

        # create the media
        if sys.version < '3':
            filename = unicode(filename)
        self.media = self.instance.media_new(filename)
        # put the media in the media player
        self.mediaplayer.set_media(self.media)

        # parse the metadata of the file
        self.media.parse()
        # set the title of the track as window title
        self.setWindowTitle(self.media.get_meta(0))

        # the media player has to be 'connected' to the QFrame
        # (otherwise a video would be displayed in it's own window)
        # this is platform specific!
        # you have to give the id of the QFrame (or similar object) to
        # vlc, different platforms have different functions for this
        if sys.platform.startswith('linux'):  # for Linux using the X Server
            self.mediaplayer.set_xwindow(self.videoframe.winId())
        elif sys.platform == "win32":  # for Windows
            self.mediaplayer.set_hwnd(self.videoframe.winId())
        elif sys.platform == "darwin":  # for MacOS
            self.mediaplayer.set_nsobject(int(self.videoframe.winId()))
        self.size = self.mediaplayer.video_get_size()
        self.PlayPause()

    def setVolume(self, Volume):
        """Set the volume
        """
        self.mediaplayer.audio_set_volume(Volume)

    def setPosition(self, position):
        """Set the position
        """
        # setting the position to where the slider was dragged
        self.mediaplayer.set_position(position / 1000.0)
        # the vlc MediaPlayer needs a float value between 0 and 1, Qt
        # uses integer variables, so you need a factor; the higher the
        # factor, the more precise are the results
        # (1000 should be enough)

    def updateUI(self):
        """updates the user interface"""
        # setting the slider to the desired position
        self.positionslider.setValue(self.mediaplayer.get_position() * 1000)
        self.time_label.setText(
            f'Time {str(self.mediaplayer.get_time() / 1000)} seconds')
        #self.size = self.mediaplayer.video_get_size()

        if not self.mediaplayer.is_playing():
            # no need to call this function if nothing is played
            self.timer.stop()
            if not self.isPaused:
                # after the video finished, the play button stills shows
                # "Pause", not the desired behavior of a media player
                # this will fix it
                self.Stop()

    def get_coordinates(self):
        return self.mediaplayer.video_get_cursor()

    def mouseMoveEvent(self, event):
        print(self.mediaplayer.video_get_cursor())
        if isinstance(self.size, tuple):
            video_width, video_height = self.size
        else:
            video_width, video_height = 0, 0
        # self.coordinates.setText('Coordinates: ( %d : %d )' % (event.x(), event.y()) + "Distance from center: " + str(distance_from_center))
        #self.coordinates.setText('Coordinates: ( %d : %d )' % (event.x() - 9, event.y() - 30) + "Distance from center: ")
        self.coordinates.setText(
            f'Coordinates: {self.get_coordinates()[0]}, {self.get_coordinates()[1]}. Video size {video_height} {video_width}'
        )
        self.pos = event.pos()
        self.update()
Ejemplo n.º 33
0
class Ycm(QObject, CategoryMixin):
    """YCMD instance control"""

    YCMD_CMD = ['ycmd']
    """Base ycmd command.

	Useful if ycmd is not in `PATH` or set permanent arguments
	"""

    IDLE_SUICIDE = 120
    """Maximum time after which ycmd should quit if it has received no requests.

	A periodic ping is sent by `Ycm` objects.
	"""

    CHECK_REPLY_SIGNATURE = True
    TIMEOUT = 10

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

        self.addr = None
        """Address of the ycmd server."""

        self.port = 0
        """TCP port of the ycmd server."""

        self._ready = False
        self.secret = ''
        self.config = {}

        self.proc = QProcess()
        self.proc.started.connect(self.procStarted)
        self.proc.errorOccurred.connect(self.procError)
        self.proc.finished.connect(self.procFinished)

        self.pingTimer = QTimer(self)
        self.pingTimer.timeout.connect(self.ping)

        self.network = QNetworkAccessManager()

        qApp().aboutToQuit.connect(self.stop)

        self.addCategory('ycm_control')

    def makeConfig(self):
        self.secret = generate_key()
        self.config['hmac_secret'] = b64encode(self.secret).decode('ascii')

        fd, path = tempfile.mkstemp()
        with open(path, 'w') as fd:
            fd.write(json.dumps(self.config))
            fd.flush()
        return path

    def checkReply(self, reply):
        """Check the ycmd reply is a success.

		Checks the `reply` has a HTTP 200 status code and the signature is valid.
		In case of error, raises a :any:`ServerError`.

		:type reply: QNetworkReply
		"""
        reply.content = bytes(reply.readAll())

        if reply.error():
            raise ServerError(reply.error() + 1000, reply.errorString(),
                              reply.content)
        status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
        if status_code != 200:
            data = reply.content.decode('utf-8')
            try:
                data = json.loads(data)
            except (ValueError, JSONDecodeError):
                LOGGER.info('ycmd replied non-json body: %r', data)

            raise ServerError(status_code, data)

        if not self.CHECK_REPLY_SIGNATURE:
            return
        actual = b64decode(bytes(reply.rawHeader(HMAC_HEADER)))
        expected = self._hmacDigest(reply.content)

        if not hmac.compare_digest(expected, actual):
            raise RuntimeError('Server signature did not match')

    def _jsonReply(self, reply):
        body = reply.content.decode('utf-8')
        return json.loads(body)

    def _hmacDigest(self, msg):
        return hmac.new(self.secret, msg, hashlib.sha256).digest()

    def _sign(self, verb, path, body=b''):
        digests = [self._hmacDigest(part) for part in [verb, path, body]]
        return self._hmacDigest(b''.join(digests))

    def _doGet(self, path):
        url = urlunsplit(('http', self.addr, path, '', ''))
        sig = self._sign(b'GET', path.encode('utf-8'), b'')
        headers = {HMAC_HEADER: b64encode(sig)}

        request = QNetworkRequest(QUrl(url))
        for hname in headers:
            request.setRawHeader(hname, headers[hname])

        LOGGER_REQUESTS.debug('GET %r', url)
        reply = self.network.get(request)
        return reply

    def _doPost(self, path, **params):
        url = urlunsplit(('http', self.addr, path, '', ''))
        body = json.dumps(params).encode('utf-8')
        sig = self._sign(b'POST', path.encode('utf-8'), body)
        headers = {
            HMAC_HEADER: b64encode(sig),
            b'Content-Type': b'application/json'
        }

        request = QNetworkRequest(QUrl(url))
        for hname in headers:
            request.setRawHeader(hname, headers[hname])
        LOGGER_REQUESTS.debug('POST %r with data %r', url, body)
        reply = self.network.post(request, body)
        return reply

    def ping(self):
        def handleReply():
            self.checkReply(reply)
            if not self._ready:
                self._ready = True
                self.pingTimer.start(60000)
                self.ready.emit()

        reply = self._doGet('/healthy')
        reply.finished.connect(handleReply)
        reply.finished.connect(reply.deleteLater)

    def start(self):
        if not self.port:
            self.port = generate_port()
        self.addr = 'localhost:%s' % self.port
        path = self.makeConfig()

        _, outlogpath = tempfile.mkstemp(prefix='eye-ycm', suffix='.out.log')
        _, errlogpath = tempfile.mkstemp(prefix='eye-ycm', suffix='.err.log')
        LOGGER.info('ycmd will log to %r and %r', outlogpath, errlogpath)

        cmd = (self.YCMD_CMD + [
            '--idle_suicide_seconds',
            str(self.IDLE_SUICIDE),
            '--port',
            str(self.port),
            '--options_file',
            path,
            '--stdout',
            outlogpath,
            '--stderr',
            errlogpath,
        ])

        LOGGER.debug('will run %r', cmd)

        self.proc.start(cmd[0], cmd[1:])

        self._ready = False

    @Slot()
    def stop(self, wait=0.2):
        if self.proc.state() == QProcess.NotRunning:
            return
        self.proc.terminate()
        if self.proc.state() == QProcess.NotRunning:
            return
        time.sleep(wait)
        self.proc.kill()

    def isRunning(self):
        return self.proc.state() == QProcess.Running

    def connectTo(self, addr):
        self.addr = addr
        self._ready = False
        self.pingTimer.start(1000)

    @Slot()
    def procStarted(self):
        LOGGER.debug('daemon has started')
        self.pingTimer.start(1000)

    @Slot(int, QProcess.ExitStatus)
    def procFinished(self, code, status):
        LOGGER.info('daemon has exited with status %r and code %r', status,
                    code)
        self.pingTimer.stop()
        self._ready = False

    @Slot(QProcess.ProcessError)
    def procError(self, error):
        LOGGER.warning('daemon failed to start (%r): %s', error,
                       self.errorString())

    ready = Signal()

    def _commonPostDict(self, filepath, filetype, contents, line=1, column=1):
        d = {
            'filepath': filepath,
            'filetype': filetype,
            'file_data': {
                filepath: {
                    'filetypes': [filetype],
                    'contents': contents
                }
            },
            'line_num': line,
            'column_num': column,
        }
        return d

    def _postSimpleRequest(self, urlpath, filepath, filetype, contents,
                           **kwargs):
        d = self._commonPostDict(filepath, filetype, contents)
        d.update(**kwargs)

        return self._doPost(urlpath, **d)

    def acceptExtraConf(self, filepath, filetype, contents):
        reply = self._postSimpleRequest('/load_extra_conf_file', filepath,
                                        filetype, contents)
        reply.finished.connect(reply.deleteLater)

    def rejectExtraConf(self, filepath, filetype, contents):
        reply = self._postSimpleRequest('/ignore_extra_conf_file',
                                        filepath,
                                        filetype,
                                        contents,
                                        _ignore_body=True)
        reply.finished.connect(reply.deleteLater)

    def sendParse(self, filepath, filetype, contents, retry_extra=True):
        d = {'event_name': 'FileReadyToParse'}
        reply = self._postSimpleRequest('/event_notification', filepath,
                                        filetype, contents, **d)

        def handleReply():
            try:
                self.checkReply(reply)
            except ServerError as exc:
                excdata = exc.args[1]
                if (isinstance(excdata, dict) and 'exception' in excdata
                        and excdata['exception']['TYPE'] == 'UnknownExtraConf'
                        and retry_extra):
                    confpath = excdata['exception']['extra_conf_file']
                    LOGGER.info(
                        'ycmd encountered %r and wonders if it should be loaded',
                        confpath)

                    accepted = sendIntent(None,
                                          'queryExtraConf',
                                          conf=confpath)
                    if accepted:
                        LOGGER.info('extra conf %r will be loaded', confpath)
                        self.acceptExtraConf(confpath, filetype, contents)
                    else:
                        LOGGER.info('extra conf %r will be rejected', confpath)
                        self.rejectExtraConf(confpath, filetype, contents)

                    return self.sendParse(filepath,
                                          filetype,
                                          contents,
                                          retry_extra=False)
                raise

        reply.finished.connect(handleReply)
        reply.finished.connect(reply.deleteLater)

    def querySubcommandsList(self, filepath, filetype, contents, line, col):
        return self._postSimpleRequest('/defined_subcommands', filepath,
                                       filetype, contents)

    def querySubcommand(self, filepath, filetype, contents, line, col, *args):
        d = {
            'command_arguments': list(args),
            'line_num': line,
            'column_num': col,
        }
        return self._postSimpleRequest('/run_completer_command', filepath,
                                       filetype, contents, **d)

    def queryCompletions(self, filepath, filetype, contents, line, col):
        d = {
            'line_num': line,
            'column_num': col,
        }
        return self._postSimpleRequest('/completions', filepath, filetype,
                                       contents, **d)

    if 0:

        def queryDiagnostic(self, filepath, filetype, contents, line, col):
            return self._postSimpleRequest('/detailed_diagnostic', filepath,
                                           filetype, contents)

        def queryDebug(self, filepath, filetype, contents, line, col):
            return self._postSimpleRequest('/debug_info', filepath, filetype,
                                           contents)
Ejemplo n.º 34
0
class QVTKRenderWindowInteractor(QVTKRWIBaseClass):

    """ A QVTKRenderWindowInteractor for Python and Qt.  Uses a
    vtkGenericRenderWindowInteractor to handle the interactions.  Use
    GetRenderWindow() to get the vtkRenderWindow.  Create with the
    keyword stereo=1 in order to generate a stereo-capable window.

    The user interface is summarized in vtkInteractorStyle.h:

    - Keypress j / Keypress t: toggle between joystick (position
    sensitive) and trackball (motion sensitive) styles. In joystick
    style, motion occurs continuously as long as a mouse button is
    pressed. In trackball style, motion occurs when the mouse button
    is pressed and the mouse pointer moves.

    - Keypress c / Keypress o: toggle between camera and object
    (actor) modes. In camera mode, mouse events affect the camera
    position and focal point. In object mode, mouse events affect
    the actor that is under the mouse pointer.

    - Button 1: rotate the camera around its focal point (if camera
    mode) or rotate the actor around its origin (if actor mode). The
    rotation is in the direction defined from the center of the
    renderer's viewport towards the mouse position. In joystick mode,
    the magnitude of the rotation is determined by the distance the
    mouse is from the center of the render window.

    - Button 2: pan the camera (if camera mode) or translate the actor
    (if object mode). In joystick mode, the direction of pan or
    translation is from the center of the viewport towards the mouse
    position. In trackball mode, the direction of motion is the
    direction the mouse moves. (Note: with 2-button mice, pan is
    defined as <Shift>-Button 1.)

    - Button 3: zoom the camera (if camera mode) or scale the actor
    (if object mode). Zoom in/increase scale if the mouse position is
    in the top half of the viewport; zoom out/decrease scale if the
    mouse position is in the bottom half. In joystick mode, the amount
    of zoom is controlled by the distance of the mouse pointer from
    the horizontal centerline of the window.

    - Keypress 3: toggle the render window into and out of stereo
    mode.  By default, red-blue stereo pairs are created. Some systems
    support Crystal Eyes LCD stereo glasses; you have to invoke
    SetStereoTypeToCrystalEyes() on the rendering window.  Note: to
    use stereo you also need to pass a stereo=1 keyword argument to
    the constructor.

    - Keypress e: exit the application.

    - Keypress f: fly to the picked point

    - Keypress p: perform a pick operation. The render window interactor
    has an internal instance of vtkCellPicker that it uses to pick.

    - Keypress r: reset the camera view along the current view
    direction. Centers the actors and moves the camera so that all actors
    are visible.

    - Keypress s: modify the representation of all actors so that they
    are surfaces.

    - Keypress u: invoke the user-defined function. Typically, this
    keypress will bring up an interactor that you can type commands in.

    - Keypress w: modify the representation of all actors so that they
    are wireframe.
    """

    # Map between VTK and Qt cursors.
    _CURSOR_MAP = {
        0:  Qt.ArrowCursor,          # VTK_CURSOR_DEFAULT
        1:  Qt.ArrowCursor,          # VTK_CURSOR_ARROW
        2:  Qt.SizeBDiagCursor,      # VTK_CURSOR_SIZENE
        3:  Qt.SizeFDiagCursor,      # VTK_CURSOR_SIZENWSE
        4:  Qt.SizeBDiagCursor,      # VTK_CURSOR_SIZESW
        5:  Qt.SizeFDiagCursor,      # VTK_CURSOR_SIZESE
        6:  Qt.SizeVerCursor,        # VTK_CURSOR_SIZENS
        7:  Qt.SizeHorCursor,        # VTK_CURSOR_SIZEWE
        8:  Qt.SizeAllCursor,        # VTK_CURSOR_SIZEALL
        9:  Qt.PointingHandCursor,   # VTK_CURSOR_HAND
        10: Qt.CrossCursor,          # VTK_CURSOR_CROSSHAIR
    }

    def __init__(self, parent=None, **kw):
        # the current button
        self._ActiveButton = Qt.NoButton

        # private attributes
        self.__saveX = 0
        self.__saveY = 0
        self.__saveModifiers = Qt.NoModifier
        self.__saveButtons = Qt.NoButton
        self.__wheelDelta = 0

        # do special handling of some keywords:
        # stereo, rw

        try:
            stereo = bool(kw['stereo'])
        except KeyError:
            stereo = False

        try:
            rw = kw['rw']
        except KeyError:
            rw = None

        # create base qt-level widget
        if QVTKRWIBase == "QWidget":
            if "wflags" in kw:
                wflags = kw['wflags']
            else:
                wflags = Qt.WindowFlags()
            QWidget.__init__(self, parent, wflags | Qt.MSWindowsOwnDC)
        elif QVTKRWIBase == "QGLWidget":
            QGLWidget.__init__(self, parent)

        if rw: # user-supplied render window
            self._RenderWindow = rw
        else:
            self._RenderWindow = vtk.vtkRenderWindow()

        WId = self.winId()

        # Python2
        if type(WId).__name__ == 'PyCObject':
            from ctypes import pythonapi, c_void_p, py_object

            pythonapi.PyCObject_AsVoidPtr.restype  = c_void_p
            pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object]

            WId = pythonapi.PyCObject_AsVoidPtr(WId)

        # Python3
        elif type(WId).__name__ == 'PyCapsule':
            from ctypes import pythonapi, c_void_p, py_object, c_char_p

            pythonapi.PyCapsule_GetName.restype = c_char_p
            pythonapi.PyCapsule_GetName.argtypes = [py_object]

            name = pythonapi.PyCapsule_GetName(WId)

            pythonapi.PyCapsule_GetPointer.restype  = c_void_p
            pythonapi.PyCapsule_GetPointer.argtypes = [py_object, c_char_p]

            WId = pythonapi.PyCapsule_GetPointer(WId, name)

        self._RenderWindow.SetWindowInfo(str(int(WId)))

        if stereo: # stereo mode
            self._RenderWindow.StereoCapableWindowOn()
            self._RenderWindow.SetStereoTypeToCrystalEyes()

        try:
            self._Iren = kw['iren']
        except KeyError:
            self._Iren = vtk.vtkGenericRenderWindowInteractor()
            self._Iren.SetRenderWindow(self._RenderWindow)

        # do all the necessary qt setup
        self.setAttribute(Qt.WA_OpaquePaintEvent)
        self.setAttribute(Qt.WA_PaintOnScreen)
        self.setMouseTracking(True) # get all mouse events
        self.setFocusPolicy(Qt.WheelFocus)
        self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))

        self._Timer = QTimer(self)
        self._Timer.timeout.connect(self.TimerEvent)

        self._Iren.AddObserver('CreateTimerEvent', self.CreateTimer)
        self._Iren.AddObserver('DestroyTimerEvent', self.DestroyTimer)
        self._Iren.GetRenderWindow().AddObserver('CursorChangedEvent',
                                                 self.CursorChangedEvent)

        #Create a hidden child widget and connect its destroyed signal to its
        #parent ``Finalize`` slot. The hidden children will be destroyed before
        #its parent thus allowing cleanup of VTK elements.
        self._hidden = QWidget(self)
        self._hidden.hide()
        self._hidden.destroyed.connect(self.Finalize)

    def __getattr__(self, attr):
        """Makes the object behave like a vtkGenericRenderWindowInteractor"""
        if attr == '__vtk__':
            return lambda t=self._Iren: t
        elif hasattr(self._Iren, attr):
            return getattr(self._Iren, attr)
        else:
            raise AttributeError(self.__class__.__name__ +
                  " has no attribute named " + attr)

    def Finalize(self):
        '''
        Call internal cleanup method on VTK objects
        '''
        self._RenderWindow.Finalize()

    def CreateTimer(self, obj, evt):
        self._Timer.start(10)

    def DestroyTimer(self, obj, evt):
        self._Timer.stop()
        return 1

    def TimerEvent(self):
        self._Iren.TimerEvent()

    def CursorChangedEvent(self, obj, evt):
        """Called when the CursorChangedEvent fires on the render window."""
        # This indirection is needed since when the event fires, the current
        # cursor is not yet set so we defer this by which time the current
        # cursor should have been set.
        QTimer.singleShot(0, self.ShowCursor)

    def HideCursor(self):
        """Hides the cursor."""
        self.setCursor(Qt.BlankCursor)

    def ShowCursor(self):
        """Shows the cursor."""
        vtk_cursor = self._Iren.GetRenderWindow().GetCurrentCursor()
        qt_cursor = self._CURSOR_MAP.get(vtk_cursor, Qt.ArrowCursor)
        self.setCursor(qt_cursor)

    def closeEvent(self, evt):
        self.Finalize()

    def sizeHint(self):
        return QSize(400, 400)

    def paintEngine(self):
        return None

    def paintEvent(self, ev):
        self._Iren.Render()

    def resizeEvent(self, ev):
        w = self.width()
        h = self.height()
        vtk.vtkRenderWindow.SetSize(self._RenderWindow, w, h)
        self._Iren.SetSize(w, h)
        self._Iren.ConfigureEvent()
        self.update()

    def _GetCtrlShift(self, ev):
        ctrl = shift = False

        if hasattr(ev, 'modifiers'):
            if ev.modifiers() & Qt.ShiftModifier:
                shift = True
            if ev.modifiers() & Qt.ControlModifier:
                ctrl = True
        else:
            if self.__saveModifiers & Qt.ShiftModifier:
                shift = True
            if self.__saveModifiers & Qt.ControlModifier:
                ctrl = True

        return ctrl, shift

    @staticmethod
    def _getPixelRatio():
        if PyQtImpl == "PyQt5":
            # Source: https://stackoverflow.com/a/40053864/3388962
            pos = QCursor.pos()
            for screen in QApplication.screens():
                rect = screen.geometry()
                if rect.contains(pos):
                    return screen.devicePixelRatio()
            # Should never happen, but try to find a good fallback.
            return QApplication.screens()[0].devicePixelRatio()
        else:
            # Qt4 seems not to provide any cross-platform means to get the
            # pixel ratio.
            return 1.

    def _setEventInformation(self, x, y, ctrl, shift,
                             key, repeat=0, keysum=None):
        scale = self._getPixelRatio()
        self._Iren.SetEventInformation(int(round(x*scale)),
                                       int(round((self.height()-y-1)*scale)),
                                       ctrl, shift, key, repeat, keysum)

    def enterEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        self._setEventInformation(self.__saveX, self.__saveY,
                                  ctrl, shift, chr(0), 0, None)
        self._Iren.EnterEvent()

    def leaveEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        self._setEventInformation(self.__saveX, self.__saveY,
                                  ctrl, shift, chr(0), 0, None)
        self._Iren.LeaveEvent()

    def mousePressEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        repeat = 0
        if ev.type() == QEvent.MouseButtonDblClick:
            repeat = 1
        self._setEventInformation(ev.x(), ev.y(),
                                  ctrl, shift, chr(0), repeat, None)

        self._ActiveButton = ev.button()

        if self._ActiveButton == Qt.LeftButton:
            self._Iren.LeftButtonPressEvent()
        elif self._ActiveButton == Qt.RightButton:
            self._Iren.RightButtonPressEvent()
        elif self._ActiveButton == Qt.MidButton:
            self._Iren.MiddleButtonPressEvent()

    def mouseReleaseEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        self._setEventInformation(ev.x(), ev.y(),
                                  ctrl, shift, chr(0), 0, None)

        if self._ActiveButton == Qt.LeftButton:
            self._Iren.LeftButtonReleaseEvent()
        elif self._ActiveButton == Qt.RightButton:
            self._Iren.RightButtonReleaseEvent()
        elif self._ActiveButton == Qt.MidButton:
            self._Iren.MiddleButtonReleaseEvent()

    def mouseMoveEvent(self, ev):
        self.__saveModifiers = ev.modifiers()
        self.__saveButtons = ev.buttons()
        self.__saveX = ev.x()
        self.__saveY = ev.y()

        ctrl, shift = self._GetCtrlShift(ev)
        self._setEventInformation(ev.x(), ev.y(),
                                  ctrl, shift, chr(0), 0, None)
        self._Iren.MouseMoveEvent()

    def keyPressEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        if ev.key() < 256:
            key = str(ev.text())
        else:
            key = chr(0)

        keySym = _qt_key_to_key_sym(ev.key())
        if shift and len(keySym) == 1 and keySym.isalpha():
            keySym = keySym.upper()

        self._setEventInformation(self.__saveX, self.__saveY,
                                  ctrl, shift, key, 0, keySym)
        self._Iren.KeyPressEvent()
        self._Iren.CharEvent()

    def keyReleaseEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        if ev.key() < 256:
            key = chr(ev.key())
        else:
            key = chr(0)

        self._setEventInformation(self.__saveX, self.__saveY,
                                  ctrl, shift, key, 0, None)
        self._Iren.KeyReleaseEvent()

    def wheelEvent(self, ev):
        if hasattr(ev, 'delta'):
            self.__wheelDelta += ev.delta()
        else:
            self.__wheelDelta += ev.angleDelta().y()

        if self.__wheelDelta >= 120:
            self._Iren.MouseWheelForwardEvent()
            self.__wheelDelta = 0
        elif self.__wheelDelta <= -120:
            self._Iren.MouseWheelBackwardEvent()
            self.__wheelDelta = 0

    def GetRenderWindow(self):
        return self._RenderWindow

    def Render(self):
        self.update()
Ejemplo n.º 35
0
class MainWindow(QtWidgets.QWidget):
    def __init__(self, event_list):
        QtWidgets.QWidget.__init__(self)
        self.left = 0
        self.top = 0
        self.width = args.width
        self.height = args.height
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.setWindowTitle('一锤定音')



        # variables initialization
        self.all_event_list=event_list
        self.passed_days = 0
        self.page = 0
        self.start_time = datetime.strptime('20110301', '%Y%m%d')


        self.step=0

        self.total_times = args.total_times
        self.tolerance = args.tolerance
        self.hit_times=0
        self.cnt=0



        # progress bar initialization
        self.pbar = QProgressBar(self)
        self.pbar_width = self.width * 0.95
        self.pbar_height = 20
        self.pbar_left = (self.width - self.pbar_width) / 2
        self.pbar_top = self.height * 0.4
        self.pbar_max = 250
        self.pbar.setGeometry(self.pbar_left, self.pbar_top, self.pbar_width, self.pbar_height)
        self.pbar.setRange(0, self.pbar_max)


        # textedits and polylines initialization
        self.textedit_list=[]
        self.points_list = []
        self.textedit_height = self.height * 0.3
        self.textedit_num = 5
        self.base_html = "<html><body><h2>h2_placeholder</h2><p>p_placeholder</p></body></html>"
        for i in range(self.textedit_num):
            textedit = QTextEdit(self)
            textedit.setGeometry(self.left + self.width / self.textedit_num * i, self.top,
                self.width / self.textedit_num, self.textedit_height)
            self.textedit_list.append(textedit)
        # self.nextPage()


        # push button initialization
        self.start_button = QPushButton('Start', self)
        self.start_button.setFocusPolicy(Qt.NoFocus)
        self.button_width = 100
        self.button_height = 50
        self.start_button_left = self.width*0.4
        self.start_button_top = self.height * 0.5
        self.start_button.setGeometry(self.start_button_left, self.start_button_top, self.button_width,
            self.button_height)

        self.reset_button = QPushButton('Reset', self)
        self.reset_button.setFocusPolicy(Qt.NoFocus)
        self.reset_button_left = self.start_button_left
        self.reset_button_top = self.height * 0.7
        self.reset_button.setGeometry(self.reset_button_left, self.reset_button_top, self.button_width,
            self.button_height)

        # label initialization
        self.label1=QLabel(self)
        self.label_width = 400
        self.label_height=50
        self.label1_left=self.width*0.7
        self.label1_top=self.height*0.5
        self.label1.setGeometry(self.label1_left,self.label1_top,self.label_width,self.label_height)
        # self.label1.setText("你击中了{}次".format(self.cnt))

        self.label2=QLabel(self)
        self.label2_left=self.width*0.7
        self.label2_top=self.height*0.7
        self.label2.setGeometry(self.label2_left,self.label2_top,self.label_width,self.label_height)
        # self.label2.setText("你还有{}次机会".format(self.total_times-self.hit_times))

        self.label3 = QLabel(self)
        self.label3_left = self.width * 0.7
        self.label3_top = self.height * 0.6
        self.label3.setGeometry(self.label3_left, self.label3_top, self.label_width, self.label_height)
        # self.label3.setText("上次你距离最近的事件只差了{}天".format('x'))

        self.label4 = QLabel(self)
        self.label4_left = self.width * 0.1
        self.label4_top = self.height * 0.5
        self.label4.setGeometry(self.label4_left, self.label4_top, self.label_width, self.label_height * 2)
        self.label4.setText("11-14次准确停下时光机可以获得1个章\n\n15-18次准确停下时光机可以获得2个章\n\n19-25次准确停下时光机可以获得3个章")


        # timer initialization
        self.timer = QTimer()
        self.timeout_interval = args.timeout_interval




        # connect signals and slots
        self.start_button.clicked.connect(self.hit)
        self.reset_button.clicked.connect(self.reset)
        self.timer.timeout.connect(self.stepPlus)

        self.show()
        self.reset()
        # QMessageBox.information(self, "游戏规则", "本游戏你一共有{}次按钮机暂停时间的机会,当击中的日期和最近的事件日期相差{}天以内时,即算击中,总共有{}个事件,最后将根据击中事件数的多少发放奖励".format(self.total_times,self.tolerance,len(self.all_event_list)), QMessageBox.Ok)


    def keyPressEvent(self, QKeyEvent):
        if QKeyEvent.key()==Qt.Key_Return:
            self.hit()
        elif QKeyEvent.key()==Qt.Key_Escape:
            self.close()

    def paintEvent(self, QPaintEvent):
        painter = QPainter()
        painter.begin(self)
        pen = QPen(Qt.black, 2, Qt.SolidLine)
        painter.setPen(pen)
        for points in self.points_list:
            painter.drawPolyline(QPolygon(points))
        painter.end()


    def stepPlus(self):
        if self.step>self.pbar_max:
            if self.page < 5:
                self.step = 0
                self.passed_days += self.days_this_page
                self.nextPage()
            else:
                self.timer.stop()
                self.start_button.setText('Start')
                QMessageBox.information(self, "",
                    "时光机回到了现在,本次游戏中你一共成功了{}次,学院的发展离不开大家的支持,让我们一起努力吧,也许有一天你也能成为这些进展的主角!".format(self.cnt),
                    QMessageBox.Ok)
                self.reset()
        else:
            self.step = self.step + 1
        self.pbar.setValue(self.step)
        now = self.start_time + timedelta(int(self.step / self.pbar_max * self.days_this_page + self.passed_days))
        now_str = now.strftime("%Y{}%m{}%d{}").format('年', '月', '日')
        self.pbar.setFormat(now_str)


    def hit(self):
        if self.timer.isActive():
            if self.hit_times < self.total_times:
                self.hit_times += 1
                self.timer.stop()
                self.start_button.setText('Start')
                self.testHit(int(self.pbar.value() / self.pbar_max * self.days_this_page + self.passed_days))
                self.updateLabel()
            if self.hit_times == self.total_times:
                QMessageBox.information(self, "",
                    "时光机回到了现在,本次游戏中你一共成功了{}次,学院的发展离不开大家的支持,让我们一起努力吧,也许有一天你也能成为这些进展的主角!".format(self.cnt),
                    QMessageBox.Ok)
                self.reset()
        else:
            self.timer.start(self.timeout_interval)
            self.start_button.setText('Stop')



    def reset(self):
        self.timer.stop()
        self.start_button.setText('Start')
        self.pbar.reset()
        self.step = 0
        self.hit_times=0
        self.cnt=0
        self.page = 0
        self.passed_days = 0
        self.nextPage()
        self.updateLabel()
        self.update()
        QMessageBox.information(self, "时光穿梭机-游戏规则",
            "过去的十年里,我们学院取得了一些进展,现在让我们一起坐着时光机来回顾一下吧。。。本游戏你一共有{}次暂停时光机的机会,当时光机停下的日期和最近的事件日期相差{}天以内时,即算成功1次,总共有{}个事件,最后将根据成功次数发放奖励".format(
                self.total_times, self.tolerance, len(self.all_event_list)), QMessageBox.Ok)

    def testHit(self, value):
        diffs=[]
        # print((self.start_time+timedelta(value)).strftime('%Y%m%d'))
        for event in self.event_list:
            days = (event.event_time - self.start_time).days
            diffs.append(abs(value-days))
        # print(diffs)
        if min(diffs) <= self.tolerance:
            self.label3.setText("上次你的时光机停在了正确的时间哦")
            self.cnt += 1
        else:
            self.label3.setText("上次你的时光机距离最近的事件只相差了{}天".format(min(diffs)))

    def nextPage(self):
        self.event_list = self.all_event_list[self.page * 5:(self.page + 1) * 5]
        self.days_this_page = int(((self.event_list[4].event_time - self.start_time).days - self.passed_days) * 1.01)
        self.points_list = []
        for i in range(len(self.event_list)):
            html = self.base_html.replace('h2_placeholder',
                datetime.strftime(self.event_list[i].event_time, "%Y{}%m{}%d{}").format('年', '月', '日'))
            html = html.replace('p_placeholder', self.event_list[i].event_name)
            self.textedit_list[i].setHtml(html)
            self.textedit_list[i].setReadOnly(True)
            days = (self.event_list[i].event_time - self.start_time).days - self.passed_days
            points = []
            points.append(
                QPoint(self.left + self.width / self.textedit_num * (i + 0.5), self.top + self.textedit_height))
            points.append(QPoint(self.left + self.width / self.textedit_num * (i + 0.5),
                self.pbar_top - (self.pbar_top - self.top - self.textedit_height) / (self.textedit_num + 1) * (i + 1)))
            points.append(QPoint(self.pbar_left + days / self.days_this_page * self.pbar_width,
                self.pbar_top - (self.pbar_top - self.top - self.textedit_height) / (self.textedit_num + 1) * (i + 1)))
            points.append(QPoint(self.pbar_left + days / self.days_this_page * self.pbar_width, self.pbar_top))
            self.points_list.append(points)
        self.update()
        self.page += 1

    def updateLabel(self):
        self.label1.setText("你已经{}次把时光机停在了正确的位置".format(self.cnt))
        self.label2.setText("你还有{}次暂停时光机的机会哦".format(self.total_times - self.hit_times))
Ejemplo n.º 36
0
class InviteSenderDialog(QDialog):
    done = pyqtSignal(QWidget)
    closed = pyqtSignal(QWidget)

    def __init__(self, gateway, gui, folder_names=None):
        super().__init__()
        self.gateway = gateway
        self.gui = gui
        self.folder_names = folder_names
        self.folder_names_humanized = humanized_list(folder_names, "folders")
        self.settings = {}
        self.pending_invites = []
        self.use_tor = self.gateway.use_tor

        self.setMinimumSize(500, 300)

        header_icon = QLabel(self)
        if self.folder_names:
            icon = QFileIconProvider().icon(
                QFileInfo(
                    self.gateway.get_magic_folder_directory(
                        self.folder_names[0]
                    )
                )
            )
        else:
            icon = QIcon(os.path.join(gateway.nodedir, "icon"))
            if not icon.availableSizes():
                icon = QIcon(resource("tahoe-lafs.png"))
        header_icon.setPixmap(icon.pixmap(50, 50))

        header_text = QLabel(self)
        if self.folder_names:
            header_text.setText(self.folder_names_humanized)
        else:
            header_text.setText(self.gateway.name)
        header_text.setFont(Font(18))
        header_text.setAlignment(Qt.AlignCenter)

        header_layout = QGridLayout()
        header_layout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding, 0), 1, 1
        )
        header_layout.addWidget(header_icon, 1, 2)
        header_layout.addWidget(header_text, 1, 3)
        header_layout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding, 0), 1, 4
        )

        self.subtext_label = QLabel(self)
        self.subtext_label.setFont(Font(10))
        self.subtext_label.setStyleSheet("color: grey")
        self.subtext_label.setWordWrap(True)
        self.subtext_label.setAlignment(Qt.AlignCenter)

        self.noise_label = QLabel()
        font = Font(16)
        font.setFamily("Courier")
        font.setStyleHint(QFont.Monospace)
        self.noise_label.setFont(font)
        self.noise_label.setStyleSheet("color: grey")

        self.noise_timer = QTimer()
        self.noise_timer.timeout.connect(
            lambda: self.noise_label.setText(b58encode(os.urandom(16)))
        )
        self.noise_timer.start(75)

        self.code_label = QLabel()
        self.code_label.setFont(Font(18))
        self.code_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
        self.code_label.hide()

        self.box_title = QLabel(self)
        self.box_title.setAlignment(Qt.AlignCenter)
        self.box_title.setFont(Font(16))

        self.box = QGroupBox()
        self.box.setAlignment(Qt.AlignCenter)
        self.box.setStyleSheet("QGroupBox {font-size: 16px}")

        self.copy_button = QToolButton()
        self.copy_button.setIcon(QIcon(resource("copy.png")))
        self.copy_button.setToolTip("Copy to clipboard")
        self.copy_button.setStyleSheet("border: 0px; padding: 0px;")
        self.copy_button.hide()

        box_layout = QGridLayout(self.box)
        box_layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, 0), 1, 1)
        box_layout.addWidget(self.noise_label, 1, 2)
        box_layout.addWidget(self.code_label, 1, 3)
        box_layout.addWidget(self.copy_button, 1, 4)
        box_layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, 0), 1, 5)

        self.close_button = QPushButton("Close and cancel invite")
        self.close_button.setAutoDefault(False)

        self.checkmark = QLabel()
        self.checkmark.setPixmap(Pixmap("green_checkmark.png", 32))
        self.checkmark.setAlignment(Qt.AlignCenter)
        self.checkmark.hide()

        self.tor_label = QLabel()
        self.tor_label.setToolTip(
            "This connection is being routed through the Tor network."
        )
        self.tor_label.setPixmap(Pixmap("tor-onion.png", 24))
        self.tor_label.hide()

        self.progress_bar = QProgressBar()
        self.progress_bar.setMaximum(2)
        self.progress_bar.setTextVisible(False)
        self.progress_bar.hide()

        layout = QGridLayout(self)
        layout.addItem(QSpacerItem(0, 0, 0, QSizePolicy.Expanding), 0, 0)
        layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, 0), 1, 1)
        layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, 0), 1, 2)
        layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, 0), 1, 3)
        layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, 0), 1, 4)
        layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, 0), 1, 5)
        layout.addLayout(header_layout, 1, 3)
        layout.addItem(QSpacerItem(0, 0, 0, QSizePolicy.Expanding), 2, 1)
        layout.addWidget(self.box_title, 3, 2, 1, 3)
        layout.addWidget(self.checkmark, 3, 3)
        layout.addWidget(
            self.tor_label, 4, 1, 1, 1, Qt.AlignRight | Qt.AlignVCenter
        )
        layout.addWidget(self.box, 4, 2, 1, 3)
        layout.addWidget(self.progress_bar, 4, 2, 1, 3)
        layout.addWidget(self.subtext_label, 5, 2, 1, 3)
        layout.addItem(QSpacerItem(0, 0, 0, QSizePolicy.Expanding), 6, 1)
        layout.addWidget(self.close_button, 7, 3)
        layout.addItem(QSpacerItem(0, 0, 0, QSizePolicy.Expanding), 8, 1)

        self.copy_button.clicked.connect(self.on_copy_button_clicked)
        self.close_button.clicked.connect(self.close)

        self.set_box_title("Generating invite code...")
        self.subtext_label.setText("Creating folder invite(s)...\n\n")

        if self.use_tor:
            self.tor_label.show()
            self.progress_bar.setStyleSheet(
                "QProgressBar::chunk {{ background-color: {}; }}".format(
                    TOR_PURPLE
                )
            )

        self.go()  # XXX

    def set_box_title(self, text):
        if sys.platform == "darwin":
            self.box_title.setText(text)
            self.box_title.show()
        else:
            self.box.setTitle(text)

    def on_copy_button_clicked(self):
        code = self.code_label.text()
        for mode in get_clipboard_modes():
            set_clipboard_text(code, mode)
        self.subtext_label.setText(
            "Copied '{}' to clipboard!\n\n".format(code)
        )

    def on_got_code(self, code):
        self.noise_timer.stop()
        self.noise_label.hide()
        self.set_box_title("Your invite code is:")
        self.code_label.setText(code)
        self.code_label.show()
        self.copy_button.show()
        if self.folder_names:
            if len(self.folder_names) == 1:
                abilities = 'download "{}" and modify its contents'.format(
                    self.folder_names[0]
                )
            else:
                abilities = "download {} and modify their contents".format(
                    self.folder_names_humanized
                )
        else:
            abilities = 'connect to "{}" and upload new folders'.format(
                self.gateway.name
            )
        self.subtext_label.setText(
            "Entering this code on another device will allow it to {}.\n"
            "This code can only be used once.".format(abilities)
        )

    def on_got_introduction(self):
        if sys.platform == "darwin":
            self.box_title.hide()
        self.box.hide()
        self.progress_bar.show()
        self.progress_bar.setValue(1)
        self.subtext_label.setText("Connection established; sending invite...")

    def on_send_completed(self):
        self.box.hide()
        self.progress_bar.show()
        self.progress_bar.setValue(2)
        self.checkmark.show()
        self.close_button.setText("Finish")
        if self.folder_names:
            target = self.folder_names_humanized
        else:
            target = self.gateway.name
        text = "Your invitation to {} was accepted".format(target)
        self.subtext_label.setText(
            "Invite successful!\n {} at {}".format(
                text, datetime.now().strftime("%H:%M")
            )
        )
        if get_preference("notifications", "invite") != "false":
            self.gui.show_message("Invite successful", text)

        if self.folder_names:
            for view in self.gui.main_window.central_widget.views:
                if view.gateway.name == self.gateway.name:
                    for folder in self.folder_names:
                        # Immediately tell the Model that there are at least 2
                        # members for this folder, i.e., that it is now shared
                        view.model().on_members_updated(folder, [None, None])

    def handle_failure(self, failure):
        if failure.type == wormhole.errors.LonelyError:
            return
        logging.error(str(failure))
        show_failure(failure, self)
        self.invite_sender.cancel()
        self.close()

    def on_created_invite(self):
        self.subtext_label.setText("Opening wormhole...\n\n")

    def go(self):
        self.invite_sender = InviteSender(self.use_tor)
        self.invite_sender.created_invite.connect(self.on_created_invite)
        self.invite_sender.got_code.connect(self.on_got_code)
        self.invite_sender.got_introduction.connect(self.on_got_introduction)
        self.invite_sender.send_completed.connect(self.on_send_completed)
        self.invite_sender.send(self.gateway, self.folder_names).addErrback(
            self.handle_failure
        )

    def closeEvent(self, event):
        if self.code_label.text() and self.progress_bar.value() < 2:
            msg = QMessageBox(self)
            msg.setIcon(QMessageBox.Question)
            msg.setWindowTitle("Cancel invitation?")
            msg.setText(
                'Are you sure you wish to cancel the invitation to "{}"?'.format(
                    self.gateway.name
                )
            )
            msg.setInformativeText(
                'The invite code "{}" will no longer be valid.'.format(
                    self.code_label.text()
                )
            )
            msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            msg.setDefaultButton(QMessageBox.No)
            if msg.exec_() == QMessageBox.Yes:
                self.invite_sender.cancel()
                event.accept()
                self.closed.emit(self)
            else:
                event.ignore()
        else:
            event.accept()
            if self.noise_timer.isActive():
                self.noise_timer.stop()
            self.closed.emit(self)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()
Ejemplo n.º 37
0
class NetworkSDRInterfacePlugin(SDRPlugin):
    NETWORK_SDR_NAME = "Network SDR"  # Display text for device combo box
    rcv_index_changed = pyqtSignal(
        int, int
    )  # int arguments are just for compatibility with native and grc backend
    show_proto_sniff_dialog_clicked = pyqtSignal()
    sending_status_changed = pyqtSignal(bool)
    sending_stop_requested = pyqtSignal()
    current_send_message_changed = pyqtSignal(int)

    class MyTCPHandler(socketserver.BaseRequestHandler):
        def handle(self):
            received = self.request.recv(4096)
            self.data = received
            while received:
                received = self.request.recv(4096)
                self.data += received
            #print("{} wrote:".format(self.client_address[0]))
            #print(self.data)
            if hasattr(self.server, "received_bits"):
                self.server.received_bits.append(
                    NetworkSDRInterfacePlugin.bytearray_to_bit_str(self.data))
            else:
                received = np.frombuffer(self.data, dtype=np.complex64)
                self.server.receive_buffer[self.server.current_receive_index:
                                           self.server.current_receive_index +
                                           len(received)] = received
                self.server.current_receive_index += len(received)

    def __init__(self, raw_mode=False):
        """

        :param raw_mode: If true, sending and receiving raw samples if false bits are received/sent
        """
        super().__init__(name="NetworkSDRInterface")
        self.client_ip = self.qsettings.value("client_ip",
                                              defaultValue="127.0.0.1",
                                              type=str)
        self.server_ip = ""

        self.client_port = self.qsettings.value("client_port",
                                                defaultValue=2222,
                                                type=int)
        self.server_port = self.qsettings.value("server_port",
                                                defaultValue=4444,
                                                type=int)

        self.receive_check_timer = QTimer()
        self.receive_check_timer.setInterval(250)
        # need to make the connect for the time in constructor, as create connects is called elsewhere in base class
        self.receive_check_timer.timeout.connect(self.__emit_rcv_index_changed)

        self.__is_sending = False
        self.__sending_interrupt_requested = False

        self.sending_repeats = 1  # only used in raw mode
        self.current_sent_sample = 0
        self.current_sending_repeat = 0

        self.raw_mode = raw_mode
        if self.raw_mode:
            # Take 60% of avail memory>
            num_samples = constants.SETTINGS.value(
                'ram_threshold', 0.6,
                float) * (psutil.virtual_memory().available / 8)
            self.receive_buffer = np.zeros(int(num_samples),
                                           dtype=np.complex64,
                                           order='C')
        else:
            self.received_bits = []

    @property
    def is_sending(self) -> bool:
        return self.__is_sending

    @is_sending.setter
    def is_sending(self, value: bool):
        if value != self.__is_sending:
            self.__is_sending = value
            self.sending_status_changed.emit(self.__is_sending)

    @property
    def received_data(self):
        if self.raw_mode:
            return self.receive_buffer[:self.current_receive_index]
        else:
            return self.received_bits

    @property
    def current_receive_index(self):
        if hasattr(self.server, "current_receive_index"):
            return self.server.current_receive_index
        else:
            return 0

    @current_receive_index.setter
    def current_receive_index(self, value):
        if hasattr(self.server, "current_receive_index"):
            self.server.current_receive_index = value
        else:
            pass

    def free_data(self):
        if self.raw_mode:
            self.receive_buffer = np.empty(0)
        else:
            self.received_bits[:] = []

    def create_connects(self):
        self.settings_frame.lineEditClientIP.setText(self.client_ip)
        self.settings_frame.spinBoxClientPort.setValue(self.client_port)
        self.settings_frame.spinBoxServerPort.setValue(self.server_port)

        self.settings_frame.lineEditClientIP.editingFinished.connect(
            self.on_linedit_client_ip_editing_finished)
        self.settings_frame.lineEditServerIP.editingFinished.connect(
            self.on_linedit_server_ip_editing_finished)
        self.settings_frame.spinBoxClientPort.editingFinished.connect(
            self.on_spinbox_client_port_editing_finished)
        self.settings_frame.spinBoxServerPort.editingFinished.connect(
            self.on_spinbox_server_port_editing_finished)

        self.settings_frame.lOpenProtoSniffer.linkActivated.connect(
            self.on_lopenprotosniffer_link_activated)

    def start_tcp_server_for_receiving(self):
        self.server = socketserver.TCPServer(
            (self.server_ip, self.server_port),
            self.MyTCPHandler,
            bind_and_activate=False)
        if self.raw_mode:
            self.server.receive_buffer = self.receive_buffer
            self.server.current_receive_index = 0
        else:
            self.server.received_bits = self.received_bits

        self.server.allow_reuse_address = True  # allow reusing addresses if the server is stopped and started again
        self.server.server_bind(
        )  # only necessary, because we disabled bind_and_activate above
        self.server.server_activate(
        )  # only necessary, because we disabled bind_and_activate above

        self.receive_check_timer.start()

        self.server_thread = threading.Thread(target=self.server.serve_forever)
        self.server_thread.daemon = True
        self.server_thread.start()

    def stop_tcp_server(self):
        if hasattr(self, "server"):
            self.server.shutdown()
        if hasattr(self, "server_thread"):
            self.server_thread.join()
        self.receive_check_timer.stop()

    def send_data(self, data) -> str:
        # Create a socket (SOCK_STREAM means a TCP socket)
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
                # Connect to server and send data
                sock.connect((self.client_ip, self.client_port))
                sock.sendall(data)
                return ""
        except Exception as e:
            return str(e)

    def send_raw_data(self, data: np.ndarray, num_repeats: int):
        byte_data = data.tostring()

        if num_repeats == -1:
            # forever
            rng = iter(int, 1)
        else:
            rng = range(0, num_repeats)

        for _ in rng:
            self.send_data(byte_data)
            self.current_sent_sample = len(data)
            self.current_sending_repeat += 1

    def __send_messages(self, messages, sample_rates):
        """

        :type messages: list of Message
        :type sample_rates: list of int
        :param sample_rates: Sample Rate for each messages, this is needed to calculate the wait time,
                             as the pause for a message is given in samples
        :return:
        """
        self.is_sending = True
        for i, msg in enumerate(messages):
            if self.__sending_interrupt_requested:
                break
            assert isinstance(msg, Message)
            wait_time = msg.pause / sample_rates[i]

            self.current_send_message_changed.emit(i)
            error = self.send_data(
                self.bit_str_to_bytearray(msg.encoded_bits_str))
            if not error:
                logger.debug("Sent message {0}/{1}".format(
                    i + 1, len(messages)))
                logger.debug(
                    "Waiting message pause: {0:.2f}s".format(wait_time))
                if self.__sending_interrupt_requested:
                    break
                time.sleep(wait_time)
            else:
                self.is_sending = False
                Errors.generic_error("Could not connect to {0}:{1}".format(
                    self.client_ip, self.client_port),
                                     msg=error)
                break
        logger.debug("Sending finished")
        self.is_sending = False

    def start_message_sending_thread(self, messages, sample_rates):
        """

        :type messages: list of Message
        :type sample_rates: list of int
        :param sample_rates: Sample Rate for each messages, this is needed to calculate the wait time,
                             as the pause for a message is given in samples
        :return:
        """
        self.__sending_interrupt_requested = False
        self.sending_thread = threading.Thread(target=self.__send_messages,
                                               args=(messages, sample_rates))
        self.sending_thread.daemon = True
        self.sending_thread.start()

    def start_raw_sending_thread(self):
        self.__sending_interrupt_requested = False
        self.sending_thread = threading.Thread(target=self.send_raw_data,
                                               args=(self.samples_to_send,
                                                     self.sending_repeats))
        self.sending_thread.daemon = True
        self.sending_thread.start()

    def stop_sending_thread(self):
        self.__sending_interrupt_requested = True
        self.sending_stop_requested.emit()

    @staticmethod
    def bytearray_to_bit_str(arr: bytearray) -> str:
        return "".join("{:08b}".format(a) for a in arr)

    @staticmethod
    def bit_str_to_bytearray(bits: str) -> bytearray:
        bits += "0" * ((8 - len(bits) % 8) % 8)
        return bytearray(
            (int(bits[i:i + 8], 2) for i in range(0, len(bits), 8)))

    def on_linedit_client_ip_editing_finished(self):
        ip = self.settings_frame.lineEditClientIP.text()
        self.client_ip = ip
        self.qsettings.setValue('client_ip', self.client_ip)

    def on_linedit_server_ip_editing_finished(self):
        # Does nothing, because field is disabled
        ip = self.settings_frame.lineEditServerIP.text()
        self.server_ip = ip
        self.qsettings.setValue('server_ip', self.server_ip)

    def on_spinbox_client_port_editing_finished(self):
        self.client_port = self.settings_frame.spinBoxClientPort.value()
        self.qsettings.setValue('client_port', str(self.client_port))

    def on_spinbox_server_port_editing_finished(self):
        self.server_port = self.settings_frame.spinBoxServerPort.value()
        self.qsettings.setValue('server_port', str(self.server_port))

    def __emit_rcv_index_changed(self):
        # for updating received bits in protocol sniffer
        if hasattr(self, "received_bits") and self.received_bits:
            self.rcv_index_changed.emit(
                0, 0
            )  # int arguments are just for compatibility with native and grc backend

    @pyqtSlot(str)
    def on_lopenprotosniffer_link_activated(self, link: str):
        if link == "open_proto_sniffer":
            self.show_proto_sniff_dialog_clicked.emit()
Ejemplo n.º 38
0
class SlidingStackedWidget(QStackedWidget):

    LEFT2RIGHT, RIGHT2LEFT, TOP2BOTTOM, BOTTOM2TOP, AUTOMATIC = range(5)
    clicked_release = pyqtSignal()

    def __init__(self, *args, **kwargs):
        super(SlidingStackedWidget, self).__init__(*args, **kwargs)
        self._pnow = QPoint(0, 0)
        # 动画速度
        self._speed = 500
        # 当前索引
        self._now = 0
        # 自动模式的当前索引
        self._current = 0
        # 下一个索引
        self._next = 0
        # 是否激活
        self._active = 0
        # 动画方向(默认是横向)
        self._orientation = Qt.Horizontal
        # 动画曲线类型
        self._easing = QEasingCurve.Linear
        # 初始化动画
        self._initAnimation()

    def setSpeed(self, speed=500):
        """设置动画速度
        :param speed:       速度值,默认值为500
        :type speed:        int
        """
        self._speed = speed

    @pyqtProperty(int, fset=setSpeed)
    def speed(self):
        return self._speed

    def setOrientation(self, orientation=Qt.Horizontal):
        """设置动画的方向(横向和纵向)
        :param orientation:    方向(Qt.Horizontal或Qt.Vertical)
        :type orientation:     http://doc.qt.io/qt-5/qt.html#Orientation-enum
        """
        self._orientation = orientation

    @pyqtProperty(int, fset=setOrientation)
    def orientation(self):
        return self._orientation

    def setEasing(self, easing=QEasingCurve.OutBack):
        """设置动画的曲线类型
        :param easing:    默认为QEasingCurve.OutBack
        :type easing:     http://doc.qt.io/qt-5/qeasingcurve.html#Type-enum
        """
        self._easing = easing

    @pyqtProperty(int, fset=setEasing)
    def easing(self):
        return self._easing

    def slideInNext(self):
        """滑动到下一页"""
        now = self.currentIndex()
        if now < self.count() - 1:
            self.slideInIdx(now + 1)
            self._current = now + 1

    def slideInPrev(self):
        """滑动到上一页"""
        now = self.currentIndex()
        if now > 0:
            self.slideInIdx(now - 1)
            self._current = now - 1

    def slideInIdx(self, idx, direction=4):
        """滑动到指定序号
        :param idx:               序号
        :type idx:                int
        :param direction:         方向,默认是自动AUTOMATIC=4
        :type direction:          int
        """
        if idx > self.count() - 1:
            direction = self.TOP2BOTTOM if self._orientation == Qt.Vertical else self.RIGHT2LEFT
            idx = idx % self.count()
        elif idx < 0:
            direction = self.BOTTOM2TOP if self._orientation == Qt.Vertical else self.LEFT2RIGHT
            idx = (idx + self.count()) % self.count()
        self.slideInWgt(self.widget(idx), direction)

    def slideInWgt(self, widget, direction):
        """滑动到指定的widget
        :param widget:        QWidget, QLabel, etc...
        :type widget:         QWidget Base Class
        :param direction:     方向
        :type direction:      int
        """
        if self._active:
            return
        self._active = 1
        _now = self.currentIndex()
        _next = self.indexOf(widget)
        if _now == _next:
            self._active = 0
            return

        w_now = self.widget(_now)
        w_next = self.widget(_next)

        # 自动判断方向
        if _now < _next:
            directionhint = self.TOP2BOTTOM if self._orientation == Qt.Vertical else self.RIGHT2LEFT
        else:
            directionhint = self.BOTTOM2TOP if self._orientation == Qt.Vertical else self.LEFT2RIGHT
        if direction == self.AUTOMATIC:
            direction = directionhint

        # 计算偏移量
        offsetX = self.frameRect().width()
        offsetY = self.frameRect().height()
        w_next.setGeometry(0, 0, offsetX, offsetY)

        if direction == self.BOTTOM2TOP:
            offsetX = 0
            offsetY = -offsetY
        elif direction == self.TOP2BOTTOM:
            offsetX = 0
        elif direction == self.RIGHT2LEFT:
            offsetX = -offsetX
            offsetY = 0
        elif direction == self.LEFT2RIGHT:
            offsetY = 0

        # 重新定位显示区域外部/旁边的下一个窗口小部件
        pnext = w_next.pos()
        pnow = w_now.pos()
        self._pnow = pnow

        # 移动到指定位置并显示
        w_next.move(pnext.x() - offsetX, pnext.y() - offsetY)
        w_next.show()
        w_next.raise_()

        self._animnow.setTargetObject(w_now)
        self._animnow.setDuration(self._speed)
        self._animnow.setEasingCurve(self._easing)
        self._animnow.setStartValue(QPoint(pnow.x(), pnow.y()))
        self._animnow.setEndValue(
            QPoint(offsetX + pnow.x(), offsetY + pnow.y()))

        self._animnext.setTargetObject(w_next)
        self._animnext.setDuration(self._speed)
        self._animnext.setEasingCurve(self._easing)
        self._animnext.setStartValue(
            QPoint(-offsetX + pnext.x(), offsetY + pnext.y()))
        self._animnext.setEndValue(QPoint(pnext.x(), pnext.y()))

        self._next = _next
        self._now = _now
        self._active = 1
        self._animgroup.start()

    def _initAnimation(self):
        """初始化当前页和下一页的动画变量"""
        # 当前页的动画
        self._animnow = QPropertyAnimation(self,
                                           propertyName=b'pos',
                                           duration=self._speed,
                                           easingCurve=self._easing)
        # 下一页的动画
        self._animnext = QPropertyAnimation(self,
                                            propertyName=b'pos',
                                            duration=self._speed,
                                            easingCurve=self._easing)
        # 并行动画组
        self._animgroup = QParallelAnimationGroup(
            self, finished=self.animationDoneSlot)
        self._animgroup.addAnimation(self._animnow)
        self._animgroup.addAnimation(self._animnext)

    def setCurrentIndex(self, index):
        # 覆盖该方法实现的动画切换
        # super(SlidingStackedWidget, self).setCurrentIndex(index)
        # 坚决不能调用上面的函数,否则动画失效
        self.slideInIdx(index)

    def setCurrentWidget(self, widget):
        # 覆盖该方法实现的动画切换
        super(SlidingStackedWidget, self).setCurrentWidget(widget)
        # 坚决不能调用上面的函数,否则动画失效
        self.setCurrentIndex(self.indexOf(widget))

    def mouseReleaseEvent(self, event):
        """ 点击事件(松开时生效) """
        if event.button() == Qt.LeftButton:
            self.clicked_release.emit()

    def animationDoneSlot(self):
        """动画结束处理函数"""
        # 由于重写了setCurrentIndex方法所以这里要用父类本身的方法
        #         self.setCurrentIndex(self._next)
        QStackedWidget.setCurrentIndex(self, self._next)
        w = self.widget(self._now)
        w.hide()
        w.move(self._pnow)
        self._active = 0

    def autoStop(self):
        """停止自动播放"""
        if hasattr(self, '_autoTimer'):
            self._autoTimer.stop()

    def autoStart(self, msec=3000):
        """自动轮播
        :param time: 时间, 默认3000, 3秒
        """
        if not hasattr(self, '_autoTimer'):
            self._autoTimer = QTimer(self, timeout=self._autoStart)
        self._autoTimer.stop()
        self._autoTimer.start(msec)

    def _autoStart(self):
        if self._current == self.count():
            self._current = 0
        self._current += 1
        self.setCurrentIndex(self._current)
Ejemplo n.º 39
0
class Simulation(QObject):
    init_move_done = pyqtSignal()
    make_move = pyqtSignal()
    drawDirection = pyqtSignal(int, int, int, int, int)

    def __init__(self, corrono, view, button, slider):
        super(QObject, self).__init__()
        self.scene = GraphicsScene()
        self.view = view
        self.view.setScene(self.scene)
        self.view.scale(.65, -.65)
        self.view.setRenderHint(QPainter.Antialiasing)
        self.view.setBackgroundBrush(QBrush(QPixmap(':/images/tufo.png')))
        self.view.setCacheMode(QGraphicsView.CacheBackground)
        self.view.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)
        self.view.setDragMode(QGraphicsView.NoDrag) #ScrollHandDrag)
        self.view.setResizeAnchor(QGraphicsView.AnchorViewCenter)
        self.view.setTransformationAnchor(QGraphicsView.AnchorViewCenter)
        self.view.setWindowTitle("Tempo di Palio III")

        self.slider = slider
        self.slider.setMinimum(0)
        self.slider.setMaximum(50)

        self.button = button
        self.button.clicked.connect(self.button_pushed)

        self.human = "Lupa"

        self.timer = QTimer()
        self.timer.setInterval(25)
        self.timer.timeout.connect(self.moveBarberi)

        self.scene.move_ended.connect(self.checkVelocities)
        self.scene.race_ended.connect(self.fineCorsa)
        self.scene.setting_direction.connect(self.setHumanDirection)

        self.drawDirection.connect(self.scene.drawDirection)

        self.mortaretto = QSound("mortaretto.wav")
        self.iBarbero = -1
        self.start = False
        #self.ordine = range(10)

        self.drawPista()

        # init mossa
        self.barberi = []
        for ic, c in enumerate(corrono):
            #if ic > 0:
                #continue
            # FIXME posizione dx casuale (-20, 20)
            self.barberi.append(Barbero(c, 50 - ic * 2, 1380 + ic * 43))
            if c == self.human:
                self.barberi[-1].isHuman = True
            #self.barberi.append(Barbero(corrono[2], 747.0, 1325)) #1250.0)) #360.0, 850.0))
            self.scene.addItem(self.barberi[-1])
            #self.barberi.append(Barbero(corrono[3], 687.0, 1300))  # 1250.0)) #360.0, 850.0))
            #self.scene.addItem(self.barberi[-1])

    @pyqtSlot()
    def button_pushed(self):
        self.scene.humanPlaying = True
        self.scene.removeLine()
        self.scene.removeHighlight()
        self.button.setEnabled(False)
        self.barberi[self.iBarbero].initKinematics(self.human_direction, self.slider.value())
        self.timer.start()

    @pyqtSlot(int, int)
    def setHumanDirection(self, x, y):
        dx = x - self.barberi[self.iBarbero].pos.x()
        dy = y - self.barberi[self.iBarbero].pos.y()
        base = 5
        self.human_direction = base * round(math.degrees(math.atan2(dy, dx))/5)
        smax, lrange, hrange = self.barberi[self.iBarbero].findTarget(self.sectors)

        if lrange < self.human_direction < hrange:
            isok = 1
            self.button.setEnabled(True)
        else:
            isok = -1
            self.button.setEnabled(False)

        self.drawDirection.emit(self.barberi[self.iBarbero].pos.x(),
                                self.barberi[self.iBarbero].pos.y(),
                                x, y, isok)

    def drawPista(self):
        self.sectors = []
        for s in json.load(open("pista.json")):
            self.sectors.append(Sector(**s))
        # FIXME to remove after testing
        self.scene.setPista(self.sectors)

        self.borders = []
        tmp = json.load(open("bordi.json"))

        for b in tmp:
            border = Border(*b)
            self.scene.addItem(border)
            self.borders.append(border)

    @pyqtSlot()
    def run(self):
        if not self.start:
            return
        # FIXME aggiungere penalty casuale alle mossa
        # dipendente dalla prontezza del fantino
        # alla rincorsa niente per definizione
        self.iBarbero += 1
        if self.iBarbero == len(self.barberi):
            self.iBarbero = 0
            #sorted(self.barberi, key=compare)
            self.barberi = sorted(self.barberi, key=cmp_to_key(compare), reverse = True)
            print ("ordinamento ")
            for b in self.barberi:
                print (b.id, b.currentSector, b.distToTarget())

        # cambia vista per centrarla
        # x0 = max(0, self.barberi[self.iBarbero].pos.x() - 235)
        # x1 = x0 + 470
        # y1 = min(1800, self.barberi[self.iBarbero].pos.y() + 205)
        # y0 = y1 - 405
        #print (x0, x1, y0, y1)
        # if x0 < 0:
        #     x0 = 0
        #     x1 = 470
        # elif x1 > self.scene.xlim:
        #     x1 = self.scene.xlim
        #     x0 = x1 - 470
        #
        # if y0 < 0:
        #     y0 = 0
        #     y1 = 410
        # elif y1 > self.scene.ylim:
        #     y1 = self.scene.ylim
        #     y0 = y1 - 410
        #self.scene.setSceneRect(x0, y0, x1, y1)

        # scegli il path migliore o definisci velocita direzione se umano
        if not self.barberi[self.iBarbero].isHuman:
            self.barberi[self.iBarbero].chooseMove(self.iBarbero, self.sectors, self.borders)
            self.timer.start()
        else:
            self.scene.humanPlaying = True
            self.scene.highlightPlayer(self.barberi[self.iBarbero].pos.x(),
                                       self.barberi[self.iBarbero].pos.y())
            self.button.setEnabled(True)

    @pyqtSlot(str)
    def fineCorsa(self, vincitore):
        self.timer.stop()
        print ("Il Palio e` stato vinto da {}".format(vincitore))
        #sound = QSound("mortaretto.wav")
        self.mortaretto.play()
        # raccogli informazioni necessarie e passale a chi deve aggioranre statistiche
        # ovvero al manager
        # vittorie contrada, fantino, cavallo
        # eventuali aggiornamenti a fantini cavalli e contrade (infortuni, miglioramenti...)
        # partiti fatti e mantenuti

    @pyqtSlot()
    def moveBarberi(self):
        for it in self.scene.items():
            if it.type() == QGraphicsItem.UserType + 2:
                it.move(self.sectors)

    @pyqtSlot()
    def checkVelocities(self):
        print ("CHECK SPEED")
        move = False
        for b in self.barberi:
            #print ("Vel:", b.v.length())
            if b.v.length() > 0.1:
                move = True
                break

        if not move:
            self.timer.stop()
            print (self.barberi[self.iBarbero].pos)
Ejemplo n.º 40
0
class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # Fix so that image shows when run from python or bundled .exe
        path_to_logo = os.path.abspath(os.path.join(os.path.dirname(__file__), 'images/logo.png'))
        self.ui.logoImg.setPixmap(QtGui.QPixmap(path_to_logo))

        self.data = dict()

        # How long is each round in seconds
        self.roundTime = 3

        self.progressMaxValue = 1000000
        self.gameTimer = QTimer()

        # Ease the progress bar to give more tension to the player making a choice
        self.pbar_animation = QPropertyAnimation(self.ui.gameTime, b"value")
        self.pbar_animation.setEasingCurve(QEasingCurve.InCubic)
        self.pbar_animation.setDuration(self.roundTime * 1000)
        self.pbar_animation.setStartValue(self.progressMaxValue)
        self.pbar_animation.setEndValue(0)

        self.ui.gameTime.setMaximum(self.progressMaxValue)
        self.gameTimer.timeout.connect(self.handle_gameTime)
        self.ui.startGame.clicked.connect(lambda: self.start_game())
        self.ui.btnRock.clicked.connect(lambda: self.handle_choice("r"))
        self.ui.btnPaper.clicked.connect(lambda: self.handle_choice("p"))
        self.ui.btnScissors.clicked.connect(lambda: self.handle_choice("s"))
        self.ui.btnLizard.clicked.connect(lambda: self.handle_choice("li"))
        self.ui.btnSpock.clicked.connect(lambda: self.handle_choice("sp"))

        # What choice beats what other choices according to the rules
        self.rulesDict = {"r": ['s', 'li'], "p": ['r', 'sp'], "s": ['p', 'li'], "sp": ['r', 's'], "li": ['p', 'sp']}
        self.choices = ['r', 'p', 's', 'li', 'sp']

    def start_game(self):
        """
        Starts game and creates/loads save file that contains the player's name, win/losses, and
        historical probability for "AI" choices.
        :return:
        """
        if self.ui.playerName.text() != "":
            self.ui.statusbar.showMessage("Good Luck!", 3000)
            self.ui.widgetStack.setCurrentIndex(1)

            self.gameTimer.start(self.roundTime * 1000)
            self.ui.playerLabel.setText(f"<h1>{self.ui.playerName.text()}</h2>")

            self.pbar_animation.start()

            playerName = self.ui.playerName.text().lower()
            filename = playerName + ".dat"

            if os.path.exists(filename):
                infile = open(filename, 'rb')
                logger.info(f"OPEN DAT:{filename}")
                self.data = pickle.load(infile)
                infile.close()
            else:
                outfile = open(filename, 'wb')
                self.data = {
                    "player": playerName,
                    "wins": 0,
                    "losses": 0,
                    "probability": [0.2, 0.2, 0.2, 0.2, 0.2]
                }
                pickle.dump(self.data, outfile)
                outfile.close()

            self.update_data()
            self.gameMessage('<b>Welcome to RPSLiSp!!!</b>')
            self.gameMessage('Time is ticking, choose your option!')
            self.gameMessage('========================================')

            logger.info("START GAME")
        else:
            self.ui.statusbar.showMessage("Your name is required!", 3000)
            logger.info("NAMED REQUIRED")

    def handle_gameTime(self):
        """
        Timer handler that awards the computer a point if the player doesnt act in time.
        :return:
        """
        self.gameMessage(" ")
        self.gameMessage('<b style="color:red">TIME UP!</b> Next round!')
        self.gameMessage("<b style='color:#E76F51'>===== COMPUTER WINS BY DEFAULT =====</b>")
        self.data["losses"] += 1
        self.update_data()
        self.pbar_animation.stop()
        self.pbar_animation.start()

    def reset_timer(self):
        self.gameTimer.stop()
        self.pbar_animation.stop()
        self.gameTimer.start(self.roundTime * 1000)
        self.pbar_animation.start()

    def handle_choice(self, playerChoice):
        value = choice_to_value(playerChoice)
        self.ui.statusbar.showMessage(f"{value}!", 3000)
        self.check_winner(playerChoice)

    def check_winner(self, playerChoice):
        computer_choice = choice(self.choices, 1, p=self.data["probability"])
        self.gameMessage(" ")
        self.gameMessage(
            f"{self.ui.playerName.text()}: <b style='color:#E9C46A'>{choice_to_value(playerChoice)}</b> vs "
            f"COMPUTER: <b style='color:#E9C46A'>{choice_to_value(computer_choice[0])}</b> ")

        if computer_choice[0] == playerChoice:
            self.gameMessage("<b style='color:#264653'>===== TIE! =====</b>")
        elif computer_choice[0] in self.rulesDict[playerChoice]:
            self.gameMessage("<b style='color:#2A9D8F'>===== PLAYER WINS =====</b>")
            self.data["wins"] += 1
            self.update_probability(playerChoice)
        else:
            self.gameMessage("<b style='color:#E76F51'>===== COMPUTER WINS =====</b>")
            self.data["losses"] += 1

        self.reset_timer()
        self.update_data()

    def update_probability(self, playerChoice):
        """
        The "AI" takes the choice the player made and increases the probability that it will pick
        a choice to beat the player next time as well as decrease the probability that it will pick
        a choice that would allow the player to beat it. Probability is fed into numpy.choice() for
        "random" selection.
        :param playerChoice:
        :return:
        """
        choiceIndex = {"r": 0, "p": 1, "s": 2, "li": 3, "sp": 4}
        oppositeRules = {"r": ['p', 'sp'], "p": ['s', 'li'], "s": ['r', 'sp'], "sp": ['p', 'li'], "li": ['s', 'r']}

        newProbabilityArray = self.data["probability"]

        for rule in oppositeRules[playerChoice]:
            logger.info(f"Increase rule: {rule} ")
            newProb = format(newProbabilityArray[choiceIndex[rule]] + 0.02, '.2f')
            if float(newProb) <= 0.98:
                newProbabilityArray[choiceIndex[rule]] = float(newProb)

        for rule in self.rulesDict[playerChoice]:
            logger.info(f"Decrease rule: {rule} ")
            newProb = format(newProbabilityArray[choiceIndex[rule]] - 0.02, '.2f')
            if float(newProb) >= 0.02:
                newProbabilityArray[choiceIndex[rule]] = float(newProb)

        # To prevent crash reset array when it gets out of bounds for numpy.choice()
        if sum(newProbabilityArray) > 1.0:
            newProbabilityArray = [0.2, 0.2, 0.2, 0.2, 0.2]

        logger.info(sum(newProbabilityArray))
        self.data["probability"] = newProbabilityArray
        logger.info(self.data["probability"])

    def gameMessage(self, message):
        self.ui.gameLog.append(message)

    def update_data(self):
        """
        Updates UI for player wins and losses
        :return:
        """
        self.ui.playerScore.display(self.data["wins"])
        self.ui.computerScore.display(self.data["losses"])

    def closeEvent(self, event: QCloseEvent) -> None:
        """
        Handle clean up after application is closed
        :param event:
        :return:
        """
        self.gameTimer.stop()
        if self.ui.playerName.text() != "":
            filename = self.data["player"] + ".dat"
            outfile = open(filename, 'wb')
            pickle.dump(self.data, outfile)
            outfile.close()
            logger.info(f"CLOSING: {filename}")
Ejemplo n.º 41
0
class MineSweeperGUI(superGUI.Ui_MainWindow):
    def __init__(self, MainWindow):
        file = r'media/background.mp3'
        pygame.mixer.init()
        pygame.mixer.music.load(file)
        pygame.mixer.music.play(-1, 0)
        self.row = 9
        self.column = 9
        self.mineNum = 10
        self.rank = 0
        self.finish = False
        self.mainWindow = MainWindow
        self.mainWindow.setWindowIcon(QIcon("media/mine.jpg"))
        self.mainWindow.setFixedSize(self.mainWindow.minimumSize())
        self.setupUi(self.mainWindow)
        self.mineLabel = []
        self.initMineArea()
        self.createMine()
        self.label_2.leftRelease.connect(self.gameStart)
        pixmap = QPixmap("media/underway.png")
        self.label_2.setPixmap(pixmap)
        self.label_2.setScaledContents(True)
        pe = QPalette()
        pe.setColor(QPalette.WindowText, Qt.red)  # 设置字体颜色
        self.label_3.setPalette(pe)
        self.label_3.setFont(QFont("Roman times", 15, QFont.Bold))
        self.label.setPalette(pe)
        self.label.setFont(QFont("Roman times", 15, QFont.Bold))
        self.label.setText(str(self.mineNum))
        self.timer = QTimer()
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.timeCount)
        self.timeStart = False

        # 绑定菜单栏事件
        self.action.triggered.connect(self.gameStart)
        self.action_B.triggered.connect(self.action_BEvent)
        self.action_I.triggered.connect(self.action_IEvent)
        self.action_E.triggered.connect(self.action_Event)
        self.action_C.triggered.connect(self.action_CEvent)
        self.action_T.triggered.connect(self.action_TEvent)
        self.action_R.triggered.connect(self.action_REvent)
        self.action_S_2.triggered.connect(self.action_SEvent)
        self.action_L_2.triggered.connect(self.action_LEvent)
        self.action_X_2.triggered.connect(QCoreApplication.instance().quit)
        self.actionChecked('B')  # 默认选择初级

    def outOfBorder(self, i, j):
        if i < 0 or i >= self.row or j < 0 or j >= self.column:
            return True
        return False

    def createMine(self):
        num = self.mineNum
        while num > 0:
            r = random.randint(0, self.row - 1)
            c = random.randint(0, self.column - 1)
            if self.mineLabel[r][c].num != -1:
                self.mineLabel[r][c].num = -1
                num -= 1
                for i in range(r - 1, r + 2):
                    for j in range(c - 1, c + 2):
                        if not self.outOfBorder(
                                i, j) and (self.mineLabel[i][j].num != -1):
                            self.mineLabel[i][j].num += 1

    def initMineArea(self):
        self.gridLayout.setSpacing(0)
        for i in range(0, self.row):
            self.mineLabel.append([])
            for j in range(0, self.column):
                label = mineLabel.mineLabel(i, j, 0, "")
                label.setMinimumSize(18, 18)
                label.setFrameShape(QtWidgets.QFrame.WinPanel)
                label.setFrameShadow(QtWidgets.QFrame.Raised)
                label.setAlignment(Qt.AlignCenter)

                # 绑定雷区点击事件
                label.leftPressed.connect(self.mineAreaLeftPressed)
                label.leftAndRightPressed.connect(
                    self.mineAreaLeftAndRightPressed)
                label.leftAndRightRelease.connect(
                    self.mineAreaLeftAndRightRelease)
                label.leftRelease.connect(self.mineAreaLeftRelease)
                label.rightRelease.connect(self.mineAreaRightRelease)

                self.mineLabel[i].append(label)
                self.gridLayout.addWidget(label, i, j)

    def timeCount(self):
        self.label_3.setText(str(int(self.label_3.text()) + 1))

    def setFontColor(self, i, j):
        if self.mineLabel[i][j].num == 1:
            self.mineLabel[i][j].setStyleSheet("color:blue;")
        elif self.mineLabel[i][j].num == 2:
            self.mineLabel[i][j].setStyleSheet("color:brown;")
        elif self.mineLabel[i][j].num == 3:
            self.mineLabel[i][j].setStyleSheet("color:red;")
        elif self.mineLabel[i][j].num == 4:
            self.mineLabel[i][j].setStyleSheet("color:purple;")
        elif self.mineLabel[i][j].num == 5:
            self.mineLabel[i][j].setStyleSheet("color:orange;")
        elif self.mineLabel[i][j].num == 6:
            self.mineLabel[i][j].setStyleSheet("color:pink;")
        elif self.mineLabel[i][j].num == 7:
            self.mineLabel[i][j].setStyleSheet("color:#00aa00;")
        elif self.mineLabel[i][j].num == 8:
            self.mineLabel[i][j].setStyleSheet("color:black;")

    def DFS(self, i, j, start0):
        if self.mineLabel[i][j].status == 0:
            self.mineLabel[i][j].status = 1
            self.mineLabel[i][j].setFrameShape(QtWidgets.QFrame.Panel)
            self.mineLabel[i][j].setFrameShadow(QtWidgets.QFrame.Sunken)
            if self.mineLabel[i][j].num > 0:
                if not self.timeStart:
                    self.timeStart = True
                    self.timer.start()
                self.setFontColor(i, j)
                self.mineLabel[i][j].setFont(
                    QFont("Roman times", 15, QFont.Bold))
                self.mineLabel[i][j].setText(str(self.mineLabel[i][j].num))
            if self.isGameFinished():
                self.gameWin()
            if (start0 and self.mineLabel[i][j].num
                    == 0) or (not start0 and self.mineLabel[i][j].num == 0):
                for r in range(i - 1, i + 2):
                    for c in range(j - 1, j + 2):
                        if not self.outOfBorder(r, c) and self.mineLabel[r][
                                c].status == 0 and self.mineLabel[r][
                                    c].num != -1:
                            self.DFS(r, c, start0)

    def mineAreaLeftRelease(self, i, j):
        if not self.finish:
            if self.mineLabel[i][j].num >= 0:
                self.DFS(i, j, self.mineLabel[i][j].num == 0)
                if self.isGameFinished():
                    self.gameWin()
            else:
                self.gameFailed()

    def mineAreaRightRelease(self, i, j):
        if not self.finish:
            if self.mineLabel[i][j].status == 0:
                pixmap = QPixmap("media/flag.jpg")
                self.mineLabel[i][j].setPixmap(pixmap)
                self.mineLabel[i][j].setScaledContents(True)
                self.mineLabel[i][j].status = 2
                self.label.setText(str(int(self.label.text()) - 1))
            elif self.mineLabel[i][j].status == 2:
                self.mineLabel[i][j].setPixmap(QPixmap("media/question.jpg"))
                self.mineLabel[i][j].status = 3
                self.label.setText(str(int(self.label.text()) + 1))
            elif self.mineLabel[i][j].status == 3:
                self.mineLabel[i][j].setPixmap(QPixmap(""))
                self.mineLabel[i][j].status = 0

    def mineAreaLeftPressed(self, i, j):
        if not self.finish:
            if self.mineLabel[i][j].status == 0:
                self.mineLabel[i][j].setFrameShape(QtWidgets.QFrame.Panel)
                self.mineLabel[i][j].setFrameShadow(QtWidgets.QFrame.Sunken)

    def mineAreaLeftAndRightPressed(self, i, j):
        if not self.finish:
            if self.mineLabel[i][j].status == 1:
                count = 0
                for r in range(i - 1, i + 2):
                    for c in range(j - 1, j + 2):
                        if not self.outOfBorder(r, c):
                            if self.mineLabel[r][
                                    c].status == 0 or self.mineLabel[r][
                                        c].status == 3:
                                self.mineLabel[r][c].setFrameShape(
                                    QtWidgets.QFrame.Panel)
                                self.mineLabel[r][c].setFrameShadow(
                                    QtWidgets.QFrame.Sunken)
                            elif self.mineLabel[r][c].status == 2:
                                count += 1
                return count == self.mineLabel[i][j].num
            else:
                return False

    def mineAreaLeftAndRightRelease(self, i, j):
        if not self.finish:
            if self.mineLabel[i][j].status == 1:
                if self.mineAreaLeftAndRightPressed(i, j):
                    Fail = False
                    for r in range(i - 1, i + 2):
                        for c in range(j - 1, j + 2):
                            if not self.outOfBorder(r, c):
                                if self.mineLabel[r][
                                        c].status == 0 or self.mineLabel[r][
                                            c].status == 3:
                                    if self.mineLabel[r][c].status == 3:
                                        self.mineLabel[r][c].setPixmap(
                                            QPixmap(""))
                                        self.mineLabel[r][c].setScaledContents(
                                            True)
                                        self.mineLabel[r][c].status = 0
                                    if self.mineLabel[r][c].num >= 0:
                                        self.DFS(r, c,
                                                 self.mineLabel[r][c].num == 0)
                                    else:
                                        Fail = True
                    if Fail:
                        self.gameFailed()
                else:
                    for r in range(i - 1, i + 2):
                        for c in range(j - 1, j + 2):
                            if not self.outOfBorder(r, c):
                                if self.mineLabel[r][
                                        c].status == 0 or self.mineLabel[r][
                                            c].status == 3:
                                    self.mineLabel[r][c].setFrameShape(
                                        QtWidgets.QFrame.WinPanel)
                                    self.mineLabel[r][c].setFrameShadow(
                                        QtWidgets.QFrame.Raised)

    def gameStart(self):
        for i in self.mineLabel:
            for j in i:
                self.gridLayout.removeWidget(j)
                sip.delete(j)
        self.label.setText(str(self.mineNum))
        pixmap = QPixmap("media/underway.png")
        self.label_2.setPixmap(pixmap)
        self.label_2.setScaledContents(True)
        self.label_3.setText("0")
        self.timeStart = False
        self.finish = False
        self.timer.stop()
        self.mineLabel.clear()
        self.mineLabel = []
        self.initMineArea()
        self.createMine()
        self.mainWindow.setMinimumSize(0, 0)
        self.mainWindow.resize(self.mainWindow.minimumSize())

    def gameFinished(self):
        for i in self.mineLabel:
            for j in i:
                if j.num == -1 or j.status == 2:
                    j.setFrameShape(QtWidgets.QFrame.Panel)
                    j.setFrameShadow(QtWidgets.QFrame.Sunken)
                    if j.num == -1 and j.status == 2:
                        pixmap = QPixmap("media/correct.jpg")
                    elif j.num == -1:
                        pixmap = QPixmap("media/mine.jpg")
                    else:
                        pixmap = QPixmap("media/mistake.jpg")
                    j.setPixmap(pixmap)
                    j.setScaledContents(True)
                j.status = 1
        self.timer.stop()
        self.finish = True

    def isGameFinished(self):
        for i in self.mineLabel:
            for j in i:
                if j.status == 0 and j.num != -1:
                    return False
        return True

    def gameWin(self):
        pixmap = QPixmap("media/win.png")
        self.label_2.setPixmap(pixmap)
        self.label_2.setScaledContents(True)
        self.gameFinished()
        try:
            with open("hero.txt") as file:
                data = file.readlines()
        except FileNotFoundError:
            with open("hero.txt", "w") as file2:
                file2.write("9999\n匿名\n9999\n匿名\n9999\n匿名")
            with open("hero.txt") as file3:
                data = file3.readlines()
        if self.rank < 3 and int(data[self.rank * 2].rstrip()) > int(
                self.label_3.text()):
            dic = ["初级", "中级", "高级"]
            s, ok = QInputDialog.getText(
                self.mainWindow, "已破纪录",
                "已破" + dic[int(self.rank)] + "记录,请留尊姓大名:", QLineEdit.Normal,
                "匿名")
            if ok and s.strip():
                data[self.rank * 2] = self.label_3.text() + "\n"
                data[self.rank * 2 + 1] = s + "\n"
                with open("hero.txt", "w") as file2:
                    for i in data:
                        file2.write(i)
                ui = heroDialog.Ui_Dialog()
                ui.Dialog.setModal(True)
                ui.Dialog.show()
                ui.Dialog.exec_()

    def gameFailed(self):
        pixmap = QPixmap("media/fail.png")
        self.label_2.setPixmap(pixmap)
        self.label_2.setScaledContents(True)
        self.gameFinished()

    def actionChecked(self, k):
        self.action_B.setChecked(False)
        self.action_I.setChecked(False)
        self.action_E.setChecked(False)
        self.action_C.setChecked(False)
        if k == 'B':
            self.action_B.setChecked(True)
        elif k == 'I':
            self.action_I.setChecked(True)
        elif k == 'E':
            self.action_E.setChecked(True)
        elif k == 'C':
            self.action_C.setChecked(True)

    def action_BEvent(self):
        self.actionChecked('B')
        self.row = 9
        self.column = 9
        self.mineNum = 10
        self.rank = 0
        self.gameStart()

    def action_IEvent(self):
        self.actionChecked('I')
        self.row = 16
        self.column = 16
        self.mineNum = 40
        self.rank = 1
        self.gameStart()

    def action_Event(self):
        self.actionChecked('E')
        self.row = 16
        self.column = 30
        self.mineNum = 99
        self.rank = 2
        self.gameStart()

    def action_CEvent(self):
        self.actionChecked('C')
        ui = selfDefinedParameter.Ui_Dialog(self.row, self.column,
                                            self.mineNum)
        ui.Dialog.setModal(True)
        ui.Dialog.show()
        ui.Dialog.exec_()
        if ui.alter:
            self.row = ui.row
            self.column = ui.column
            self.mineNum = ui.mineNum
            self.rank = 3
            self.gameStart()

    def action_TEvent(self):
        ui = heroDialog.Ui_Dialog()
        ui.Dialog.setModal(True)
        ui.Dialog.show()
        ui.Dialog.exec_()

    def action_REvent(self):
        Dialog = QtWidgets.QDialog()
        ui = rule.Ui_Dialog()
        ui.setupUi(Dialog)
        Dialog.show()
        Dialog.exec_()

    def action_SEvent(self):
        Dialog = QtWidgets.QDialog()
        ui = symbolDialog.SymbolDialog(Dialog)
        Dialog.show()
        Dialog.exec_()

    def action_LEvent(self):
        Dialog = QtWidgets.QDialog()
        ui = limit.Ui_Dialog()
        ui.setupUi(Dialog)
        Dialog.show()
        Dialog.exec_()
Ejemplo n.º 42
0
class GameMainWindow(object):
    def setup_ui(self, MainWindow):
        global in_progress
        global choose_cell_phase
        MainWindow.setObjectName("Draughts")
        MainWindow.resize(800, 600)
        MainWindow.setMinimumSize(QtCore.QSize(800, 600))
        MainWindow.setMaximumSize(QtCore.QSize(800, 600))
        MainWindow.setStyleSheet("")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.frame = QtWidgets.QFrame(self.centralwidget)
        self.frame.setGeometry(QtCore.QRect(0, 0, 181, 601))
        self.frame.setStyleSheet("QFrame {\n"
                                 "border-color:black;\n"
                                 "border-style:solid;\n"
                                 "border-width:5px;\n"
                                 "background-color:#FFFACD;\n"
                                 "}\n"
                                 "\n"
                                 "QPushButton {\n"
                                 "border-color:black;\n"
                                 "border-style:solid;\n"
                                 "background-color: #dcb35c;\n"
                                 "border-radius: 40px;\n"
                                 "border-width:3px;\n"
                                 "}\n"
                                 "\n"
                                 "QPushButton:hover {\n"
                                 "background-color: #ECE7BC;\n"
                                 "}\n"
                                 "\n"
                                 "QLabel{\n"
                                 "border-color:black;\n"
                                 "border-style:solid;\n"
                                 "border-width:5 5 5 5;\n"
                                 "}")
        self.label = QtWidgets.QLabel(self.frame)
        self.label.setGeometry(QtCore.QRect(0, 0, 181, 111))
        font = QtGui.QFont()
        font.setFamily("Verdana")
        font.setPointSize(12)
        font.setBold(True)
        font.setWeight(75)
        self.label.setFont(font)
        self.label.setTextFormat(QtCore.Qt.PlainText)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setWordWrap(True)
        self.restart = QtWidgets.QPushButton(self.frame)
        self.restart.setGeometry(QtCore.QRect(20, 160, 141, 81))
        font = QtGui.QFont()
        font.setFamily("Verdana")
        font.setPointSize(12)
        font.setBold(True)
        font.setWeight(75)
        self.restart.setFont(font)
        self.restart.setObjectName("restart")
        self.restart.pressed.connect(lambda: self.restart_pressed())
        self.widget = QtWidgets.QWidget(self.centralwidget)
        self.widget.setGeometry(QtCore.QRect(180, 0, 621, 601))
        self.gridLayout = QtWidgets.QGridLayout(self.widget)
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout.setSpacing(0)
        for vertical, horizontal in itertools.product(range(board.width),
                                                      range(board.height)):
            board_cell = BoardCell(vertical, horizontal)
            groupBox = QGroupBoxClickable(self.widget)
            image_label = QtWidgets.QLabel(groupBox)
            image_label.setGeometry(QtCore.QRect(0, 0, 80, 80))
            if (vertical + horizontal) % 2 == 0:
                image_label.setStyleSheet("background-color:#fdeda8;")
            else:
                image_label.setStyleSheet("background-color:#ba7b55;")
            event = functools.partial(self.group_clicked, board_cell)
            groupBox.clicked.connect(event)
            buttons[board_cell] = groupBox
            self.gridLayout.addWidget(groupBox, horizontal, vertical, 1, 1)
        self.dialog = UnclosableDialog(self.centralwidget)
        self.dialog.resize(400, 257)
        self.dialog.setMinimumSize(QtCore.QSize(400, 257))
        self.dialog.setMaximumSize(QtCore.QSize(400, 257))
        self.dialog.setModal(True)
        self.dialog.setWindowFlag(Qt.FramelessWindowHint)
        self.dialog.setStyleSheet("QPushButton {\n"
                                  "border-color:black;\n"
                                  "border-style:solid;\n"
                                  "background-color: #dcb35c;\n"
                                  "border-radius: 25px;\n"
                                  "border-width:3px;\n"
                                  "}\n"
                                  "QPushButton:hover {\n"
                                  "background-color: #ECE7BC;\n"
                                  "}\n"
                                  "QDialog {\n"
                                  "border-color:black;\n"
                                  "border-style:solid;\n"
                                  "border-width:5px;\n"
                                  "background-color:#FFFACD;\n"
                                  "}\n"
                                  "QFrame {\n"
                                  "border-color:black;\n"
                                  "border-style:solid;\n"
                                  "border-width:3px;\n"
                                  "}\n"
                                  "")
        self.frame = QtWidgets.QFrame(self.dialog)
        self.frame.setGeometry(QtCore.QRect(60, 80, 120, 80))
        self.frame.setStyleSheet("background-color: #dcb35c;\n"
                                 "border-width:3px;")
        self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.first_player_human = QtWidgets.QRadioButton(self.frame)
        self.first_player_human.setGeometry(QtCore.QRect(20, 20, 82, 17))
        self.first_player_human.setStyleSheet("border-width:0px;")
        self.first_player_human.setChecked(True)
        self.first_player_computer = QtWidgets.QRadioButton(self.frame)
        self.first_player_computer.setGeometry(QtCore.QRect(20, 50, 82, 17))
        self.first_player_computer.setStyleSheet("border-width:0px;")
        self.frame_2 = QtWidgets.QFrame(self.dialog)
        self.frame_2.setGeometry(QtCore.QRect(220, 80, 120, 80))
        self.frame_2.setStyleSheet("background-color: #dcb35c;\n"
                                   "border-width:3px;")
        self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised)
        self.second_player_human = QtWidgets.QRadioButton(self.frame_2)
        self.second_player_human.setGeometry(QtCore.QRect(20, 20, 82, 17))
        self.second_player_human.setStyleSheet("border-width:0px;")
        self.second_player_human.setChecked(True)
        self.second_player_computer = QtWidgets.QRadioButton(self.frame_2)
        self.second_player_computer.setGeometry(QtCore.QRect(20, 50, 82, 17))
        self.second_player_computer.setStyleSheet("border-width:0px;")
        self.push_button = QtWidgets.QPushButton(self.dialog)
        self.push_button.setGeometry(QtCore.QRect(140, 180, 121, 51))
        self.push_button.pressed.connect(lambda: self.dialog_pressed())
        self.dialog_first = QtWidgets.QLabel(self.dialog)
        self.dialog_first.setGeometry(QtCore.QRect(70, 50, 101, 21))
        self.dialog_first.setStyleSheet("font: 75 12pt \"Verdana\";\n"
                                        "border-width:0px;")
        self.dialog_second = QtWidgets.QLabel(self.dialog)
        self.dialog_second.setGeometry(QtCore.QRect(220, 50, 121, 21))
        self.dialog_second.setStyleSheet("font: 75 12pt \"Verdana\";\n"
                                         "border-width:0px;")
        self.dialog.show()
        self.timer = QTimer()
        in_progress = True
        MainWindow.setCentralWidget(self.centralwidget)
        self.retranslate_ui(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslate_ui(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("Draughts", "Draughts"))
        self.restart.setText(_translate("MainWindow", "Restart game"))
        self.first_player_human.setText(_translate("Dialog", "Human"))
        self.first_player_computer.setText(_translate("Dialog", "Computer"))
        self.second_player_human.setText(_translate("Dialog", "Human"))
        self.second_player_computer.setText(_translate("Dialog", "Computer"))
        self.push_button.setText(_translate("Dialog", "OK"))
        self.dialog_first.setText(
            _translate(
                "Dialog",
                "<html><head/><body><p><span style=\" font-size:12pt;\">First "
                "player</span></p></body></html>"))
        self.dialog_second.setText(
            _translate(
                "Dialog",
                "<html><head/><body><p>Second player</p></body></html>"))

    def group_clicked(self, board_cell: BoardCell):
        global choose_cell_phase
        global chosen_cell
        group = buttons[board_cell]
        image_label = group.children()[0]
        if in_progress:
            if choose_cell_phase:
                if board.cells[board_cell] is not None:
                    if board.cells[board_cell].side == board.turn:
                        image_label.setStyleSheet("background-color:#ac6539;")
                        choose_cell_phase = False
                        chosen_cell = board_cell
            else:
                turn = board.make_turn(chosen_cell, board_cell.vertical,
                                       board_cell.horizontal)
                if board.turn_phase != 2:
                    if turn is None:
                        buttons[chosen_cell].children()[0].setStyleSheet(
                            "background-color:#ba7b55;")
                        choose_cell_phase = True
                        chosen_cell = None
                    elif turn[1] is None:
                        image_label.setStyleSheet("background-color:#ba7b55;")
                        buttons[chosen_cell].children()[0].setStyleSheet(
                            "background-color:#ba7b55;")
                        self.update_board(chosen_cell)
                        self.update_board(board_cell)
                        choose_cell_phase = True
                        chosen_cell = None
                        current_turn = board.turn
                        for cell in marked_cells:
                            buttons[cell].children()[0].setStyleSheet(
                                "background-color:#ba7b55;")
                            buttons[cell].children()[0].repaint()
                        marked_cells.clear()
                        while board.turn == current_turn and in_progress and (
                                self.first_player_computer.isChecked()
                                or self.second_player_computer.isChecked()):
                            sleep(1)
                            self.make_computer_turn()
                if turn is not None and turn[1] is not None:
                    buttons[chosen_cell].children()[0].setStyleSheet(
                        "background-color:#ba7b55;")
                    self.update_board(chosen_cell)
                    self.update_board(turn[1])
                    self.update_board(turn[0])
                    if board.cells[board_cell].side == board.turn:
                        image_label.setStyleSheet("background-color:#ac6539;")
                        chosen_cell = board_cell
                    else:
                        choose_cell_phase = True
                        chosen_cell = None
                        current_turn = board.turn
                        for cell in marked_cells:
                            buttons[cell].children()[0].setStyleSheet(
                                "background-color:#ba7b55;")
                            buttons[cell].children()[0].repaint()
                        marked_cells.clear()
                        while board.turn == current_turn and in_progress and (
                                self.first_player_computer.isChecked()
                                or self.second_player_computer.isChecked()):
                            sleep(1)
                            self.make_computer_turn()

    def restart_pressed(self):
        if self.timer.isActive():
            self.timer.stop()
        self.dialog.show()

    def dialog_pressed(self):
        global in_progress
        global chosen_cell
        global choose_cell_phase
        if chosen_cell is not None:
            buttons[chosen_cell].children()[0].setStyleSheet(
                "background-color:#ba7b55;")
            choose_cell_phase = True
            chosen_cell = None
        board.fill_board()
        for cell in board.cells:
            self.update_board(cell)
        for cell in marked_cells:
            buttons[cell].children()[0].setStyleSheet(
                "background-color:#ba7b55;")
            buttons[cell].children()[0].repaint()
        marked_cells.clear()
        in_progress = True
        self.dialog.hide()
        if self.first_player_computer.isChecked(
        ) and self.second_player_human.isChecked():
            board.turn = Side.BLACK
            self.make_computer_turn()
        if self.first_player_computer.isChecked(
        ) and self.second_player_computer.isChecked():
            self.timer.timeout.connect(lambda: self.make_computer_turn())
            self.timer.start(1500)
        self.update_status()

    def make_computer_turn(self):
        sleep(1)
        if board.attr_story and board.turn != board.attr_story[
                len(board.attr_story) - 1][0]:
            for cell in marked_cells:
                buttons[cell].children()[0].setStyleSheet(
                    "background-color:#ba7b55;")
                buttons[cell].children()[0].repaint()
            marked_cells.clear()
        computer_turn = ComputerLogic.make_computer_turn(board)
        self.update_board(computer_turn[0])
        self.update_board(computer_turn[1][0])
        buttons[computer_turn[0]].children()[0].setStyleSheet(
            "background-color:#ac6539;")
        buttons[computer_turn[0]].children()[0].repaint()
        buttons[computer_turn[1][0]].children()[0].setStyleSheet(
            "background-color:#ac6539;")
        buttons[computer_turn[1][0]].children()[0].repaint()
        marked_cells.append(computer_turn[0])
        marked_cells.append(computer_turn[1][0])
        if computer_turn[1][1] is not None:
            self.update_board(computer_turn[1][1])
        if not in_progress and self.timer.isActive():
            self.timer.stop()

    def update_board(self, board_cell: BoardCell):
        checker = board.cells[board_cell]
        group = buttons[board_cell]
        is_inverted_sides = self.first_player_computer.isChecked(
        ) and self.second_player_human.isChecked()
        if len(group.children()) > 1:
            group.children()[len(group.children()) - 1].setParent(None)
            group.repaint()
        if checker is None:
            return
        elif (checker.side == Side.BLACK and not is_inverted_sides) or\
                (checker.side == Side.WHITE and is_inverted_sides):
            image_label = QtWidgets.QLabel(group)
            image_label.setGeometry(QtCore.QRect(10, 8, 60, 60))
            if checker.is_king:
                image_label.setPixmap(
                    QtGui.QPixmap(resource_path("Black_king.png")))
            else:
                image_label.setPixmap(
                    QtGui.QPixmap(resource_path("Black_checker.png")))
            group.children().append(image_label)
            image_label.show()
        elif (checker.side == Side.WHITE and not is_inverted_sides) or\
                (checker.side == Side.BLACK and is_inverted_sides):
            image_label = QtWidgets.QLabel(group)
            image_label.setGeometry(QtCore.QRect(10, 8, 60, 60))
            if checker.is_king:
                image_label.setPixmap(
                    QtGui.QPixmap(resource_path("White_king.png")))
            else:
                image_label.setPixmap(
                    QtGui.QPixmap(resource_path("White_checker.png")))
            group.children().append(image_label)
            image_label.show()
        self.centralwidget.repaint()
        self.update_status()

    def update_status(self):
        global in_progress
        winner = board.winner()
        is_inverted_sides = self.first_player_computer.isChecked(
        ) and self.second_player_human.isChecked()
        if winner == Side.BLACK:
            in_progress = False
            status_string = str(
                "Game status:\nBlack wins. Press restart to play again.")
        elif winner == Side.WHITE:
            in_progress = False
            status_string = str(
                "Game status:\nWhite wins. Press restart to play again.")
        elif (board.turn == Side.BLACK and not is_inverted_sides) or \
                (board.turn == Side.WHITE and is_inverted_sides) :
            status_string = str("Game status:\nBlacks turn.")
        else:
            status_string = str("Game status:\nWhites turn.")
        self.label.setText(status_string)
Ejemplo n.º 43
0
class OpencvWidget(QLabel):
    def __init__(self, *args, **kwargs):
        super(OpencvWidget, self).__init__(*args, **kwargs)
        self.httpRequestAborted = False
        self.fps = 24
        self.resize(800, 600)

        if not os.path.exists("Data/shape_predictor_68_face_landmarks.dat"):
            self.setText("正在下载数据文件。。。")
            self.outFile = QFile(
                "Data/shape_predictor_68_face_landmarks.dat.bz2")
            if not self.outFile.open(QIODevice.WriteOnly):
                QMessageBox.critical(self, '错误', '无法写入文件')
                return
            self.qnam = QNetworkAccessManager(self)
            self._reply = self.qnam.get(QNetworkRequest(QUrl(URL)))
            self._reply.finished.connect(self.httpFinished)
            self._reply.readyRead.connect(self.httpReadyRead)
            self._reply.downloadProgress.connect(self.updateDataReadProgress)
        else:
            self.startCapture()

    def httpFinished(self):
        self.outFile.close()
        if self.httpRequestAborted or self._reply.error():
            self.outFile.remove()
        self._reply.deleteLater()
        del self._reply
        # 下载完成解压文件并加载摄像头
        self.setText("正在解压数据。。。")
        try:
            bz = BZ2Decompressor()
            data = bz.decompress(
                open('Data/shape_predictor_68_face_landmarks.dat.bz2',
                     'rb').read())
            open('Data/shape_predictor_68_face_landmarks.dat',
                 'wb').write(data)
        except Exception as e:
            self.setText('解压失败:' + str(e))
            return
        self.setText('正在开启摄像头。。。')
        self.startCapture()

    def httpReadyRead(self):
        self.outFile.write(self._reply.readAll())
        self.outFile.flush()

    def updateDataReadProgress(self, bytesRead, totalBytes):
        self.setText('已下载:{} %'.format(round(bytesRead / 64040097 * 100, 2)))

    def startCapture(self):
        self.setText("请稍候,正在初始化数据和摄像头。。。")
        try:
            # 检测相关
            self.detector = dlib.get_frontal_face_detector()
            self.predictor = dlib.shape_predictor(
                "Data/shape_predictor_68_face_landmarks.dat")
            cascade_fn = "Data/lbpcascades/lbpcascade_frontalface.xml"
            self.cascade = cv2.CascadeClassifier(cascade_fn)
            if not self.cascade:
                return QMessageBox.critical(self, "错误", cascade_fn + " 无法找到")
            self.cap = cv2.VideoCapture(0)
            if not self.cap or not self.cap.isOpened():
                return QMessageBox.critical(self, "错误", "打开摄像头失败")
            # 开启定时器定时捕获
            self.timer = QTimer(self, timeout=self.onCapture)
            self.timer.start(1000 / self.fps)
        except Exception as e:
            QMessageBox.critical(self, "错误", str(e))

    def closeEvent(self, event):
        if hasattr(self, "_reply") and self._reply:
            self.httpRequestAborted = True
            self._reply.abort()
            try:
                os.unlink("Data/shape_predictor_68_face_landmarks.dat.bz2")
            except:
                pass
            try:
                os.unlink("Data/shape_predictor_68_face_landmarks.dat")
            except:
                pass
        if hasattr(self, "timer"):
            self.timer.stop()
            self.timer.deleteLater()
            self.cap.release()
            del self.predictor, self.detector, self.cascade, self.cap
        super(OpencvWidget, self).closeEvent(event)
        self.deleteLater()

    def onCapture(self):
        _, frame = self.cap.read()

        minisize = (int(frame.shape[1] / DOWNSCALE),
                    int(frame.shape[0] / DOWNSCALE))
        tmpframe = cv2.resize(frame, minisize)
        tmpframe = cv2.cvtColor(tmpframe, cv2.COLOR_BGR2GRAY)  # 做灰度处理
        tmpframe = cv2.equalizeHist(tmpframe)

        # minNeighbors表示每一个目标至少要被检测到5次
        faces = self.cascade.detectMultiScale(tmpframe, minNeighbors=5)
        del tmpframe
        if len(faces) < 1:  # 没有检测到脸
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            img = QImage(frame.data, frame.shape[1], frame.shape[0],
                         frame.shape[1] * 3, QImage.Format_RGB888)
            del frame
            return self.setPixmap(QPixmap.fromImage(img))
        # 特征点检测描绘
        for x, y, w, h in faces:
            x, y, w, h = x * DOWNSCALE, y * DOWNSCALE, w * DOWNSCALE, h * DOWNSCALE
            # 画脸矩形
            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0))
            # 截取的人脸部分
            tmpframe = frame[y:y + h, x:x + w]
            # 进行特征点描绘
            rects = self.detector(tmpframe, 1)
            if len(rects) > 0:
                landmarks = numpy.matrix(
                    [[p.x, p.y]
                     for p in self.predictor(tmpframe, rects[0]).parts()])
                for _, point in enumerate(landmarks):
                    pos = (point[0, 0] + x, point[0, 1] + y)
                    # 在原来画面上画点
                    cv2.circle(frame, pos, 3, color=(0, 255, 0))
            # 转成Qt能显示的
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            img = QImage(frame.data, frame.shape[1], frame.shape[0],
                         frame.shape[1] * 3, QImage.Format_RGB888)
            del frame
            self.setPixmap(QPixmap.fromImage(img))
class ObjectPacker:
    def __init__(self, physics_plugin):
        self._physics_plugin = physics_plugin

        self._running = False

        self._message = None

        self._planes = []

        self._current_iteration = 0
        self._max_iterations = 100
        self._initial_plane_distance = -500
        self._min_plane_distance = -100

        self._timer = QTimer()
        self._timer.setInterval(20)
        self._timer.timeout.connect(self._update)

    def pack(self):
        if self._running:
            return

        self._message = Message("Packing Objects...",
                                lifetime=-1,
                                dismissable=False,
                                progress=100)
        self._message.show()

        Application.getInstance().getController().setToolsEnabled(False)

        plane = ode.GeomPlane(self._physics_plugin.space, (1, 0, 0),
                              self._initial_plane_distance)
        self._planes.append(plane)

        plane = ode.GeomPlane(self._physics_plugin.space, (-1, 0, 0),
                              self._initial_plane_distance)
        self._planes.append(plane)

        plane = ode.GeomPlane(self._physics_plugin.space, (0, 0, 1),
                              self._initial_plane_distance)
        self._planes.append(plane)

        plane = ode.GeomPlane(self._physics_plugin.space, (0, 0, -1),
                              self._initial_plane_distance)
        self._planes.append(plane)

        for node in DepthFirstIterator(Application.getInstance().getController(
        ).getScene().getRoot()):
            if node.getDecorator(RigidBodyDecorator.RigidBodyDecorator):
                random_position = Vector(
                    random.randrange(self._initial_plane_distance,
                                     -self._initial_plane_distance), 0,
                    random.randrange(self._initial_plane_distance,
                                     -self._initial_plane_distance))
                node.setPosition(random_position)

        self._timer.start()

    def _update(self):
        for plane in self._planes:
            params = plane.getParams()
            distance = max(
                self._min_plane_distance, self._initial_plane_distance -
                (self._initial_plane_distance *
                 (self._current_iteration / self._max_iterations)))
            plane.setParams(params[0], distance)

        self._current_iteration += 1
        if self._current_iteration > self._max_iterations:
            self._timer.stop()
            self._message.hide()
            Application.getInstance().getController().setToolsEnabled(True)

            for plane in self._planes:
                self._physics_plugin.space.remove(plane)
            self._planes = []

            self._current_iteration = 0
        else:
            self._message.setProgress(
                (self._current_iteration / self._max_iterations) * 100)
Ejemplo n.º 45
0
class GUIBoard(QFrame):
    """


    """
    # Class Constant
    BOARD_SIZE = 500

    # signal

    turn_change = pyqtSignal(int)
    is_game_over = pyqtSignal(bool)

    def __init__(self):
        """盤面の初期化。完全ランダムで埋める.Playerの選択も"""
        super().__init__()

        self.init_ui()
        self.table_size = 4
        self.init_board()

    def init_board(self):

        self.Board = CBoard.Board(table_size=self.table_size)
        self.Board.init_board(max_num=3)
        self.is_paused = True
        self.resize(GUIBoard.BOARD_SIZE, GUIBoard.BOARD_SIZE)
        self.Board_drawn = self.Board
        self.drop_timer = QTimer(self)
        self.drop_timer.setInterval(200)
        self.is_game_over.emit(False)
        self.drop_timer.timeout.connect(self.draw_dropped)

    def step(self):
        """step one turn"""
        if not self.is_paused:  # ポーズ中でなければすすめる
            if not self.Board.is_game_end():
                # self.timer.stop()
                next_c = self.player.next_cell(self.Board)

                # draw before_drop
                self.Board_drawn = self.Board.select_cell(
                    next_c, return_board_before_drop=True)

                # self.timer.start()
                self.update()
                # draw after_drop

                self.drop_timer.start()

            else:

                self.is_paused = True
                self.is_game_over.emit(True)

    def draw_dropped(self):
        """Boardをおとした後の盤面を表示する"""
        self.drop_timer.stop()
        self.Board_drawn = self.Board
        self.update()
        self.turn_change.emit(self.Board.get_turn_num())
        QTimer.singleShot(100, self.step)

    def start(self):
        """AIをスタートする"""
        if self.Board.is_game_end():
            self.init_board()
        self.is_paused = False
        QTimer.singleShot(100, self.step)

    def pause(self):
        """AIをストップする"""
        self.is_paused = True

    def mousePressEvent(self, event):
        """when mouse clicked"""
        self.mouse_step(event.x(), event.y())

    def mouse_step(self, x, y):
        """select cell which is clicked by mouse"""
        # get number of clicked cell
        cell = -1
        if not self.Board.is_game_end():
            cell_size = self._get_cell_size()
            table_size = self.Board.get_table_size()
            for i in range(table_size):
                for j in range(table_size):
                    if j * cell_size <= x < (j + 1) * cell_size:
                        if i * cell_size <= y < (i + 1) * cell_size:
                            cell = i * self.Board.get_table_size() + j
            # if cell is selectable
            if cell in self.Board.selectable_list():
                next_c = cell
                self.Board_drawn = self.Board.select_cell(
                    next_c, return_board_before_drop=True)

                if not self.Board.is_game_end():
                    self.drop_timer.start()
                    self.update()
                else:
                    self.drop_timer.start()
                    self.update()
                    self.is_game_over.emit(True)

    def init_ui(self):
        """GUIBoardのUIの初期化"""
        self.resize(GUIBoard.BOARD_SIZE, GUIBoard.BOARD_SIZE)

    def _get_cell_size(self):
        """それぞれのマスの大きさをピクセル数で返す"""
        return GUIBoard.BOARD_SIZE // self.Board.get_table_size()

    def paintEvent(self, event):

        self._draw_board(self.Board_drawn)

    def _draw_a_cell(self, painter, i, j, value):
        """Draw cell(i, j), value is the number of the cell"""
        color_table = [
            "white", "#01bfa6", "#0b9cdb", "#ff5d1a", "#ffa81b", "#f22c43",
            "#7b50ff", "#e33b92", "black", "green", "blue", "orange", "red",
            "indigo", "peru"
        ]

        color = QColor(color_table[value])
        painter.fillRect(j * self._get_cell_size(), i * self._get_cell_size(),
                         self._get_cell_size(), self._get_cell_size(), color)

        font = QFont("Times", 0.20 * self._get_cell_size())
        painter.setFont(font)
        pen_color = QColor("white")
        painter.setPen(pen_color)
        painter.drawText(j * self._get_cell_size(), i * self._get_cell_size(),
                         self._get_cell_size() - 1,
                         self._get_cell_size() - 1, Qt.AlignCenter, str(value))

    def _draw_board(self, board):
        painter = QPainter(self)
        board_drawn = board.get_board()

        for i in range(self.Board.get_table_size()):
            for j in range(self.Board.get_table_size()):
                self._draw_a_cell(
                    painter, i, j,
                    board_drawn[i * self.Board.get_table_size() + j])
Ejemplo n.º 46
0
class LeftMenuPlaylist(QListWidget):
    """
    This class represents the menu with video files that is visible in the left menu.
    Only shows when a video is playing.
    """

    playing_item_change = pyqtSignal(int)  # file index
    list_loaded = pyqtSignal()
    item_should_play = pyqtSignal(
    )  # no info required, a double click always follow a click event

    def __init__(self, parent):
        QListWidget.__init__(self, parent)

        self.files_data = []
        self.loaded_list = False
        self.loading_list = False
        self.active_index = -1
        self.infohash = None
        self.itemClicked.connect(self.on_item_clicked)
        self.itemDoubleClicked.connect(self.on_item_double_clicked)

        self.files_request_mgr = None
        self.files_request_timer = None

    def set_loading(self):
        self.clear()
        self.addItem("Loading...")
        self.loaded_list = False
        self.loading_list = True

    def load_list(self, infohash):
        self.infohash = infohash
        self.set_loading()

        if self.files_request_timer:
            self.files_request_timer.stop()

        self.files_request_timer = QTimer()
        self.files_request_timer.timeout.connect(
            self.perform_get_files_request)
        self.files_request_timer.start(1000)

    def perform_get_files_request(self):
        self.files_request_mgr = TriblerRequestManager()
        self.files_request_mgr.perform_request("downloads/%s/files" %
                                               self.infohash,
                                               self.on_received_files,
                                               capture_errors=False)

    def on_received_files(self, files):
        if not files:
            return
        if "files" not in files or not files["files"]:
            return

        if self.files_request_timer:
            self.files_request_timer.stop()
            self.files_request_timer = None

        self.set_files(files["files"])
        self.loaded_list = True
        self.loading_list = False
        self.list_loaded.emit()

    def get_largest_file(self):
        largest_file = None
        largest_index = None
        for index, file_info in enumerate(self.files_data):
            if is_video_file(file_info["name"]) and \
                    (largest_file is None or file_info["size"] > largest_file["size"]):
                largest_file = file_info
                largest_index = index
        return largest_index, largest_file

    def set_files(self, files):
        self.clear()
        self.files_data = []

        for file_info in files:
            if is_video_file(file_info['name']):
                self.addItem(file_info['name'])
                self.files_data.append(file_info)

    def set_active_index(self, file_index):
        cur_ind = 0
        for ind, file_info in enumerate(self.files_data):
            if ind == file_index:
                self.item(cur_ind).setSelected(True)
                self.setFocus()
                break
            cur_ind += 1

    def get_file_info(self, menu_index):
        """
        Get the file info, based on the menu index
        """
        return self.files_data[menu_index] if menu_index < len(
            self.files_data) else None

    def on_item_clicked(self, item):
        item_ind = self.row(item)
        if self.loaded_list:
            self.playing_item_change.emit(item_ind)

    def on_item_double_clicked(self, item):
        self.item_should_play.emit()
Ejemplo n.º 47
0
class CuraEngineBackend(QObject, Backend):
    backendError = Signal()

    ##  Starts the back-end plug-in.
    #
    #   This registers all the signal listeners and prepares for communication
    #   with the back-end in general.
    #   CuraEngineBackend is exposed to qml as well.
    def __init__(self) -> None:
        super().__init__()
        # Find out where the engine is located, and how it is called.
        # This depends on how Cura is packaged and which OS we are running on.
        executable_name = "CuraEngine"
        if Platform.isWindows():
            executable_name += ".exe"
        default_engine_location = executable_name

        search_path = [
            os.path.abspath(os.path.dirname(sys.executable)),
            os.path.abspath(
                os.path.join(os.path.dirname(sys.executable), "bin")),
            os.path.abspath(os.path.join(os.path.dirname(sys.executable),
                                         "..")),
            os.path.join(CuraApplication.getInstallPrefix(), "bin"),
            os.path.dirname(os.path.abspath(sys.executable)),
        ]

        for path in search_path:
            engine_path = os.path.join(path, executable_name)
            if os.path.isfile(engine_path):
                default_engine_location = engine_path
                break

        if Platform.isLinux() and not default_engine_location:
            if not os.getenv("PATH"):
                raise OSError(
                    "There is something wrong with your Linux installation.")
            for pathdir in cast(str, os.getenv("PATH")).split(os.pathsep):
                execpath = os.path.join(pathdir, executable_name)
                if os.path.exists(execpath):
                    default_engine_location = execpath
                    break

        self._application = CuraApplication.getInstance(
        )  #type: CuraApplication
        self._multi_build_plate_model = None  #type: Optional[MultiBuildPlateModel]
        self._machine_error_checker = None  #type: Optional[MachineErrorChecker]

        if not default_engine_location:
            raise EnvironmentError("Could not find CuraEngine")

        Logger.log("i", "Found CuraEngine at: %s", default_engine_location)

        default_engine_location = os.path.abspath(default_engine_location)
        self._application.getPreferences().addPreference(
            "backend/location", default_engine_location)

        # Workaround to disable layer view processing if layer view is not active.
        self._layer_view_active = False  #type: bool
        self._onActiveViewChanged()

        self._stored_layer_data = []  # type: List[Arcus.PythonMessage]
        self._stored_optimized_layer_data = {
        }  # type: Dict[int, List[Arcus.PythonMessage]] # key is build plate number, then arrays are stored until they go to the ProcessSlicesLayersJob

        self._scene = self._application.getController().getScene(
        )  #type: Scene
        self._scene.sceneChanged.connect(self._onSceneChanged)

        # Triggers for auto-slicing. Auto-slicing is triggered as follows:
        #  - auto-slicing is started with a timer
        #  - whenever there is a value change, we start the timer
        #  - sometimes an error check can get scheduled for a value change, in that case, we ONLY want to start the
        #    auto-slicing timer when that error check is finished
        # If there is an error check, stop the auto-slicing timer, and only wait for the error check to be finished
        # to start the auto-slicing timer again.
        #
        self._global_container_stack = None  #type: Optional[ContainerStack]

        # Listeners for receiving messages from the back-end.
        self._message_handlers["cura.proto.Layer"] = self._onLayerMessage
        self._message_handlers[
            "cura.proto.LayerOptimized"] = self._onOptimizedLayerMessage
        self._message_handlers["cura.proto.Progress"] = self._onProgressMessage
        self._message_handlers[
            "cura.proto.GCodeLayer"] = self._onGCodeLayerMessage
        self._message_handlers[
            "cura.proto.GCodePrefix"] = self._onGCodePrefixMessage
        self._message_handlers[
            "cura.proto.PrintTimeMaterialEstimates"] = self._onPrintTimeMaterialEstimates
        self._message_handlers[
            "cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage

        self._start_slice_job = None  #type: Optional[StartSliceJob]
        self._start_slice_job_build_plate = None  #type: Optional[int]
        self._slicing = False  #type: bool # Are we currently slicing?
        self._restart = False  #type: bool # Back-end is currently restarting?
        self._tool_active = False  #type: bool # If a tool is active, some tasks do not have to do anything
        self._always_restart = True  #type: bool # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
        self._process_layers_job = None  #type: Optional[ProcessSlicedLayersJob] # The currently active job to process layers, or None if it is not processing layers.
        self._build_plates_to_be_sliced = [
        ]  #type: List[int] # what needs slicing?
        self._engine_is_fresh = True  #type: bool # Is the newly started engine used before or not?

        self._backend_log_max_lines = 20000  #type: int # Maximum number of lines to buffer
        self._error_message = None  #type: Optional[Message] # Pop-up message that shows errors.
        self._last_num_objects = defaultdict(
            int
        )  #type: Dict[int, int] # Count number of objects to see if there is something changed
        self._postponed_scene_change_sources = [
        ]  #type: List[SceneNode] # scene change is postponed (by a tool)

        self._slice_start_time = None  #type: Optional[float]
        self._is_disabled = False  #type: bool

        self._application.getPreferences().addPreference(
            "general/auto_slice", False)

        self._use_timer = False  #type: bool
        # When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired.
        # This timer will group them up, and only slice for the last setting changed signal.
        # TODO: Properly group propertyChanged signals by whether they are triggered by the same user interaction.
        self._change_timer = QTimer()  #type: QTimer
        self._change_timer.setSingleShot(True)
        self._change_timer.setInterval(500)
        self.determineAutoSlicing()
        self._application.getPreferences().preferenceChanged.connect(
            self._onPreferencesChanged)

        self._application.initializationFinished.connect(self.initialize)

    def initialize(self) -> None:
        self._multi_build_plate_model = self._application.getMultiBuildPlateModel(
        )

        self._application.getController().activeViewChanged.connect(
            self._onActiveViewChanged)

        if self._multi_build_plate_model:
            self._multi_build_plate_model.activeBuildPlateChanged.connect(
                self._onActiveViewChanged)

        self._application.getMachineManager().globalContainerChanged.connect(
            self._onGlobalStackChanged)
        self._onGlobalStackChanged()

        # extruder enable / disable. Actually wanted to use machine manager here, but the initialization order causes it to crash
        ExtruderManager.getInstance().extrudersChanged.connect(
            self._extruderChanged)

        self.backendQuit.connect(self._onBackendQuit)
        self.backendConnected.connect(self._onBackendConnected)

        # When a tool operation is in progress, don't slice. So we need to listen for tool operations.
        self._application.getController().toolOperationStarted.connect(
            self._onToolOperationStarted)
        self._application.getController().toolOperationStopped.connect(
            self._onToolOperationStopped)

        self._machine_error_checker = self._application.getMachineErrorChecker(
        )
        self._machine_error_checker.errorCheckFinished.connect(
            self._onStackErrorCheckFinished)

    ##  Terminate the engine process.
    #
    #   This function should terminate the engine process.
    #   Called when closing the application.
    def close(self) -> None:
        # Terminate CuraEngine if it is still running at this point
        self._terminate()

    ##  Get the command that is used to call the engine.
    #   This is useful for debugging and used to actually start the engine.
    #   \return list of commands and args / parameters.
    def getEngineCommand(self) -> List[str]:
        command = [
            self._application.getPreferences().getValue("backend/location"),
            "connect", "127.0.0.1:{0}".format(self._port), ""
        ]

        parser = argparse.ArgumentParser(prog="cura", add_help=False)
        parser.add_argument(
            "--debug",
            action="store_true",
            default=False,
            help="Turn on the debug mode by setting this option.")
        known_args = vars(parser.parse_known_args()[0])
        if known_args["debug"]:
            command.append("-vvv")

        return command

    ##  Emitted when we get a message containing print duration and material amount.
    #   This also implies the slicing has finished.
    #   \param time The amount of time the print will take.
    #   \param material_amount The amount of material the print will use.
    printDurationMessage = Signal()

    ##  Emitted when the slicing process starts.
    slicingStarted = Signal()

    ##  Emitted when the slicing process is aborted forcefully.
    slicingCancelled = Signal()

    @pyqtSlot()
    def stopSlicing(self) -> None:
        self.setState(BackendState.NotStarted)
        if self._slicing:  # We were already slicing. Stop the old job.
            self._terminate()
            self._createSocket()

        if self._process_layers_job is not None:  # We were processing layers. Stop that, the layers are going to change soon.
            Logger.log("i", "Aborting process layers job...")
            self._process_layers_job.abort()
            self._process_layers_job = None

        if self._error_message:
            self._error_message.hide()

    ##  Manually triggers a reslice
    @pyqtSlot()
    def forceSlice(self) -> None:
        self.markSliceAll()
        self.slice()

    ##  Perform a slice of the scene.
    def slice(self) -> None:
        Logger.log("i", "Starting to slice...")
        self._slice_start_time = time()
        if not self._build_plates_to_be_sliced:
            self.processingProgress.emit(1.0)
            Logger.log(
                "w",
                "Slice unnecessary, nothing has changed that needs reslicing.")
            self.setState(BackendState.Done)
            return

        if self._process_layers_job:
            Logger.log("d", "Process layers job still busy, trying later.")
            return

        if not hasattr(self._scene, "gcode_dict"):
            self._scene.gcode_dict = {
            }  #type: ignore #Because we are creating the missing attribute here.

        # see if we really have to slice
        active_build_plate = self._application.getMultiBuildPlateModel(
        ).activeBuildPlate
        build_plate_to_be_sliced = self._build_plates_to_be_sliced.pop(0)
        Logger.log(
            "d", "Going to slice build plate [%s]!" % build_plate_to_be_sliced)
        num_objects = self._numObjectsPerBuildPlate()

        self._stored_layer_data = []

        if build_plate_to_be_sliced not in num_objects or num_objects[
                build_plate_to_be_sliced] == 0:
            self._scene.gcode_dict[build_plate_to_be_sliced] = [
            ]  #type: ignore #Because we created this attribute above.
            Logger.log("d",
                       "Build plate %s has no objects to be sliced, skipping",
                       build_plate_to_be_sliced)
            if self._build_plates_to_be_sliced:
                self.slice()
            return
        self._stored_optimized_layer_data[build_plate_to_be_sliced] = []
        if self._application.getPrintInformation(
        ) and build_plate_to_be_sliced == active_build_plate:
            self._application.getPrintInformation().setToZeroPrintInformation(
                build_plate_to_be_sliced)

        if self._process is None:  # type: ignore
            self._createSocket()
        self.stopSlicing()
        self._engine_is_fresh = False  # Yes we're going to use the engine

        self.processingProgress.emit(0.0)
        self.backendStateChange.emit(BackendState.NotStarted)

        self._scene.gcode_dict[build_plate_to_be_sliced] = [
        ]  #type: ignore #[] indexed by build plate number
        self._slicing = True
        self.slicingStarted.emit()

        self.determineAutoSlicing()  # Switch timer on or off if appropriate

        slice_message = self._socket.createMessage("cura.proto.Slice")
        self._start_slice_job = StartSliceJob(slice_message)
        self._start_slice_job_build_plate = build_plate_to_be_sliced
        self._start_slice_job.setBuildPlate(self._start_slice_job_build_plate)
        self._start_slice_job.start()
        self._start_slice_job.finished.connect(self._onStartSliceCompleted)

    ##  Terminate the engine process.
    #   Start the engine process by calling _createSocket()
    def _terminate(self) -> None:
        self._slicing = False
        self._stored_layer_data = []
        if self._start_slice_job_build_plate in self._stored_optimized_layer_data:
            del self._stored_optimized_layer_data[
                self._start_slice_job_build_plate]
        if self._start_slice_job is not None:
            self._start_slice_job.cancel()

        self.slicingCancelled.emit()
        self.processingProgress.emit(0)
        Logger.log("d", "Attempting to kill the engine process")

        if self._application.getUseExternalBackend():
            return

        if self._process is not None:  # type: ignore
            Logger.log("d", "Killing engine process")
            try:
                self._process.terminate()  # type: ignore
                Logger.log("d",
                           "Engine process is killed. Received return code %s",
                           self._process.wait())  # type: ignore
                self._process = None  # type: ignore

            except Exception as e:  # terminating a process that is already terminating causes an exception, silently ignore this.
                Logger.log(
                    "d",
                    "Exception occurred while trying to kill the engine %s",
                    str(e))

    ##  Event handler to call when the job to initiate the slicing process is
    #   completed.
    #
    #   When the start slice job is successfully completed, it will be happily
    #   slicing. This function handles any errors that may occur during the
    #   bootstrapping of a slice job.
    #
    #   \param job The start slice job that was just finished.
    def _onStartSliceCompleted(self, job: StartSliceJob) -> None:
        if self._error_message:
            self._error_message.hide()

        # Note that cancelled slice jobs can still call this method.
        if self._start_slice_job is job:
            self._start_slice_job = None

        if job.isCancelled() or job.getError() or job.getResult(
        ) == StartJobResult.Error:
            self.setState(BackendState.Error)
            self.backendError.emit(job)
            return

        if job.getResult() == StartJobResult.MaterialIncompatible:
            if self._application.platformActivity:
                self._error_message = Message(catalog.i18nc(
                    "@info:status",
                    "Unable to slice with the current material as it is incompatible with the selected machine or configuration."
                ),
                                              title=catalog.i18nc(
                                                  "@info:title",
                                                  "Unable to slice"))
                self._error_message.show()
                self.setState(BackendState.Error)
                self.backendError.emit(job)
            else:
                self.setState(BackendState.NotStarted)
            return

        if job.getResult() == StartJobResult.SettingError:
            if self._application.platformActivity:
                if not self._global_container_stack:
                    Logger.log(
                        "w",
                        "Global container stack not assigned to CuraEngineBackend!"
                    )
                    return
                extruders = ExtruderManager.getInstance(
                ).getActiveExtruderStacks()
                error_keys = []  #type: List[str]
                for extruder in extruders:
                    error_keys.extend(extruder.getErrorKeys())
                if not extruders:
                    error_keys = self._global_container_stack.getErrorKeys()
                error_labels = set()
                for key in error_keys:
                    for stack in [
                            self._global_container_stack
                    ] + extruders:  #Search all container stacks for the definition of this setting. Some are only in an extruder stack.
                        definitions = cast(
                            DefinitionContainerInterface,
                            stack.getBottom()).findDefinitions(key=key)
                        if definitions:
                            break  #Found it! No need to continue search.
                    else:  #No stack has a definition for this setting.
                        Logger.log(
                            "w",
                            "When checking settings for errors, unable to find definition for key: {key}"
                            .format(key=key))
                        continue
                    error_labels.add(definitions[0].label)

                self._error_message = Message(catalog.i18nc(
                    "@info:status",
                    "Unable to slice with the current settings. The following settings have errors: {0}"
                ).format(", ".join(error_labels)),
                                              title=catalog.i18nc(
                                                  "@info:title",
                                                  "Unable to slice"))
                self._error_message.show()
                self.setState(BackendState.Error)
                self.backendError.emit(job)
            else:
                self.setState(BackendState.NotStarted)
            return

        elif job.getResult() == StartJobResult.ObjectSettingError:
            errors = {}
            for node in DepthFirstIterator(
                    self._application.getController().getScene().getRoot()):
                stack = node.callDecoration("getStack")
                if not stack:
                    continue
                for key in stack.getErrorKeys():
                    if not self._global_container_stack:
                        Logger.log(
                            "e",
                            "CuraEngineBackend does not have global_container_stack assigned."
                        )
                        continue
                    definition = cast(DefinitionContainerInterface,
                                      self._global_container_stack.getBottom()
                                      ).findDefinitions(key=key)
                    if not definition:
                        Logger.log(
                            "e",
                            "When checking settings for errors, unable to find definition for key {key} in per-object stack."
                            .format(key=key))
                        continue
                    errors[key] = definition[0].label
            self._error_message = Message(catalog.i18nc(
                "@info:status",
                "Unable to slice due to some per-model settings. The following settings have errors on one or more models: {error_labels}"
            ).format(error_labels=", ".join(errors.values())),
                                          title=catalog.i18nc(
                                              "@info:title",
                                              "Unable to slice"))
            self._error_message.show()
            self.setState(BackendState.Error)
            self.backendError.emit(job)
            return

        if job.getResult() == StartJobResult.BuildPlateError:
            if self._application.platformActivity:
                self._error_message = Message(catalog.i18nc(
                    "@info:status",
                    "Unable to slice because the prime tower or prime position(s) are invalid."
                ),
                                              title=catalog.i18nc(
                                                  "@info:title",
                                                  "Unable to slice"))
                self._error_message.show()
                self.setState(BackendState.Error)
                self.backendError.emit(job)
            else:
                self.setState(BackendState.NotStarted)

        if job.getResult() == StartJobResult.ObjectsWithDisabledExtruder:
            self._error_message = Message(catalog.i18nc(
                "@info:status",
                "Unable to slice because there are objects associated with disabled Extruder %s."
            ) % job.getMessage(),
                                          title=catalog.i18nc(
                                              "@info:title",
                                              "Unable to slice"))
            self._error_message.show()
            self.setState(BackendState.Error)
            self.backendError.emit(job)
            return

        if job.getResult() == StartJobResult.NothingToSlice:
            if self._application.platformActivity:
                self._error_message = Message(catalog.i18nc(
                    "@info:status",
                    "Please review settings and check if your models:"
                    "\n- Fit within the build volume"
                    "\n- Are assigned to an enabled extruder"
                    "\n- Are not all set as modifier meshes"),
                                              title=catalog.i18nc(
                                                  "@info:title",
                                                  "Unable to slice"))
                self._error_message.show()
                self.setState(BackendState.Error)
                self.backendError.emit(job)
            else:
                self.setState(BackendState.NotStarted)
            self._invokeSlice()
            return

        # Preparation completed, send it to the backend.
        self._socket.sendMessage(job.getSliceMessage())

        # Notify the user that it's now up to the backend to do it's job
        self.setState(BackendState.Processing)

        if self._slice_start_time:
            Logger.log("d", "Sending slice message took %s seconds",
                       time() - self._slice_start_time)

    ##  Determine enable or disable auto slicing. Return True for enable timer and False otherwise.
    #   It disables when
    #   - preference auto slice is off
    #   - decorator isBlockSlicing is found (used in g-code reader)
    def determineAutoSlicing(self) -> bool:
        enable_timer = True
        self._is_disabled = False

        if not self._application.getPreferences().getValue(
                "general/auto_slice"):
            enable_timer = False
        for node in DepthFirstIterator(self._scene.getRoot()):
            if node.callDecoration("isBlockSlicing"):
                enable_timer = False
                self.setState(BackendState.Disabled)
                self._is_disabled = True
            gcode_list = node.callDecoration("getGCodeList")
            if gcode_list is not None:
                self._scene.gcode_dict[node.callDecoration(
                    "getBuildPlateNumber"
                )] = gcode_list  #type: ignore #Because we generate this attribute dynamically.

        if self._use_timer == enable_timer:
            return self._use_timer
        if enable_timer:
            self.setState(BackendState.NotStarted)
            self.enableTimer()
            return True
        else:
            self.disableTimer()
            return False

    ##  Return a dict with number of objects per build plate
    def _numObjectsPerBuildPlate(self) -> Dict[int, int]:
        num_objects = defaultdict(int)  #type: Dict[int, int]
        for node in DepthFirstIterator(self._scene.getRoot()):
            # Only count sliceable objects
            if node.callDecoration("isSliceable"):
                build_plate_number = node.callDecoration("getBuildPlateNumber")
                if build_plate_number is not None:
                    num_objects[build_plate_number] += 1
        return num_objects

    ##  Listener for when the scene has changed.
    #
    #   This should start a slice if the scene is now ready to slice.
    #
    #   \param source The scene node that was changed.
    def _onSceneChanged(self, source: SceneNode) -> None:
        if not source.callDecoration("isSliceable"):
            return

        # This case checks if the source node is a node that contains GCode. In this case the
        # current layer data is removed so the previous data is not rendered - CURA-4821
        if source.callDecoration("isBlockSlicing") and source.callDecoration(
                "getLayerData"):
            self._stored_optimized_layer_data = {}

        build_plate_changed = set()
        source_build_plate_number = source.callDecoration(
            "getBuildPlateNumber")
        if source == self._scene.getRoot():
            # we got the root node
            num_objects = self._numObjectsPerBuildPlate()
            for build_plate_number in list(
                    self._last_num_objects.keys()) + list(num_objects.keys()):
                if build_plate_number not in self._last_num_objects or num_objects[
                        build_plate_number] != self._last_num_objects[
                            build_plate_number]:
                    self._last_num_objects[build_plate_number] = num_objects[
                        build_plate_number]
                    build_plate_changed.add(build_plate_number)
        else:
            # we got a single scenenode
            if not source.callDecoration("isGroup"):
                mesh_data = source.getMeshData()
                if mesh_data is None or mesh_data.getVertices() is None:
                    return

            # There are some SceneNodes that do not have any build plate associated, then do not add to the list.
            if source_build_plate_number is not None:
                build_plate_changed.add(source_build_plate_number)

        if not build_plate_changed:
            return

        if self._tool_active:
            # do it later, each source only has to be done once
            if source not in self._postponed_scene_change_sources:
                self._postponed_scene_change_sources.append(source)
            return

        self.stopSlicing()
        for build_plate_number in build_plate_changed:
            if build_plate_number not in self._build_plates_to_be_sliced:
                self._build_plates_to_be_sliced.append(build_plate_number)
            self.printDurationMessage.emit(source_build_plate_number, {}, [])
        self.processingProgress.emit(0.0)
        self._clearLayerData(build_plate_changed)

        self._invokeSlice()

    ##  Called when an error occurs in the socket connection towards the engine.
    #
    #   \param error The exception that occurred.
    def _onSocketError(self, error: Arcus.Error) -> None:
        if self._application.isShuttingDown():
            return

        super()._onSocketError(error)
        if error.getErrorCode() == Arcus.ErrorCode.Debug:
            return

        self._terminate()
        self._createSocket()

        if error.getErrorCode() not in [
                Arcus.ErrorCode.BindFailedError,
                Arcus.ErrorCode.ConnectionResetError, Arcus.ErrorCode.Debug
        ]:
            Logger.log("w", "A socket error caused the connection to be reset")

        # _terminate()' function sets the job status to 'cancel', after reconnecting to another Port the job status
        # needs to be updated. Otherwise backendState is "Unable To Slice"
        if error.getErrorCode(
        ) == Arcus.ErrorCode.BindFailedError and self._start_slice_job is not None:
            self._start_slice_job.setIsCancelled(False)

    # Check if there's any slicable object in the scene.
    def hasSlicableObject(self) -> bool:
        has_slicable = False
        for node in DepthFirstIterator(self._scene.getRoot()):
            if node.callDecoration("isSliceable"):
                has_slicable = True
                break
        return has_slicable

    ##  Remove old layer data (if any)
    def _clearLayerData(self, build_plate_numbers: Set = None) -> None:
        # Clear out any old gcode
        self._scene.gcode_dict = {}  # type: ignore

        for node in DepthFirstIterator(self._scene.getRoot()):
            if node.callDecoration("getLayerData"):
                if not build_plate_numbers or node.callDecoration(
                        "getBuildPlateNumber") in build_plate_numbers:
                    # We can asume that all nodes have a parent as we're looping through the scene (and filter out root)
                    cast(SceneNode, node.getParent()).removeChild(node)

    def markSliceAll(self) -> None:
        for build_plate_number in range(
                self._application.getMultiBuildPlateModel().maxBuildPlate + 1):
            if build_plate_number not in self._build_plates_to_be_sliced:
                self._build_plates_to_be_sliced.append(build_plate_number)

    ##  Convenient function: mark everything to slice, emit state and clear layer data
    def needsSlicing(self) -> None:
        # CURA-6604: If there's no slicable object, do not (try to) trigger slice, which will clear all the current
        # gcode. This can break Gcode file loading if it tries to remove it afterwards.
        if not self.hasSlicableObject():
            return
        self.determineAutoSlicing()
        self.stopSlicing()
        self.markSliceAll()
        self.processingProgress.emit(0.0)
        if not self._use_timer:
            # With manually having to slice, we want to clear the old invalid layer data.
            self._clearLayerData()

    ##  A setting has changed, so check if we must reslice.
    # \param instance The setting instance that has changed.
    # \param property The property of the setting instance that has changed.
    def _onSettingChanged(self, instance: SettingInstance,
                          property: str) -> None:
        if property == "value":  # Only reslice if the value has changed.
            self.needsSlicing()
            self._onChanged()

        elif property == "validationState":
            if self._use_timer:
                self._change_timer.stop()

    def _onStackErrorCheckFinished(self) -> None:
        self.determineAutoSlicing()
        if self._is_disabled:
            return

        if not self._slicing and self._build_plates_to_be_sliced:
            self.needsSlicing()
            self._onChanged()

    ##  Called when a sliced layer data message is received from the engine.
    #
    #   \param message The protobuf message containing sliced layer data.
    def _onLayerMessage(self, message: Arcus.PythonMessage) -> None:
        self._stored_layer_data.append(message)

    ##  Called when an optimized sliced layer data message is received from the engine.
    #
    #   \param message The protobuf message containing sliced layer data.
    def _onOptimizedLayerMessage(self, message: Arcus.PythonMessage) -> None:
        if self._start_slice_job_build_plate is not None:
            if self._start_slice_job_build_plate not in self._stored_optimized_layer_data:
                self._stored_optimized_layer_data[
                    self._start_slice_job_build_plate] = []
            self._stored_optimized_layer_data[
                self._start_slice_job_build_plate].append(message)

    ##  Called when a progress message is received from the engine.
    #
    #   \param message The protobuf message containing the slicing progress.
    def _onProgressMessage(self, message: Arcus.PythonMessage) -> None:
        self.processingProgress.emit(message.amount)
        self.setState(BackendState.Processing)

    def _invokeSlice(self) -> None:
        if self._use_timer:
            # if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
            # otherwise business as usual
            if self._machine_error_checker is None:
                self._change_timer.stop()
                return

            if self._machine_error_checker.needToWaitForResult:
                self._change_timer.stop()
            else:
                self._change_timer.start()

    ##  Called when the engine sends a message that slicing is finished.
    #
    #   \param message The protobuf message signalling that slicing is finished.
    def _onSlicingFinishedMessage(self, message: Arcus.PythonMessage) -> None:
        self.setState(BackendState.Done)
        self.processingProgress.emit(1.0)

        try:
            gcode_list = self._scene.gcode_dict[
                self.
                _start_slice_job_build_plate]  #type: ignore #Because we generate this attribute dynamically.
        except KeyError:  # Can occur if the g-code has been cleared while a slice message is still arriving from the other end.
            gcode_list = []
        for index, line in enumerate(gcode_list):
            replaced = line.replace(
                "{print_time}",
                str(self._application.getPrintInformation().currentPrintTime.
                    getDisplayString(DurationFormat.Format.ISO8601)))
            replaced = replaced.replace(
                "{filament_amount}",
                str(self._application.getPrintInformation().materialLengths))
            replaced = replaced.replace(
                "{filament_weight}",
                str(self._application.getPrintInformation().materialWeights))
            replaced = replaced.replace(
                "{filament_cost}",
                str(self._application.getPrintInformation().materialCosts))
            replaced = replaced.replace(
                "{jobname}",
                str(self._application.getPrintInformation().jobName))

            gcode_list[index] = replaced

        self._slicing = False
        if self._slice_start_time:
            Logger.log("d", "Slicing took %s seconds",
                       time() - self._slice_start_time)
        Logger.log("d", "Number of models per buildplate: %s",
                   dict(self._numObjectsPerBuildPlate()))

        # See if we need to process the sliced layers job.
        active_build_plate = self._application.getMultiBuildPlateModel(
        ).activeBuildPlate
        if (self._layer_view_active
                and (self._process_layers_job is None
                     or not self._process_layers_job.isRunning())
                and active_build_plate == self._start_slice_job_build_plate
                and active_build_plate not in self._build_plates_to_be_sliced):

            self._startProcessSlicedLayersJob(active_build_plate)
        # self._onActiveViewChanged()
        self._start_slice_job_build_plate = None

        Logger.log("d", "See if there is more to slice...")
        # Somehow this results in an Arcus Error
        # self.slice()
        # Call slice again using the timer, allowing the backend to restart
        if self._build_plates_to_be_sliced:
            self.enableTimer(
            )  # manually enable timer to be able to invoke slice, also when in manual slice mode
            self._invokeSlice()

    ##  Called when a g-code message is received from the engine.
    #
    #   \param message The protobuf message containing g-code, encoded as UTF-8.
    def _onGCodeLayerMessage(self, message: Arcus.PythonMessage) -> None:
        try:
            self._scene.gcode_dict[self._start_slice_job_build_plate].append(
                message.data.decode("utf-8", "replace")
            )  #type: ignore #Because we generate this attribute dynamically.
        except KeyError:  # Can occur if the g-code has been cleared while a slice message is still arriving from the other end.
            pass  # Throw the message away.

    ##  Called when a g-code prefix message is received from the engine.
    #
    #   \param message The protobuf message containing the g-code prefix,
    #   encoded as UTF-8.
    def _onGCodePrefixMessage(self, message: Arcus.PythonMessage) -> None:
        try:
            self._scene.gcode_dict[self._start_slice_job_build_plate].insert(
                0, message.data.decode("utf-8", "replace")
            )  #type: ignore #Because we generate this attribute dynamically.
        except KeyError:  # Can occur if the g-code has been cleared while a slice message is still arriving from the other end.
            pass  # Throw the message away.

    ##  Creates a new socket connection.
    def _createSocket(self, protocol_file: str = None) -> None:
        if not protocol_file:
            plugin_path = PluginRegistry.getInstance().getPluginPath(
                self.getPluginId())
            if not plugin_path:
                Logger.log("e", "Could not get plugin path!",
                           self.getPluginId())
                return
            protocol_file = os.path.abspath(
                os.path.join(plugin_path, "Cura.proto"))
        super()._createSocket(protocol_file)
        self._engine_is_fresh = True

    ##  Called when anything has changed to the stuff that needs to be sliced.
    #
    #   This indicates that we should probably re-slice soon.
    def _onChanged(self, *args: Any, **kwargs: Any) -> None:
        self.needsSlicing()
        if self._use_timer:
            # if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
            # otherwise business as usual
            if self._machine_error_checker is None:
                self._change_timer.stop()
                return

            if self._machine_error_checker.needToWaitForResult:
                self._change_timer.stop()
            else:
                self._change_timer.start()

    ##  Called when a print time message is received from the engine.
    #
    #   \param message The protobuf message containing the print time per feature and
    #   material amount per extruder
    def _onPrintTimeMaterialEstimates(self,
                                      message: Arcus.PythonMessage) -> None:
        material_amounts = []
        for index in range(message.repeatedMessageCount("materialEstimates")):
            material_amounts.append(
                message.getRepeatedMessage("materialEstimates",
                                           index).material_amount)

        times = self._parseMessagePrintTimes(message)
        self.printDurationMessage.emit(self._start_slice_job_build_plate,
                                       times, material_amounts)

    ##  Called for parsing message to retrieve estimated time per feature
    #
    #   \param message The protobuf message containing the print time per feature
    def _parseMessagePrintTimes(
            self, message: Arcus.PythonMessage) -> Dict[str, float]:
        result = {
            "inset_0": message.time_inset_0,
            "inset_x": message.time_inset_x,
            "skin": message.time_skin,
            "infill": message.time_infill,
            "support_infill": message.time_support_infill,
            "support_interface": message.time_support_interface,
            "support": message.time_support,
            "skirt": message.time_skirt,
            "prime_tower": message.time_prime_tower,
            "travel": message.time_travel,
            "retract": message.time_retract,
            "none": message.time_none
        }
        return result

    ##  Called when the back-end connects to the front-end.
    def _onBackendConnected(self) -> None:
        if self._restart:
            self._restart = False
            self._onChanged()

    ##  Called when the user starts using some tool.
    #
    #   When the user starts using a tool, we should pause slicing to prevent
    #   continuously slicing while the user is dragging some tool handle.
    #
    #   \param tool The tool that the user is using.
    def _onToolOperationStarted(self, tool: Tool) -> None:
        self._tool_active = True  # Do not react on scene change
        self.disableTimer()
        # Restart engine as soon as possible, we know we want to slice afterwards
        if not self._engine_is_fresh:
            self._terminate()
            self._createSocket()

    ##  Called when the user stops using some tool.
    #
    #   This indicates that we can safely start slicing again.
    #
    #   \param tool The tool that the user was using.
    def _onToolOperationStopped(self, tool: Tool) -> None:
        self._tool_active = False  # React on scene change again
        self.determineAutoSlicing()  # Switch timer on if appropriate
        # Process all the postponed scene changes
        while self._postponed_scene_change_sources:
            source = self._postponed_scene_change_sources.pop(0)
            self._onSceneChanged(source)

    def _startProcessSlicedLayersJob(self, build_plate_number: int) -> None:
        self._process_layers_job = ProcessSlicedLayersJob(
            self._stored_optimized_layer_data[build_plate_number])
        self._process_layers_job.setBuildPlate(build_plate_number)
        self._process_layers_job.finished.connect(
            self._onProcessLayersFinished)
        self._process_layers_job.start()

    ##  Called when the user changes the active view mode.
    def _onActiveViewChanged(self) -> None:
        view = self._application.getController().getActiveView()
        if view:
            active_build_plate = self._application.getMultiBuildPlateModel(
            ).activeBuildPlate
            if view.getPluginId(
            ) == "SimulationView":  # If switching to layer view, we should process the layers if that hasn't been done yet.
                self._layer_view_active = True
                # There is data and we're not slicing at the moment
                # if we are slicing, there is no need to re-calculate the data as it will be invalid in a moment.
                # TODO: what build plate I am slicing
                if (active_build_plate in self._stored_optimized_layer_data
                        and not self._slicing and not self._process_layers_job
                        and active_build_plate
                        not in self._build_plates_to_be_sliced):

                    self._startProcessSlicedLayersJob(active_build_plate)
            else:
                self._layer_view_active = False

    ##  Called when the back-end self-terminates.
    #
    #   We should reset our state and start listening for new connections.
    def _onBackendQuit(self) -> None:
        if not self._restart:
            if self._process:  # type: ignore
                Logger.log(
                    "d",
                    "Backend quit with return code %s. Resetting process and socket.",
                    self._process.wait())  # type: ignore
                self._process = None  # type: ignore

    ##  Called when the global container stack changes
    def _onGlobalStackChanged(self) -> None:
        if self._global_container_stack:
            self._global_container_stack.propertyChanged.disconnect(
                self._onSettingChanged)
            self._global_container_stack.containersChanged.disconnect(
                self._onChanged)

            for extruder in self._global_container_stack.extruderList:
                extruder.propertyChanged.disconnect(self._onSettingChanged)
                extruder.containersChanged.disconnect(self._onChanged)

        self._global_container_stack = self._application.getMachineManager(
        ).activeMachine

        if self._global_container_stack:
            self._global_container_stack.propertyChanged.connect(
                self._onSettingChanged
            )  # Note: Only starts slicing when the value changed.
            self._global_container_stack.containersChanged.connect(
                self._onChanged)

            for extruder in self._global_container_stack.extruderList:
                extruder.propertyChanged.connect(self._onSettingChanged)
                extruder.containersChanged.connect(self._onChanged)
            self._onChanged()

    def _onProcessLayersFinished(self, job: ProcessSlicedLayersJob) -> None:
        if job.getBuildPlate() in self._stored_optimized_layer_data:
            del self._stored_optimized_layer_data[job.getBuildPlate()]
        else:
            Logger.log(
                "w",
                "The optimized layer data was already deleted for buildplate %s",
                job.getBuildPlate())
        self._process_layers_job = None
        Logger.log("d", "See if there is more to slice(2)...")
        self._invokeSlice()

    ##  Connect slice function to timer.
    def enableTimer(self) -> None:
        if not self._use_timer:
            self._change_timer.timeout.connect(self.slice)
            self._use_timer = True

    ##  Disconnect slice function from timer.
    #   This means that slicing will not be triggered automatically
    def disableTimer(self) -> None:
        if self._use_timer:
            self._use_timer = False
            self._change_timer.timeout.disconnect(self.slice)

    def _onPreferencesChanged(self, preference: str) -> None:
        if preference != "general/auto_slice":
            return
        auto_slice = self.determineAutoSlicing()
        if auto_slice:
            self._change_timer.start()

    ##   Tickle the backend so in case of auto slicing, it starts the timer.
    def tickle(self) -> None:
        if self._use_timer:
            self._change_timer.start()

    def _extruderChanged(self) -> None:
        if not self._multi_build_plate_model:
            Logger.log(
                "w",
                "CuraEngineBackend does not have multi_build_plate_model assigned!"
            )
            return
        for build_plate_number in range(
                self._multi_build_plate_model.maxBuildPlate + 1):
            if build_plate_number not in self._build_plates_to_be_sliced:
                self._build_plates_to_be_sliced.append(build_plate_number)
        self._invokeSlice()
Ejemplo n.º 48
0
class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self._version = __version__
        self.setWindowIcon(QIcon(":/logo.png"))
        self.setWindowTitle("Tasmota Device Manager {}".format(self._version))

        self.unknown = []
        self.env = TasmotaEnvironment()
        self.device = None

        self.topics = []
        self.mqtt_queue = []
        self.fulltopic_queue = []

        # ensure TDM directory exists in the user directory
        if not os.path.isdir("{}/TDM".format(QDir.homePath())):
            os.mkdir("{}/TDM".format(QDir.homePath()))

        self.settings = QSettings("{}/TDM/tdm.cfg".format(QDir.homePath()),
                                  QSettings.IniFormat)
        self.devices = QSettings("{}/TDM/devices.cfg".format(QDir.homePath()),
                                 QSettings.IniFormat)
        self.setMinimumSize(QSize(1000, 600))

        # configure logging
        logging.basicConfig(filename="{}/TDM/tdm.log".format(QDir.homePath()),
                            level=self.settings.value("loglevel", "INFO"),
                            datefmt="%Y-%m-%d %H:%M:%S",
                            format='%(asctime)s [%(levelname)s] %(message)s')
        logging.info("### TDM START ###")

        # load devices from the devices file, create TasmotaDevices and add the to the environment
        for mac in self.devices.childGroups():
            self.devices.beginGroup(mac)
            device = TasmotaDevice(self.devices.value("topic"),
                                   self.devices.value("full_topic"),
                                   self.devices.value("device_name"))
            device.debug = self.devices.value("debug", False, bool)
            device.p['Mac'] = mac.replace("-", ":")
            device.env = self.env
            self.env.devices.append(device)

            # load device command history
            self.devices.beginGroup("history")
            for k in self.devices.childKeys():
                device.history.append(self.devices.value(k))
            self.devices.endGroup()

            self.devices.endGroup()

        self.device_model = TasmotaDevicesModel(self.env)

        self.setup_mqtt()
        self.setup_main_layout()
        self.add_devices_tab()
        self.build_mainmenu()
        # self.build_toolbars()
        self.setStatusBar(QStatusBar())

        pbSubs = QPushButton("Show subscriptions")
        pbSubs.setFlat(True)
        pbSubs.clicked.connect(self.showSubs)
        self.statusBar().addPermanentWidget(pbSubs)

        self.queue_timer = QTimer()
        self.queue_timer.timeout.connect(self.mqtt_publish_queue)
        self.queue_timer.start(250)

        self.auto_timer = QTimer()
        self.auto_timer.timeout.connect(self.auto_telemetry)

        self.load_window_state()

        if self.settings.value("connect_on_startup", False, bool):
            self.actToggleConnect.trigger()

        self.tele_docks = {}
        self.consoles = []

    def setup_main_layout(self):
        self.mdi = QMdiArea()
        self.mdi.setActivationOrder(QMdiArea.ActivationHistoryOrder)
        self.mdi.setTabsClosable(True)
        self.setCentralWidget(self.mdi)

    def setup_mqtt(self):
        self.mqtt = MqttClient()
        self.mqtt.connecting.connect(self.mqtt_connecting)
        self.mqtt.connected.connect(self.mqtt_connected)
        self.mqtt.disconnected.connect(self.mqtt_disconnected)
        self.mqtt.connectError.connect(self.mqtt_connectError)
        self.mqtt.messageSignal.connect(self.mqtt_message)

    def add_devices_tab(self):
        self.devices_list = ListWidget(self)
        sub = self.mdi.addSubWindow(self.devices_list)
        sub.setWindowState(Qt.WindowMaximized)
        self.devices_list.deviceSelected.connect(self.selectDevice)
        self.devices_list.openConsole.connect(self.openConsole)
        self.devices_list.openRulesEditor.connect(self.openRulesEditor)
        self.devices_list.openTelemetry.connect(self.openTelemetry)
        self.devices_list.openWebUI.connect(self.openWebUI)

    def load_window_state(self):
        wndGeometry = self.settings.value('window_geometry')
        if wndGeometry:
            self.restoreGeometry(wndGeometry)

    def build_mainmenu(self):
        mMQTT = self.menuBar().addMenu("MQTT")
        self.actToggleConnect = QAction(QIcon(":/disconnect.png"), "Connect")
        self.actToggleConnect.setCheckable(True)
        self.actToggleConnect.toggled.connect(self.toggle_connect)
        mMQTT.addAction(self.actToggleConnect)

        mMQTT.addAction(QIcon(), "Broker", self.setup_broker)
        mMQTT.addAction(QIcon(), "Autodiscovery patterns", self.patterns)

        mMQTT.addSeparator()
        mMQTT.addAction(QIcon(), "Clear obsolete retained LWTs",
                        self.clear_LWT)

        mMQTT.addSeparator()
        mMQTT.addAction(QIcon(), "Auto telemetry period",
                        self.auto_telemetry_period)

        self.actToggleAutoUpdate = QAction(QIcon(":/auto_telemetry.png"),
                                           "Auto telemetry")
        self.actToggleAutoUpdate.setCheckable(True)
        self.actToggleAutoUpdate.toggled.connect(self.toggle_autoupdate)
        mMQTT.addAction(self.actToggleAutoUpdate)

        mSettings = self.menuBar().addMenu("Settings")
        mSettings.addAction(QIcon(), "BSSId aliases", self.bssid)
        mSettings.addSeparator()
        mSettings.addAction(QIcon(), "Preferences", self.prefs)

        # mExport = self.menuBar().addMenu("Export")
        # mExport.addAction(QIcon(), "OpenHAB", self.openhab)

    def build_toolbars(self):
        main_toolbar = Toolbar(orientation=Qt.Horizontal,
                               iconsize=24,
                               label_position=Qt.ToolButtonTextBesideIcon)
        main_toolbar.setObjectName("main_toolbar")

    def initial_query(self, device, queued=False):
        for c in initial_commands():
            cmd, payload = c
            cmd = device.cmnd_topic(cmd)

            if queued:
                self.mqtt_queue.append([cmd, payload])
            else:
                self.mqtt.publish(cmd, payload, 1)

    def setup_broker(self):
        brokers_dlg = BrokerDialog()
        if brokers_dlg.exec_(
        ) == QDialog.Accepted and self.mqtt.state == self.mqtt.Connected:
            self.mqtt.disconnect()

    def toggle_autoupdate(self, state):
        if state == True:
            if self.mqtt.state == self.mqtt.Connected:
                for d in self.env.devices:
                    self.mqtt.publish(d.cmnd_topic('STATUS'), payload=8)
            self.auto_timer.setInterval(
                self.settings.value("autotelemetry", 5000, int))
            self.auto_timer.start()
        else:
            self.auto_timer.stop()

    def toggle_connect(self, state):
        if state and self.mqtt.state == self.mqtt.Disconnected:
            self.broker_hostname = self.settings.value('hostname', 'localhost')
            self.broker_port = self.settings.value('port', 1883, int)
            self.broker_username = self.settings.value('username')
            self.broker_password = self.settings.value('password')

            self.mqtt.hostname = self.broker_hostname
            self.mqtt.port = self.broker_port

            if self.broker_username:
                self.mqtt.setAuth(self.broker_username, self.broker_password)
            self.mqtt.connectToHost()
        elif not state and self.mqtt.state == self.mqtt.Connected:
            self.mqtt_disconnect()

    def auto_telemetry(self):
        if self.mqtt.state == self.mqtt.Connected:
            for d in self.env.devices:
                self.mqtt.publish(d.cmnd_topic('STATUS'), payload=8)

    def mqtt_connect(self):
        self.broker_hostname = self.settings.value('hostname', 'localhost')
        self.broker_port = self.settings.value('port', 1883, int)
        self.broker_username = self.settings.value('username')
        self.broker_password = self.settings.value('password')

        self.mqtt.hostname = self.broker_hostname
        self.mqtt.port = self.broker_port

        if self.broker_username:
            self.mqtt.setAuth(self.broker_username, self.broker_password)

        if self.mqtt.state == self.mqtt.Disconnected:
            self.mqtt.connectToHost()

    def mqtt_disconnect(self):
        self.mqtt.disconnectFromHost()

    def mqtt_connecting(self):
        self.statusBar().showMessage("Connecting to broker")

    def mqtt_connected(self):
        self.actToggleConnect.setIcon(QIcon(":/connect.png"))
        self.actToggleConnect.setText("Disconnect")
        self.statusBar().showMessage("Connected to {}:{} as {}".format(
            self.broker_hostname, self.broker_port,
            self.broker_username if self.broker_username else '[anonymous]'))

        self.mqtt_subscribe()

    def mqtt_subscribe(self):
        # clear old topics
        self.topics.clear()
        custom_patterns.clear()

        # load custom autodiscovery patterns
        self.settings.beginGroup("Patterns")
        for k in self.settings.childKeys():
            custom_patterns.append(self.settings.value(k))
        self.settings.endGroup()

        # expand fulltopic patterns to subscribable topics
        for pat in default_patterns:  # tasmota default and SO19
            self.topics += expand_fulltopic(pat)

        # check if custom patterns can be matched by default patterns
        for pat in custom_patterns:
            if pat.startswith("%prefix%") or pat.split('/')[1] == "%prefix%":
                continue  # do nothing, default subcriptions will match this topic
            else:
                self.topics += expand_fulltopic(pat)

        for d in self.env.devices:
            # if device has a non-standard pattern, check if the pattern is found in the custom patterns
            if not d.is_default() and d.p['FullTopic'] not in custom_patterns:
                # if pattern is not found then add the device topics to subscription list.
                # if the pattern is found, it will be matched without implicit subscription
                self.topics += expand_fulltopic(d.p['FullTopic'])

        # passing a list of tuples as recommended by paho
        self.mqtt.subscribe([(topic, 0) for topic in self.topics])

    @pyqtSlot(str, str)
    def mqtt_publish(self, t, p):
        self.mqtt.publish(t, p)

    def mqtt_publish_queue(self):
        for q in self.mqtt_queue:
            t, p = q
            self.mqtt.publish(t, p)
            self.mqtt_queue.pop(self.mqtt_queue.index(q))

    def mqtt_disconnected(self):
        self.actToggleConnect.setIcon(QIcon(":/disconnect.png"))
        self.actToggleConnect.setText("Connect")
        self.statusBar().showMessage("Disconnected")

    def mqtt_connectError(self, rc):
        reason = {
            1: "Incorrect protocol version",
            2: "Invalid client identifier",
            3: "Server unavailable",
            4: "Bad username or password",
            5: "Not authorized",
        }
        self.statusBar().showMessage("Connection error: {}".format(reason[rc]))
        self.actToggleConnect.setChecked(False)

    def mqtt_message(self, topic, msg):
        # try to find a device by matching known FullTopics against the MQTT topic of the message
        device = self.env.find_device(topic)
        if device:
            if topic.endswith("LWT"):
                if not msg:
                    msg = "Offline"
                device.update_property("LWT", msg)

                if msg == 'Online':
                    # known device came online, query initial state
                    self.initial_query(device, True)

            else:
                # forward the message for processing
                device.parse_message(topic, msg)
                if device.debug:
                    logging.debug("MQTT: %s %s", topic, msg)

        else:  # unknown device, start autodiscovery process
            if topic.endswith("LWT"):
                self.env.lwts.append(topic)
                logging.info("DISCOVERY: LWT from an unknown device %s", topic)

                # STAGE 1
                # load default and user-provided FullTopic patterns and for all the patterns,
                # try matching the LWT topic (it follows the device's FullTopic syntax

                for p in default_patterns + custom_patterns:
                    match = re.fullmatch(
                        p.replace("%topic%", "(?P<topic>.*?)").replace(
                            "%prefix%", "(?P<prefix>.*?)") + ".*$", topic)
                    if match:
                        # assume that the matched topic is the one configured in device settings
                        possible_topic = match.groupdict().get('topic')
                        if possible_topic not in ('tele', 'stat'):
                            # if the assumed topic is different from tele or stat, there is a chance that it's a valid topic
                            # query the assumed device for its FullTopic. False positives won't reply.
                            possible_topic_cmnd = p.replace(
                                "%prefix%", "cmnd").replace(
                                    "%topic%", possible_topic) + "FullTopic"
                            logging.debug(
                                "DISCOVERY: Asking an unknown device for FullTopic at %s",
                                possible_topic_cmnd)
                            self.mqtt_queue.append([possible_topic_cmnd, ""])

            elif topic.endswith("RESULT") or topic.endswith(
                    "FULLTOPIC"):  # reply from an unknown device
                # STAGE 2
                full_topic = loads(msg).get('FullTopic')
                if full_topic:
                    # the device replies with its FullTopic
                    # here the Topic is extracted using the returned FullTopic, identifying the device
                    parsed = parse_topic(full_topic, topic)
                    if parsed:
                        # got a match, we query the device's MAC address in case it's a known device that had its topic changed
                        logging.debug(
                            "DISCOVERY: topic %s is matched by fulltopic %s",
                            topic, full_topic)

                        d = self.env.find_device(topic=parsed['topic'])
                        if d:
                            d.update_property("FullTopic", full_topic)
                        else:
                            logging.info(
                                "DISCOVERY: Discovered topic=%s with fulltopic=%s",
                                parsed['topic'], full_topic)
                            d = TasmotaDevice(parsed['topic'], full_topic)
                            self.env.devices.append(d)
                            self.device_model.addDevice(d)
                            logging.debug(
                                "DISCOVERY: Sending initial query to topic %s",
                                parsed['topic'])
                            self.initial_query(d, True)
                            tele_topic = d.tele_topic("LWT")
                            if tele_topic in self.env.lwts:
                                self.env.lwts.remove(tele_topic)
                        d.update_property("LWT", "Online")

    def export(self):
        fname, _ = QFileDialog.getSaveFileName(self,
                                               "Export device list as...",
                                               directory=QDir.homePath(),
                                               filter="CSV files (*.csv)")
        if fname:
            if not fname.endswith(".csv"):
                fname += ".csv"

            with open(fname, "w", encoding='utf8') as f:
                column_titles = [
                    'mac', 'topic', 'friendly_name', 'full_topic',
                    'cmnd_topic', 'stat_topic', 'tele_topic', 'module',
                    'module_id', 'firmware', 'core'
                ]
                c = csv.writer(f)
                c.writerow(column_titles)

                for r in range(self.device_model.rowCount()):
                    d = self.device_model.index(r, 0)
                    c.writerow([
                        self.device_model.mac(d),
                        self.device_model.topic(d),
                        self.device_model.friendly_name(d),
                        self.device_model.fullTopic(d),
                        self.device_model.commandTopic(d),
                        self.device_model.statTopic(d),
                        self.device_model.teleTopic(d),
                        # modules.get(self.device_model.module(d)),
                        self.device_model.module(d),
                        self.device_model.firmware(d),
                        self.device_model.core(d)
                    ])

    def bssid(self):
        BSSIdDialog().exec_()

    def patterns(self):
        PatternsDialog().exec_()

    # def openhab(self):
    #     OpenHABDialog(self.env).exec_()

    def showSubs(self):
        QMessageBox.information(self, "Subscriptions",
                                "\n".join(sorted(self.topics)))

    def clear_LWT(self):
        dlg = ClearLWTDialog(self.env)
        if dlg.exec_() == ClearLWTDialog.Accepted:
            for row in range(dlg.lw.count()):
                itm = dlg.lw.item(row)
                if itm.checkState() == Qt.Checked:
                    topic = itm.text()
                    self.mqtt.publish(topic, retain=True)
                    self.env.lwts.remove(topic)
                    logging.info("MQTT: Cleared %s", topic)

    def prefs(self):
        dlg = PrefsDialog()
        if dlg.exec_() == QDialog.Accepted:
            update_devices = False

            devices_short_version = self.settings.value(
                "devices_short_version", True, bool)
            if devices_short_version != dlg.cbDevShortVersion.isChecked():
                update_devices = True
                self.settings.setValue("devices_short_version",
                                       dlg.cbDevShortVersion.isChecked())

            update_consoles = False

            console_font_size = self.settings.value("console_font_size", 9)
            if console_font_size != dlg.sbConsFontSize.value():
                update_consoles = True
                self.settings.setValue("console_font_size",
                                       dlg.sbConsFontSize.value())

            console_word_wrap = self.settings.value("console_word_wrap", True,
                                                    bool)
            if console_word_wrap != dlg.cbConsWW.isChecked():
                update_consoles = True
                self.settings.setValue("console_word_wrap",
                                       dlg.cbConsWW.isChecked())

            if update_consoles:
                for c in self.consoles:
                    c.console.setWordWrapMode(dlg.cbConsWW.isChecked())
                    new_font = QFont(c.console.font())
                    new_font.setPointSize(dlg.sbConsFontSize.value())
                    c.console.setFont(new_font)

        self.settings.sync()

    def auto_telemetry_period(self):
        curr_val = self.settings.value("autotelemetry", 5000, int)
        period, ok = QInputDialog.getInt(
            self, "Set AutoTelemetry period",
            "Values under 5000ms may cause increased ESP LoadAvg", curr_val,
            1000)
        if ok:
            self.settings.setValue("autotelemetry", period)
            self.settings.sync()

    @pyqtSlot(TasmotaDevice)
    def selectDevice(self, d):
        self.device = d

    @pyqtSlot()
    def openTelemetry(self):
        if self.device:
            tele_widget = TelemetryWidget(self.device)
            self.addDockWidget(Qt.RightDockWidgetArea, tele_widget)
            self.mqtt_publish(self.device.cmnd_topic('STATUS'), "8")

    @pyqtSlot()
    def openConsole(self):
        if self.device:
            console_widget = ConsoleWidget(self.device)
            self.mqtt.messageSignal.connect(console_widget.consoleAppend)
            console_widget.sendCommand.connect(self.mqtt.publish)
            self.addDockWidget(Qt.BottomDockWidgetArea, console_widget)
            console_widget.command.setFocus()
            self.consoles.append(console_widget)

    @pyqtSlot()
    def openRulesEditor(self):
        if self.device:
            rules = RulesWidget(self.device)
            self.mqtt.messageSignal.connect(rules.parseMessage)
            rules.sendCommand.connect(self.mqtt_publish)
            self.mdi.setViewMode(QMdiArea.TabbedView)
            self.mdi.addSubWindow(rules)
            rules.setWindowState(Qt.WindowMaximized)
            rules.destroyed.connect(self.updateMDI)
            self.mqtt_queue.append((self.device.cmnd_topic("ruletimer"), ""))
            self.mqtt_queue.append((self.device.cmnd_topic("rule1"), ""))
            self.mqtt_queue.append((self.device.cmnd_topic("Var"), ""))
            self.mqtt_queue.append((self.device.cmnd_topic("Mem"), ""))

    @pyqtSlot()
    def openWebUI(self):
        if self.device and self.device.p.get('IPAddress'):
            url = QUrl("http://{}".format(self.device.p['IPAddress']))

            try:
                webui = QWebEngineView()
                webui.load(url)

                frm_webui = QFrame()
                frm_webui.setWindowTitle("WebUI [{}]".format(self.device.name))
                frm_webui.setFrameShape(QFrame.StyledPanel)
                frm_webui.setLayout(VLayout(0))
                frm_webui.layout().addWidget(webui)
                frm_webui.destroyed.connect(self.updateMDI)

                self.mdi.addSubWindow(frm_webui)
                self.mdi.setViewMode(QMdiArea.TabbedView)
                frm_webui.setWindowState(Qt.WindowMaximized)

            except NameError:
                QDesktopServices.openUrl(
                    QUrl("http://{}".format(self.device.p['IPAddress'])))

    def updateMDI(self):
        if len(self.mdi.subWindowList()) == 1:
            self.mdi.setViewMode(QMdiArea.SubWindowView)
            self.devices_list.setWindowState(Qt.WindowMaximized)

    def closeEvent(self, e):
        self.settings.setValue("version", self._version)
        self.settings.setValue("window_geometry", self.saveGeometry())
        self.settings.setValue("views_order",
                               ";".join(self.devices_list.views.keys()))

        self.settings.beginGroup("Views")
        for view, items in self.devices_list.views.items():
            self.settings.setValue(view, ";".join(items[1:]))
        self.settings.endGroup()

        self.settings.sync()

        for d in self.env.devices:
            mac = d.p.get('Mac')
            topic = d.p['Topic']
            full_topic = d.p['FullTopic']
            device_name = d.name

            if mac:
                self.devices.beginGroup(mac.replace(":", "-"))
                self.devices.setValue("topic", topic)
                self.devices.setValue("full_topic", full_topic)
                self.devices.setValue("device_name", device_name)

                for i, h in enumerate(d.history):
                    self.devices.setValue("history/{}".format(i), h)
                self.devices.endGroup()
        self.devices.sync()

        e.accept()
class DataRecordUI(QWidget):
    receiveLogSignal = pyqtSignal(str)

    def __init__(self):
        super(DataRecordUI, self).__init__()
        loadUi('./identity/ui/DataRecord.ui', self)
        self.setWindowIcon(QIcon('../identity/icons/icon.png'))
        self.setFixedSize(1011, 601)
        self.setWindowTitle('pc端个人隐私防护系统 - 人脸采集')

        # OpenCV
        self.cap = cv2.VideoCapture()
        self.faceCascade = cv2.CascadeClassifier(
            './identity/haarcascades/haarcascade_frontalface_default.xml')
        self.logQueue = queue.Queue()  # 日志队列

        # 图像捕获
        self.isExternalCameraUsed = False
        self.useExternalCameraCheckBox.stateChanged.connect(
            lambda: self.useExternalCamera(self.useExternalCameraCheckBox))
        self.startWebcamButton.toggled.connect(self.startWebcam)
        self.startWebcamButton.setCheckable(True)

        # 定时器
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.updateFrame)

        # 人脸检测
        self.isFaceDetectEnabled = False
        self.enableFaceDetectButton.toggled.connect(self.enableFaceDetect)
        self.enableFaceDetectButton.setCheckable(True)

        # 数据库
        self.database = './identity/FaceBase.db'
        self.datasets = './identity/datasets'
        self.isDbReady = False
        self.initDbButton.setIcon(QIcon('../identity/icons/warning.png'))
        self.initDbButton.clicked.connect(self.initDb)

        # 用户信息
        self.isUserInfoReady = False
        self.userInfo = {'stu_id': '', 'cn_name': '', 'en_name': ''}
        self.addOrUpdateUserInfoButton.clicked.connect(
            self.addOrUpdateUserInfo)
        self.migrateToDbButton.clicked.connect(self.migrateToDb)

        # 人脸采集
        self.startFaceRecordButton.clicked.connect(
            lambda: self.startFaceRecord(self.startFaceRecordButton))
        self.faceRecordCount = 0
        self.minFaceRecordCount = 100  #最低采集张数,可以根据安全需求改变
        self.isFaceDataReady = False
        self.isFaceRecordEnabled = False
        self.enableFaceRecordButton.clicked.connect(self.enableFaceRecord)

        # 日志系统
        self.receiveLogSignal.connect(lambda log: self.logOutput(log))
        self.logOutputThread = threading.Thread(target=self.receiveLog,
                                                daemon=True)
        self.logOutputThread.start()

    # 是否使用外接摄像头
    def useExternalCamera(self, useExternalCameraCheckBox):
        if useExternalCameraCheckBox.isChecked():
            self.isExternalCameraUsed = True
        else:
            self.isExternalCameraUsed = False

    # 打开/关闭摄像头
    def startWebcam(self, status):
        if status:
            if self.isExternalCameraUsed:
                camID = 1
            else:
                camID = 0
            self.cap.open(camID)
            self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
            self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
            ret, frame = self.cap.read()

            if not ret:
                logging.error('无法调用电脑摄像头{}'.format(camID))
                self.logQueue.put('Error:初始化摄像头失败')
                self.cap.release()
                self.startWebcamButton.setIcon(
                    QIcon('../identity/icons/error.png'))
                self.startWebcamButton.setChecked(False)
            else:
                self.startWebcamButton.setText('关闭摄像头')
                self.enableFaceDetectButton.setEnabled(True)
                self.timer.start(5)
                self.startWebcamButton.setIcon(
                    QIcon('../identity/icons/success.png'))
        else:
            if self.cap.isOpened():
                if self.timer.isActive():
                    self.timer.stop()
                self.cap.release()
                self.faceDetectCaptureLabel.clear()
                self.faceDetectCaptureLabel.setText(
                    '<font color=red>摄像头未开启</font>')
                self.startWebcamButton.setText('打开摄像头')
                self.enableFaceDetectButton.setEnabled(False)
                self.startWebcamButton.setIcon(QIcon())

    # 开启/关闭人脸检测
    def enableFaceDetect(self, status):
        if self.cap.isOpened():
            if status:
                self.enableFaceDetectButton.setText('关闭人脸检测')
                self.isFaceDetectEnabled = True
            else:
                self.enableFaceDetectButton.setText('开启人脸检测')
                self.isFaceDetectEnabled = False

    # 采集当前捕获帧
    def enableFaceRecord(self):
        if not self.isFaceRecordEnabled:
            self.isFaceRecordEnabled = True

    # 开始/结束采集人脸数据
    def startFaceRecord(self, startFaceRecordButton):
        if startFaceRecordButton.text() == '开始采集人脸数据':
            if self.isFaceDetectEnabled:
                if self.isUserInfoReady:
                    self.addOrUpdateUserInfoButton.setEnabled(False)
                    if not self.enableFaceRecordButton.isEnabled():
                        self.enableFaceRecordButton.setEnabled(True)
                    self.enableFaceRecordButton.setIcon(QIcon())
                    self.startFaceRecordButton.setIcon(
                        QIcon('../identity/icons/success.png'))
                    self.startFaceRecordButton.setText('结束当前人脸采集')
                else:
                    self.startFaceRecordButton.setIcon(
                        QIcon('../identity/icons/error.png'))
                    self.startFaceRecordButton.setChecked(False)
                    self.logQueue.put('Error:操作失败,系统未检测到有效的用户信息')
            else:
                self.startFaceRecordButton.setIcon(
                    QIcon('../identity/icons/error.png'))
                self.logQueue.put('Error:操作失败,请开启人脸检测')
        else:
            if self.faceRecordCount < self.minFaceRecordCount:
                text = '系统当前采集了 <font color=blue>{}</font> 帧图像,采集数据过少会导致较大的识别误差。'.format(
                    self.faceRecordCount)
                informativeText = '<b>请至少采集 <font color=red>{}</font> 帧图像。</b>'.format(
                    self.minFaceRecordCount)
                DataRecordUI.callDialog(QMessageBox.Information, text,
                                        informativeText, QMessageBox.Ok)

            else:
                text = '系统当前采集了 <font color=blue>{}</font> 帧图像,继续采集可以提高识别准确率。'.format(
                    self.faceRecordCount)
                informativeText = '<b>你确定结束当前人脸采集吗?</b>'
                ret = DataRecordUI.callDialog(QMessageBox.Question, text,
                                              informativeText,
                                              QMessageBox.Yes | QMessageBox.No,
                                              QMessageBox.No)

                if ret == QMessageBox.Yes:
                    self.isFaceDataReady = True
                    if self.isFaceRecordEnabled:
                        self.isFaceRecordEnabled = False
                    self.enableFaceRecordButton.setEnabled(False)
                    self.enableFaceRecordButton.setIcon(QIcon())
                    self.startFaceRecordButton.setText('开始采集人脸数据')
                    self.startFaceRecordButton.setEnabled(False)
                    self.startFaceRecordButton.setIcon(QIcon())
                    self.migrateToDbButton.setEnabled(True)

    # 定时器,实时更新画面
    def updateFrame(self):
        ret, frame = self.cap.read()
        if ret:
            self.displayImage(frame)

            if self.isFaceDetectEnabled:
                detected_frame = self.detectFace(frame)
                self.displayImage(detected_frame)
            else:
                self.displayImage(frame)

    # 检测人脸
    def detectFace(self, frame):
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        faces = self.faceCascade.detectMultiScale(gray,
                                                  1.3,
                                                  5,
                                                  minSize=(90, 90))
        stu_id = self.userInfo.get('stu_id')
        for (x, y, w, h) in faces:
            if self.isFaceRecordEnabled:
                try:
                    if not os.path.exists('{}/stu_{}'.format(
                            self.datasets, stu_id)):
                        os.makedirs('{}/stu_{}'.format(self.datasets, stu_id))
                    if len(faces) > 1:
                        raise RecordDisturbance

                    cv2.imwrite(
                        '{}/stu_{}/img.{}.jpg'.format(
                            self.datasets, stu_id, self.faceRecordCount + 1),
                        gray[y - 20:y + h + 20, x - 20:x + w + 20])
                except RecordDisturbance:
                    self.isFaceRecordEnabled = False
                    logging.error('检测到多张人脸或环境干扰')
                    self.logQueue.put('Warning:检测到多张人脸或环境干扰,请解决问题后继续')
                    self.enableFaceRecordButton.setIcon(
                        QIcon('../identity/icons/warning.png'))
                    continue
                except Exception as e:
                    logging.error('写入人脸图像文件到计算机过程中发生异常')
                    self.enableFaceRecordButton.setIcon(
                        QIcon('../identity/icons/error.png'))
                    self.logQueue.put('Error:无法保存人脸图像,采集当前捕获帧失败')
                else:
                    self.enableFaceRecordButton.setIcon(
                        QIcon('../identity/icons/success.png'))
                    self.faceRecordCount = self.faceRecordCount + 1
                    if self.faceRecordCount > 100:
                        self.isFaceRecordEnabled = False
                    self.faceRecordCountLcdNum.display(self.faceRecordCount)
            cv2.rectangle(frame, (x - 5, y - 10), (x + w + 5, y + h + 10),
                          (0, 0, 255), 2)
        return frame

    # 显示图像
    def displayImage(self, img):
        # BGR -> RGB
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        # default:The image is stored using 8-bit indexes into a colormap, for example:a gray image
        qformat = QImage.Format_Indexed8

        if len(img.shape) == 3:  # rows[0], cols[1], channels[2]
            if img.shape[2] == 4:
                # The image is stored using a 32-bit byte-ordered RGBA format (8-8-8-8)
                # A: alpha channel,不透明度参数。如果一个像素的alpha通道数值为0%,那它就是完全透明的
                qformat = QImage.Format_RGBA8888
            else:
                qformat = QImage.Format_RGB888

        outImage = QImage(img, img.shape[1], img.shape[0], img.strides[0],
                          qformat)
        self.faceDetectCaptureLabel.setPixmap(QPixmap.fromImage(outImage))
        self.faceDetectCaptureLabel.setScaledContents(True)

    # 初始化数据库
    def initDb(self):
        conn = sqlite3.connect(self.database)
        cursor = conn.cursor()
        try:
            # 检测人脸数据目录是否存在,不存在则创建
            if not os.path.isdir(self.datasets):
                os.makedirs(self.datasets)

            # 查询数据表是否存在,不存在则创建
            cursor.execute('''CREATE TABLE IF NOT EXISTS users (
                              stu_id VARCHAR(12) PRIMARY KEY NOT NULL,
                              face_id INTEGER DEFAULT -1,
                              cn_name VARCHAR(10) NOT NULL,
                              en_name VARCHAR(16) NOT NULL,
                              created_time DATE DEFAULT (date('now','localtime'))
                              )
                          ''')
            # 查询数据表记录数
            cursor.execute('SELECT Count(*) FROM users')
            result = cursor.fetchone()
            dbUserCount = result[0]
        except Exception as e:
            logging.error('读取数据库异常,无法完成数据库初始化')
            self.isDbReady = False
            self.initDbButton.setIcon(QIcon('../identity/icons/error.png'))
            self.logQueue.put('Error:初始化数据库失败')
        else:
            self.isDbReady = True
            self.dbUserCountLcdNum.display(dbUserCount)
            self.logQueue.put('Success:数据库初始化完成')
            self.initDbButton.setIcon(QIcon('../identity/icons/success.png'))
            self.initDbButton.setEnabled(False)
            self.addOrUpdateUserInfoButton.setEnabled(True)
        finally:
            cursor.close()
            conn.commit()
            conn.close()

    # 增加/修改用户信息
    def addOrUpdateUserInfo(self):
        self.userInfoDialog = UserInfoDialog()

        stu_id, cn_name, en_name = self.userInfo.get(
            'stu_id'), self.userInfo.get('cn_name'), self.userInfo.get(
                'en_name')
        self.userInfoDialog.stuIDLineEdit.setText(stu_id)
        self.userInfoDialog.cnNameLineEdit.setText(cn_name)
        self.userInfoDialog.enNameLineEdit.setText(en_name)

        self.userInfoDialog.okButton.clicked.connect(self.checkToApplyUserInfo)
        self.userInfoDialog.exec()

    # 校验用户信息并提交
    def checkToApplyUserInfo(self):
        if not (self.userInfoDialog.stuIDLineEdit.hasAcceptableInput()
                and self.userInfoDialog.cnNameLineEdit.hasAcceptableInput()
                and self.userInfoDialog.enNameLineEdit.hasAcceptableInput()):
            self.userInfoDialog.msgLabel.setText(
                '<font color=red>你的输入有误,提交失败,请检查并重试!</font>')
        else:
            # 获取用户输入
            self.userInfo['stu_id'] = self.userInfoDialog.stuIDLineEdit.text(
            ).strip()
            self.userInfo['cn_name'] = self.userInfoDialog.cnNameLineEdit.text(
            ).strip()
            self.userInfo['en_name'] = self.userInfoDialog.enNameLineEdit.text(
            ).strip()

            # 信息确认
            stu_id, cn_name, en_name = self.userInfo.get(
                'stu_id'), self.userInfo.get('cn_name'), self.userInfo.get(
                    'en_name')
            self.stuIDLineEdit.setText(stu_id)
            self.cnNameLineEdit.setText(cn_name)
            self.enNameLineEdit.setText(en_name)

            self.isUserInfoReady = True
            if not self.startFaceRecordButton.isEnabled():
                self.startFaceRecordButton.setEnabled(True)
            self.migrateToDbButton.setIcon(QIcon())

            # 关闭对话框
            self.userInfoDialog.close()

    # 同步用户信息到数据库
    def migrateToDb(self):
        if self.isFaceDataReady:
            stu_id, cn_name, en_name = self.userInfo.get(
                'stu_id'), self.userInfo.get('cn_name'), self.userInfo.get(
                    'en_name')
            conn = sqlite3.connect(self.database)
            cursor = conn.cursor()

            try:
                cursor.execute('SELECT * FROM users WHERE stu_id=?',
                               (stu_id, ))
                if cursor.fetchall():
                    text = '数据库已存在学号为 <font color=blue>{}</font> 的用户记录。'.format(
                        stu_id)
                    informativeText = '<b>是否覆盖?</b>'
                    ret = DataRecordUI.callDialog(
                        QMessageBox.Warning, text, informativeText,
                        QMessageBox.Yes | QMessageBox.No)

                    if ret == QMessageBox.Yes:
                        # 更新已有记录
                        cursor.execute(
                            'UPDATE users SET cn_name=?, en_name=? WHERE stu_id=?',
                            (
                                cn_name,
                                en_name,
                                stu_id,
                            ))
                    else:
                        raise OperationCancel  # 记录取消覆盖操作
                else:
                    # 插入新记录
                    cursor.execute(
                        'INSERT INTO users (stu_id, cn_name, en_name) VALUES (?, ?, ?)',
                        (
                            stu_id,
                            cn_name,
                            en_name,
                        ))

                cursor.execute('SELECT Count(*) FROM users')
                result = cursor.fetchone()
                dbUserCount = result[0]
            except OperationCancel:
                pass
            except Exception as e:
                logging.error('读写数据库异常,无法向数据库插入/更新记录')
                self.migrateToDbButton.setIcon(
                    QIcon('../identity/icons/error.png'))
                self.logQueue.put('Error:读写数据库异常,同步失败')
            else:
                text = '<font color=blue>{}</font> 已添加/更新到数据库。'.format(stu_id)
                informativeText = '<b><font color=blue>{}</font> 的人脸数据采集已完成!</b>'.format(
                    cn_name)
                DataRecordUI.callDialog(QMessageBox.Information, text,
                                        informativeText, QMessageBox.Ok)

                # 清空用户信息缓存
                for key in self.userInfo.keys():
                    self.userInfo[key] = ''
                self.isUserInfoReady = False

                self.faceRecordCount = 0
                self.isFaceDataReady = False
                self.faceRecordCountLcdNum.display(self.faceRecordCount)
                self.dbUserCountLcdNum.display(dbUserCount)

                # 清空历史输入
                self.stuIDLineEdit.clear()
                self.cnNameLineEdit.clear()
                self.enNameLineEdit.clear()
                self.migrateToDbButton.setIcon(
                    QIcon('../identity/icons/success.png'))

                # 允许继续增加新用户
                self.addOrUpdateUserInfoButton.setEnabled(True)
                self.migrateToDbButton.setEnabled(False)

            finally:
                cursor.close()
                conn.commit()
                conn.close()
        else:
            self.logQueue.put('Error:操作失败,你尚未完成人脸数据采集')
            self.migrateToDbButton.setIcon(
                QIcon('../identity/icons/error.png'))

    # 系统日志服务常驻,接收并处理系统日志
    def receiveLog(self):
        while True:
            data = self.logQueue.get()
            if data:
                self.receiveLogSignal.emit(data)
            else:
                continue

    # LOG输出
    def logOutput(self, log):
        # 获取当前系统时间
        time = datetime.now().strftime('[%Y/%m/%d %H:%M:%S]')
        log = time + ' ' + log + '\n'

        self.logTextEdit.moveCursor(QTextCursor.End)
        self.logTextEdit.insertPlainText(log)
        self.logTextEdit.ensureCursorVisible()  # 自动滚屏

    # 系统对话框
    @staticmethod
    def callDialog(icon,
                   text,
                   informativeText,
                   standardButtons,
                   defaultButton=None):
        msg = QMessageBox()
        msg.setWindowIcon(QIcon('../identity/icons/icon.png'))
        msg.setWindowTitle('pc端个人隐私防护系统 - 人脸采集')
        msg.setIcon(icon)
        msg.setText(text)
        msg.setInformativeText(informativeText)
        msg.setStandardButtons(standardButtons)
        if defaultButton:
            msg.setDefaultButton(defaultButton)
        return msg.exec()

    # 窗口关闭事件,关闭定时器、摄像头
    def closeEvent(self, event):
        if self.timer.isActive():
            self.timer.stop()
        if self.cap.isOpened():
            self.cap.release()
        event.accept()
Ejemplo n.º 50
0
class SphericalBackend(QObject, MultiBackend):
    printDurationMessage = Signal()
    slicingStarted = Signal()
    slicingCancelled = Signal()
    backendError = Signal()

    def __init__(self):
        super().__init__()
        self._application = SteSlicerApplication.getInstance()
        self._multi_build_plate_model = None  # type: Optional[MultiBuildPlateModel]
        self._machine_error_checker = None  # type: Optional[MachineErrorChecker]
        self._backend_manager = self._application.getBackendManager(
        )  #type: BackendManager
        if self._backend_manager is None:
            Logger.log("e", "Failed to load BackendManager!")

        self._states = []
        # Workaround to disable layer view processing if layer view is not active.
        self._layer_view_active = False  # type: bool
        self._onActiveViewChanged()

        self._stored_layer_data = []
        self._stored_optimized_layer_data = {}

        self._scene = self._application.getController().getScene(
        )  # type: Scene
        self._scene.sceneChanged.connect(self._onSceneChanged)

        self._global_container_stack = None  # type: Optional[ContainerStack]

        self._slice_messages = []  #type: List[Any]
        self._start_slice_job_build_plate = None  # type: Optional[int]
        self._start_slice_job = None  #type: Optional[StartSliceJob]
        self._generate_basement_job = None
        self._glicer_process = None
        self._layers_size = 0
        self._classic_layers_size = 0
        self._slicing = False  # type: bool # Are we currently slicing?
        self._restart = False  # type: bool # Back-end is currently restarting?
        self._tool_active = False  # type: bool # If a tool is active, some tasks do not have to do anything
        self._always_restart = True  # type: bool # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
        self._build_plates_to_be_sliced = [
        ]  # type: List[int] # what needs slicing?
        self._engine_is_fresh = True  # type: bool # Is the newly started engine used before or not?

        self._backend_log_max_lines = 20000  # type: int # Maximum number of lines to buffer
        self._error_message = None  # type: Optional[Message] # Pop-up message that shows errors.
        self._last_num_objects = defaultdict(
            int
        )  # type: Dict[int, int] # Count number of objects to see if there is something changed
        self._postponed_scene_change_sources = [
        ]  # type: List[SceneNode] # scene change is postponed (by a tool)
        self._process_layers_job = None
        self._process_mesh_job = None
        self._slice_start_time = None  # type: Optional[float]
        self._is_disabled = False  # type: bool

        self._material_amounts = []
        self._times = {}

        self._application.getPreferences().addPreference(
            "general/auto_slice", False)

        self._use_timer = False  # type: bool
        # When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired.
        # This timer will group them up, and only slice for the last setting changed signal.
        # TODO: Properly group propertyChanged signals by whether they are triggered by the same user interaction.
        self._change_timer = QTimer()  # type: QTimer
        self._change_timer.setSingleShot(True)
        self._change_timer.setInterval(500)
        self.determineAutoSlicing()
        self._application.getPreferences().preferenceChanged.connect(
            self._onPreferencesChanged)

        self._application.initializationFinished.connect(self.initialize)

    def initialize(self) -> None:
        self._loadBackends()

        self._multi_build_plate_model = self._application.getMultiBuildPlateModel(
        )

        self._application.getController().activeViewChanged.connect(
            self._onActiveViewChanged)

        if self._multi_build_plate_model:
            self._multi_build_plate_model.activeBuildPlateChanged.connect(
                self._onActiveViewChanged)

        self._application.globalContainerStackChanged.connect(
            self._onGlobalStackChanged)
        self._onGlobalStackChanged()

        # extruder enable / disable. Actually wanted to use machine manager here, but the initialization order causes it to crash
        ExtruderManager.getInstance().extrudersChanged.connect(
            self._extruderChanged)

        # self.backendQuit.connect(self._onBackendQuit)
        # self.backendConnected.connect(self._onBackendConnected)

        # When a tool operation is in progress, don't slice. So we need to listen for tool operations.
        self._application.getController().toolOperationStarted.connect(
            self._onToolOperationStarted)
        self._application.getController().toolOperationStopped.connect(
            self._onToolOperationStopped)

        self._machine_error_checker = self._application.getMachineErrorChecker(
        )
        self._machine_error_checker.errorCheckFinished.connect(
            self._onStackErrorCheckFinished)

    def _loadBackends(self) -> None:
        classic_backend = self._backend_manager.getBackendByType("classic")
        spherical_backend = self._backend_manager.getBackendByType("spherical")
        if classic_backend is None or spherical_backend is None:
            raise ModuleNotFoundError("Not all Backends found")
        self._states = [BackendState.NotStarted, BackendState.NotStarted]

        self.addBackend(classic_backend)
        self.addBackend(spherical_backend)

    def close(self) -> None:
        for name, backend in self.getBackends().items():
            backend._terminate()

    @pyqtSlot()
    def stopSlicing(self) -> None:
        self.backendStateChange.emit(BackendState.NotStarted)
        if self._slicing:
            self.close()

        if self._generate_basement_job is not None:
            Logger.log("d", "Aborting generate basement job...")
            self._generate_basement_job.abort()
            self._generate_basement_job = None

        if self._process_mesh_job is not None:
            Logger.log("d", "Aborting process mesh job...")
            self._process_mesh_job.abort()
            self._process_mesh_job = None

        if self._process_layers_job is not None:
            Logger.log("d", "Aborting process layers job...")
            self._process_layers_job.abort()
            self._process_layers_job = None

        if self._error_message:
            self._error_message.hide()

    @pyqtSlot()
    def forceSlice(self) -> None:
        self.markSliceAll()
        self.slice()

    def slice(self) -> None:
        Logger.log("d", "Starting to slice...")
        self._material_amounts = []
        self._times = {}
        self._slice_start_time = time()
        if not self._build_plates_to_be_sliced:
            self.processingProgress.emit(1.0)
            Logger.log(
                "w",
                "Slice unnecessary, nothing has changed that needs reslicing.")
            return

        if self._process_layers_job:
            Logger.log("d", "Process layers job still busy, trying later.")
            return

        if not hasattr(self._scene, "gcode_dict"):
            self._scene.gcode_dict = {
            }  #type: ignore #Because we are creating the missing attribute here.

        # see if we really have to slice
        active_build_plate = self._application.getMultiBuildPlateModel(
        ).activeBuildPlate
        build_plate_to_be_sliced = self._build_plates_to_be_sliced.pop(0)
        Logger.log(
            "d", "Going to slice build plate [%s]!" % build_plate_to_be_sliced)
        num_objects = self._numObjectsPerBuildPlate()

        self._stored_layer_data = []
        self._stored_optimized_layer_data[build_plate_to_be_sliced] = []

        if build_plate_to_be_sliced not in num_objects or num_objects[
                build_plate_to_be_sliced] == 0:
            self._scene.gcode_dict[build_plate_to_be_sliced] = []
            Logger.log("d",
                       "Build plate %s has no objects to be sliced, skipping",
                       build_plate_to_be_sliced)
            if self._build_plates_to_be_sliced:
                self.slice()
            return

        if self._application.getPrintInformation(
        ) and build_plate_to_be_sliced == active_build_plate:
            self._application.getPrintInformation().setToZeroPrintInformation(
                build_plate_to_be_sliced)

        self.stopSlicing()

        self.processingProgress.emit(0.0)
        self.backendStateChange.emit(BackendState.NotStarted)

        self._scene.gcode_dict[build_plate_to_be_sliced] = []
        self._slicing = True
        self.slicingStarted.emit()

        self.determineAutoSlicing()

        self._start_slice_job = StartSliceJob(self.getBackends())
        self._start_slice_job_build_plate = build_plate_to_be_sliced
        self._start_slice_job.setBuildPlate(self._start_slice_job_build_plate)
        self._start_slice_job.start()
        self._start_slice_job.finished.connect(self._onStartSliceCompleted)

    def _onStartSliceCompleted(self, job) -> None:
        if self._error_message:
            self._error_message.hide()

        if self._start_slice_job is job:
            self._start_slice_job = None

        if job.isCancelled() or job.getError() or job.getResult(
        ) == StartJobResult.Error:
            self.backendStateChange.emit(BackendState.Error)
            self.backendError.emit(job)
            return

        if job.getResult() == StartJobResult.MaterialIncompatible:
            if self._application.platformActivity:
                self._error_message = Message(catalog.i18nc(
                    "@info:status",
                    "Unable to slice with the current material as it is incompatible with the selected machine or configuration."
                ),
                                              title=catalog.i18nc(
                                                  "@info:title",
                                                  "Unable to slice"))
                self._error_message.show()
                self.backendStateChange.emit(BackendState.Error)
                self.backendError.emit(job)
            else:
                self.backendStateChange.emit(BackendState.NotStarted)
            return

        if job.getResult() == StartJobResult.SettingError:
            if self._application.platformActivity:
                if not self._global_container_stack:
                    Logger.log(
                        "w",
                        "Global container stack not assigned to GlicerBackend!"
                    )
                    return

        if job.getResult() == StartJobResult.BuildPlateError:
            if self._application.platformActivity:
                self._error_message = Message(catalog.i18nc(
                    "@info:status",
                    "Unable to slice because the prime tower or prime position(s) are invalid."
                ),
                                              title=catalog.i18nc(
                                                  "@info:title",
                                                  "Unable to slice"))
                self._error_message.show()
                self.backendStateChange.emit(BackendState.Error)
                self.backendError.emit(job)
            else:
                self.backendStateChange.emit(BackendState.NotStarted)

        if job.getResult() == StartJobResult.ObjectsWithDisabledExtruder:
            self._error_message = Message(catalog.i18nc(
                "@info:status",
                "Unable to slice because there are objects associated with disabled Extruder %s."
                % job.getMessage()),
                                          title=catalog.i18nc(
                                              "@info:title",
                                              "Unable to slice"))
            self._error_message.show()
            self.backendStateChange.emit(BackendState.Error)
            self.backendError.emit(job)
            return

        if job.getResult() == StartJobResult.NothingToSlice:
            if self._application.platformActivity:
                self._error_message = Message(catalog.i18nc(
                    "@info:status",
                    "Nothing to slice because none of the models fit the build volume. Please scale or rotate models to fit."
                ),
                                              title=catalog.i18nc(
                                                  "@info:title",
                                                  "Unable to slice"))
                self._error_message.show()
                self.backendStateChange.emit(BackendState.Error)
                self.backendError.emit(job)
            else:
                self.backendStateChange.emit(BackendState.NotStarted)
            self._invokeSlice()
            return

        self.backendStateChange.emit(BackendState.Processing)
        self.processingProgress.emit(0.0)
        self._slice_messages = job.getSliceMessages()

        self._generate_basement_job = GenerateBasementJob()
        self._generate_basement_job.processingProgress.connect(
            self._onGenerateBasementProcessingProgress)
        self._generate_basement_job.finished.connect(
            self._onGenerateBasementJobFinished)
        self._generate_basement_job.start()

    def _onGenerateBasementProcessingProgress(self, amount):
        self.processingProgress.emit(amount / 10)
        self.backendStateChange.emit(BackendState.Processing)

    def _onGenerateBasementJobFinished(self, job: GenerateBasementJob):
        if not self._scene.gcode_dict:
            self._scene.gcode_dict = {0: []}
        if not self._scene.gcode_dict[self._start_slice_job_build_plate]:
            self._scene.gcode_dict[self._start_slice_job_build_plate] = []
        self._scene.gcode_dict[self._start_slice_job_build_plate].extend(
            job.getGCodeList())

        if self._start_slice_job_build_plate is not None:
            if self._start_slice_job_build_plate not in self._stored_optimized_layer_data:
                self._stored_optimized_layer_data[
                    self._start_slice_job_build_plate] = []
        self._stored_optimized_layer_data[
            self._start_slice_job_build_plate].extend(job.getLayersData())
        self._classic_layers_size = len(self._stored_optimized_layer_data[
            self._start_slice_job_build_plate])
        self._layers_size = self._classic_layers_size
        if self._generate_basement_job is job:
            self._generate_basement_job = None
        # sending to the first backend
        slice_message = self._slice_messages[0]
        if isinstance(slice_message, PythonMessage):
            self._backends["CuraEngineBackend"]._terminate()
            self._backends["CuraEngineBackend"]._createSocket()
            Logger.log("d", "Sending Arcus Message")
            # Listeners for receiving messages from the back-end.
            self._backends["CuraEngineBackend"]._message_handlers[
                "cura.proto.Layer"] = self._onLayerMessage
            self._backends["CuraEngineBackend"]._message_handlers[
                "cura.proto.LayerOptimized"] = self._onOptimizedLayerMessage
            self._backends["CuraEngineBackend"]._message_handlers[
                "cura.proto.Progress"] = self._onProgressMessage
            self._backends["CuraEngineBackend"]._message_handlers[
                "cura.proto.GCodeLayer"] = self._onGCodeLayerMessage
            self._backends["CuraEngineBackend"]._message_handlers[
                "cura.proto.GCodePrefix"] = self._onGCodePrefixMessage
            self._backends["CuraEngineBackend"]._message_handlers[
                "cura.proto.PrintTimeMaterialEstimates"] = self._onPrintTimeMaterialEstimates
            self._backends["CuraEngineBackend"]._message_handlers[
                "cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage
            self._backends["CuraEngineBackend"]._socket.sendMessage(
                slice_message)

    def _sendGlicerSliceMessage(self, slice_message):
        if isinstance(slice_message, List):
            Logger.log("d", "Sending Engine Message")
            slice_message.append('-o')
            filename = next(tempfile._get_candidate_names())
            output_path = [os.path.join(tempfile.tempdir, filename)]
            slice_message.extend(output_path)
            self._glicer_process = self._runGlicerEngineProcess(slice_message)

            self._startProcessMeshLayersJob(
                output_path,
                self._application.getMultiBuildPlateModel().activeBuildPlate,
                self._slice_messages[2])
        if self._slice_start_time:
            Logger.log("d", "Sending slice message took %s seconds",
                       time() - self._slice_start_time)

    def _onLayerMessage(self, message: Arcus.PythonMessage) -> None:
        self._stored_layer_data.append(message)

    def _onOptimizedLayerMessage(self, message: Arcus.PythonMessage) -> None:
        if self._start_slice_job_build_plate is not None:
            if self._start_slice_job_build_plate not in self._stored_optimized_layer_data:
                self._stored_optimized_layer_data[
                    self._start_slice_job_build_plate] = []
            self._stored_optimized_layer_data[
                self._start_slice_job_build_plate].append(message)

    def _onMeshOptimizedLayerMessage(self,
                                     message: Arcus.PythonMessage) -> None:
        if self._start_slice_job_build_plate is not None:
            if self._start_slice_job_build_plate not in self._stored_optimized_layer_data:
                self._stored_optimized_layer_data[
                    self._start_slice_job_build_plate] = []
            message.id += self._classic_layers_size
            self._stored_optimized_layer_data[
                self._start_slice_job_build_plate].append(message)

    def _onProgressMessage(self, message: Arcus.PythonMessage) -> None:
        self.processingProgress.emit(0.1 + message.amount / 2.5)
        self.backendStateChange.emit(BackendState.Processing)

    def _onLayersProcessorProgressMessage(
            self, message: Arcus.PythonMessage) -> None:
        self.processingProgress.emit(0.6 + message.amount / 2.5)
        self.backendStateChange.emit(BackendState.Processing)

    def _onGCodeLayerMessage(self, message: Arcus.PythonMessage) -> None:
        if not self._scene.gcode_dict:
            self._scene.gcode_dict = {0: []}
        if not self._scene.gcode_dict[self._start_slice_job_build_plate]:
            self._scene.gcode_dict[self._start_slice_job_build_plate] = []
        msg = message.data.decode("utf-8", "replace")  # type: str
        #TODO: Remove this since new basement will have start and end gcode
        if msg.startswith(";Generated with Cura_SteamEngine"):
            self._scene.gcode_dict[self._start_slice_job_build_plate].insert(
                0, msg)
        else:
            self._scene.gcode_dict[self._start_slice_job_build_plate].append(
                msg
            )  #type: ignore #Because we generate this attribute dynamically.

    def _onGCodePrefixMessage(self, message: Arcus.PythonMessage) -> None:
        if not self._scene.gcode_dict:
            self._scene.gcode_dict = {0: []}
        if not self._scene.gcode_dict[self._start_slice_job_build_plate]:
            self._scene.gcode_dict[self._start_slice_job_build_plate] = []
        self._scene.gcode_dict[self._start_slice_job_build_plate].insert(
            0, message.data.decode("utf-8", "replace")
        )  # type: ignore #Because we generate this attribute dynamically.

    def _onPrintTimeMaterialEstimates(self,
                                      message: Arcus.PythonMessage) -> None:
        material_amounts = []
        for index in range(message.repeatedMessageCount("materialEstimates")):
            material_amounts.append(
                message.getRepeatedMessage("materialEstimates",
                                           index).material_amount)
        if len(self._material_amounts) > 0:
            zipped_amounts = zip(self._material_amounts, material_amounts)
            # sum items in two lists
            self._material_amounts = [x + y for (x, y) in zipped_amounts]
        else:
            self._material_amounts = material_amounts

        self._times = self._parseMessagePrintTimes(message)
        self.printDurationMessage.emit(self._start_slice_job_build_plate,
                                       self._times, self._material_amounts)

    def _parseMessagePrintTimes(
            self, message: Arcus.PythonMessage) -> Dict[str, float]:
        result = {
            "inset_0":
            self._times.get("inset_0", 0) + message.time_inset_0,
            "inset_x":
            self._times.get("inset_x", 0) + message.time_inset_x,
            "skin":
            self._times.get("skin", 0) + message.time_skin,
            "infill":
            self._times.get("infill", 0) + message.time_infill,
            "support_infill":
            self._times.get("support_infill", 0) + message.time_support_infill,
            "support_interface":
            self._times.get("support_interface", 0) +
            message.time_support_interface,
            "support":
            self._times.get("support", 0) + message.time_support,
            "skirt":
            self._times.get("skirt", 0) + message.time_skirt,
            "travel":
            self._times.get("travel", 0) + message.time_travel,
            "retract":
            self._times.get("retract", 0) + message.time_retract,
            "none":
            self._times.get("none", 0) + message.time_none
        }
        return result

    def _onSlicingFinishedMessage(self, message: Arcus.PythonMessage) -> None:
        self._classic_layers_size = len(self._stored_optimized_layer_data[
            self._start_slice_job_build_plate]) - self._classic_layers_size
        self._layers_size += self._classic_layers_size

        self.backendStateChange.emit(BackendState.Processing)
        self.processingProgress.emit(0.5)

        self._backends["CuraEngineBackend"]._message_handlers = {}

        gcode_list = self._scene.gcode_dict[
            self.
            _start_slice_job_build_plate]  #type: ignore #Because we generate this attribute dynamically.
        for index, line in enumerate(gcode_list):
            replaced = line.replace(
                "{print_time}",
                str(self._application.getPrintInformation().currentPrintTime.
                    getDisplayString(DurationFormat.Format.ISO8601)))
            replaced = replaced.replace(
                "{filament_amount}",
                str(self._application.getPrintInformation().materialLengths))
            replaced = replaced.replace(
                "{filament_weight}",
                str(self._application.getPrintInformation().materialWeights))
            replaced = replaced.replace(
                "{filament_cost}",
                str(self._application.getPrintInformation().materialCosts))
            replaced = replaced.replace(
                "{jobname}",
                str(self._application.getPrintInformation().jobName))

            gcode_list[index] = replaced

        # Launch GlicerBackend here
        slice_message = self._slice_messages[1]
        self._sendGlicerSliceMessage(slice_message)

    def _runGlicerEngineProcess(self,
                                command_list) -> Optional[subprocess.Popen]:
        try:
            return subprocess.Popen(command_list)
        except PermissionError:
            Logger.log(
                "e",
                "Couldn't start back-end: No permission to execute process.")
        except FileNotFoundError:
            Logger.logException("e", "Unable to find backend executable: %s",
                                command_list[0])
        return None

    def _startProcessMeshLayersJob(self, output_path: List[str],
                                   build_plate_number: int,
                                   arcus_message: Arcus.PythonMessage) -> None:
        self._process_mesh_job = ProcessMeshJob(self._glicer_process,
                                                output_path, arcus_message)
        self._process_mesh_job.setBuildPlate(build_plate_number)
        self._process_mesh_job.finished.connect(self._onProcessMeshFinished)
        self._process_mesh_job.start()

    def _onTimeMaterialEstimates(self, material_amounts, times):
        zipped_amounts = zip(self._material_amounts, material_amounts)
        # sum items in two lists
        self._material_amounts = [x + y for (x, y) in zipped_amounts]
        # and sum items in two dicts
        self._times = {
            k: self._times.get(k, 0) + times.get(k, 0)
            for k in set(self._times)
        }
        self.printDurationMessage.emit(self._start_slice_job_build_plate,
                                       self._times, self._material_amounts)

    def _onProcessMeshFinished(self, job: ProcessMeshJob):
        # remove end gcode from Curaengine
        del self._scene.gcode_dict[self._start_slice_job_build_plate][-1]

        self._backends["LayersProcessorBackend"]._terminate()
        self._backends["LayersProcessorBackend"]._createSocket()
        Logger.log("d", "Sending Arcus Message")
        self._backends["LayersProcessorBackend"]._message_handlers[
            "layersprocessor.proto.LayerOptimized"] = self._onMeshOptimizedLayerMessage
        self._backends["LayersProcessorBackend"]._message_handlers[
            "layersprocessor.proto.Progress"] = self._onLayersProcessorProgressMessage
        self._backends["LayersProcessorBackend"]._message_handlers[
            "layersprocessor.proto.GCodeLayer"] = self._onGCodeLayerMessage
        self._backends["LayersProcessorBackend"]._message_handlers[
            "layersprocessor.proto.GCodePrefix"] = self._onGCodePrefixMessage
        self._backends["LayersProcessorBackend"]._message_handlers[
            "layersprocessor.proto.PrintTimeMaterialEstimates"] = self._onPrintTimeMaterialEstimates
        self._backends["LayersProcessorBackend"]._message_handlers[
            "layersprocessor.proto.SlicingFinished"] = self._onLayersProcessorFinishedMessage

        # Note that cancelled slice jobs can still call this method.
        if self._process_mesh_job is job:
            self._process_mesh_job = None

        self._backends["LayersProcessorBackend"]._socket.sendMessage(
            job.getSliceMessage())

        self.backendStateChange.emit(BackendState.Processing)
        self.processingProgress.emit(0.6)

        if self._slice_start_time:
            Logger.log("d", "Sending slice message took %s seconds",
                       time() - self._slice_start_time)

    def _onLayersProcessorFinishedMessage(
            self, message: Arcus.PythonMessage) -> None:
        self._classic_layers_size = 0
        self._layers_size = 0
        self.backendStateChange.emit(BackendState.Done)
        self.processingProgress.emit(1.0)

        gcode_list = self._scene.gcode_dict[
            self.
            _start_slice_job_build_plate]  # type: ignore #Because we generate this attribute dynamically.
        for index, line in enumerate(gcode_list):
            replaced = line.replace(
                "{print_time}",
                str(self._application.getPrintInformation().currentPrintTime.
                    getDisplayString(DurationFormat.Format.ISO8601)))
            replaced = replaced.replace(
                "{filament_amount}",
                str(self._application.getPrintInformation().materialLengths))
            replaced = replaced.replace(
                "{filament_weight}",
                str(self._application.getPrintInformation().materialWeights))
            replaced = replaced.replace(
                "{filament_cost}",
                str(self._application.getPrintInformation().materialCosts))
            replaced = replaced.replace(
                "{jobname}",
                str(self._application.getPrintInformation().jobName))

            gcode_list[index] = replaced

        if self._slice_start_time:
            Logger.log("d", "Slicing took %s seconds",
                       time() - self._slice_start_time)
        Logger.log("d", "Number of models per buildplate: %s",
                   dict(self._numObjectsPerBuildPlate()))

        # See if we need to process the sliced layers job.
        active_build_plate = self._application.getMultiBuildPlateModel(
        ).activeBuildPlate
        if (self._layer_view_active
                and (self._process_layers_job is None
                     or not self._process_layers_job.isRunning())
                and active_build_plate == self._start_slice_job_build_plate
                and active_build_plate not in self._build_plates_to_be_sliced):
            self._startProcessSlicedLayersJob(active_build_plate)
        # self._onActiveViewChanged()
        self._start_slice_job_build_plate = None

        Logger.log("d", "See if there is more to slice...")
        # Somehow this results in an Arcus Error
        # self.slice()
        # Call slice again using the timer, allowing the backend to restart
        if self._build_plates_to_be_sliced:
            self.enableTimer(
            )  # manually enable timer to be able to invoke slice, also when in manual slice mode
            self._invokeSlice()
        self._backends["LayersProcessorBackend"]._message_handlers = {}
        self._slicing = False

    def _startProcessSlicedLayersJob(self, build_plate_number: int) -> None:
        self._process_layers_job = ProcessSlicedLayersJob(
            self._stored_optimized_layer_data[build_plate_number])
        self._process_layers_job.setBuildPlate(build_plate_number)
        self._process_layers_job.finished.connect(
            self._onProcessLayersFinished)
        self._process_layers_job.start()

    def _onProcessLayersFinished(self, job: ProcessSlicedLayersJob):
        del self._stored_optimized_layer_data[job.getBuildPlate()]
        self._process_layers_job = None
        Logger.log("d", "See if there is more to slice(2)...")
        self._invokeSlice()

    def _terminate(self) -> None:
        self._slicing = False
        self._stored_layer_data = []
        if self._start_slice_job_build_plate in self._stored_optimized_layer_data:
            del self._stored_optimized_layer_data[
                self._start_slice_job_build_plate]
        if self._start_slice_job is not None:
            self._start_slice_job.cancel()
        if self._application.getUseExternalBackend():
            return
        Logger.log("d", "Killing engine process")
        try:
            for backend in self.getBackends():  # type: Backend
                backend._terminate()
        except Exception as e:  # terminating a process that is already terminating causes an exception, silently ignore this.
            Logger.log(
                "d", "Exception occurred while trying to kill the engine %s",
                str(e))

    ##  Called when the user changes the active view mode.
    def _onActiveViewChanged(self) -> None:
        view = self._application.getController().getActiveView()
        if view:
            active_build_plate = self._application.getMultiBuildPlateModel(
            ).activeBuildPlate
            if view.getPluginId(
            ) == "SimulationView":  # If switching to layer view, we should process the layers if that hasn't been done yet.
                self._layer_view_active = True
                # There is data and we're not slicing at the moment
                # if we are slicing, there is no need to re-calculate the data as it will be invalid in a moment.
                # TODO: what build plate I am slicing
                if (active_build_plate in self._stored_optimized_layer_data
                        and not self._slicing and not self._process_layers_job
                        and active_build_plate
                        not in self._build_plates_to_be_sliced):

                    self._startProcessSlicedLayersJob(active_build_plate)
            else:
                self._layer_view_active = False

    def _onSceneChanged(self, source: SceneNode) -> None:
        if not isinstance(source, SceneNode):
            return
        if source.callDecoration("isBlockSlicing") and source.callDecoration(
                "getLayerData"):
            self._stored_optimized_layer_data = {}

        build_plate_changed = set()
        source_build_plate_number = source.callDecoration(
            "getBuildPlateNumber")
        if source == self._scene.getRoot():
            num_objects = self._numObjectsPerBuildPlate()
            for build_plate_number in list(
                    self._last_num_objects.keys()) + list(num_objects.keys()):
                if build_plate_number not in self._last_num_objects or num_objects[build_plate_number] != \
                        self._last_num_objects[build_plate_number]:
                    self._last_num_objects[build_plate_number] = num_objects[
                        build_plate_number]
                    build_plate_changed.add(build_plate_number)
        else:
            if not source.callDecoration("isGroup"):
                mesh_data = source.getMeshData()
                if mesh_data is None or mesh_data.getVertices() is None:
                    return

            if source_build_plate_number is not None:
                build_plate_changed.add(source_build_plate_number)

        if not build_plate_changed:
            return

        if self._tool_active:
            if source not in self._postponed_scene_change_sources:
                self._postponed_scene_change_sources.append(source)
            return

        self.stopSlicing()
        for build_plate_number in build_plate_changed:
            if build_plate_number not in self._build_plates_to_be_sliced:
                self._build_plates_to_be_sliced.append(build_plate_number)
            self.printDurationMessage.emit(source_build_plate_number, {}, [])
        self.processingProgress.emit(0.0)
        self.backendStateChange.emit(BackendState.NotStarted)
        # if not self._use_timer:
        # With manually having to slice, we want to clear the old invalid layer data.
        self._clearLayerData(build_plate_changed)

        self._invokeSlice()

    def determineAutoSlicing(self) -> bool:
        enable_timer = True
        self._is_disabled = False

        if not self._application.getPreferences().getValue(
                "general/auto_slice"):
            enable_timer = False
        for node in DepthFirstIterator(
                self._scene.getRoot()
        ):  # type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
            if node.callDecoration("isBlockSlicing"):
                enable_timer = False
                self.backendStateChange.emit(BackendState.Disabled)
                self._is_disabled = True
            gcode_list = node.callDecoration("getGCodeList")
            if gcode_list is not None:
                self._scene.gcode_dict[node.callDecoration(
                    "getBuildPlateNumber"
                )] = gcode_list  # type: ignore #Because we generate this attribute dynamically.

        if self._use_timer == enable_timer:
            return self._use_timer
        if enable_timer:
            self.backendStateChange.emit(BackendState.NotStarted)
            self.enableTimer()
            return True
        else:
            self.disableTimer()
            return False

    def _onPreferencesChanged(self, preference: str) -> None:
        if preference != "general/auto_slice":
            return
        auto_slice = self.determineAutoSlicing()
        if auto_slice:
            self._change_timer.start()

    def _extruderChanged(self) -> None:
        if not self._multi_build_plate_model:
            Logger.log(
                "w",
                "GlicerBackend does not have multi_build_plate_model assigned!"
            )
            return
        for build_plate_number in range(
                self._multi_build_plate_model.maxBuildPlate + 1):
            if build_plate_number not in self._build_plates_to_be_sliced:
                self._build_plates_to_be_sliced.append(build_plate_number)
        self._invokeSlice()

    ##  Called when the global container stack changes
    def _onGlobalStackChanged(self) -> None:
        if self._global_container_stack:
            self._global_container_stack.propertyChanged.disconnect(
                self._onSettingChanged)
            self._global_container_stack.containersChanged.disconnect(
                self._onChanged)
            extruders = list(self._global_container_stack.extruders.values())

            for extruder in extruders:
                extruder.propertyChanged.disconnect(self._onSettingChanged)
                extruder.containersChanged.disconnect(self._onChanged)

        self._global_container_stack = self._application.getGlobalContainerStack(
        )

        if self._global_container_stack:
            self._global_container_stack.propertyChanged.connect(
                self._onSettingChanged
            )  # Note: Only starts slicing when the value changed.
            self._global_container_stack.containersChanged.connect(
                self._onChanged)
            extruders = list(self._global_container_stack.extruders.values())
            for extruder in extruders:
                extruder.propertyChanged.connect(self._onSettingChanged)
                extruder.containersChanged.connect(self._onChanged)
            self._onChanged()

    def _onChanged(self, *args: Any, **kwargs: Any) -> None:
        self.needsSlicing()
        if self._use_timer:
            # if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
            # otherwise business as usual
            if self._machine_error_checker is None:
                self._change_timer.stop()
                return

            if self._machine_error_checker.needToWaitForResult:
                self._change_timer.stop()
            else:
                self._change_timer.start()

    ##  Return a dict with number of objects per build plate
    def _numObjectsPerBuildPlate(self) -> Dict[int, int]:
        num_objects = defaultdict(int)  #type: Dict[int, int]
        for node in DepthFirstIterator(
                self._scene.getRoot()
        ):  #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
            # Only count sliceable objects
            if node.callDecoration("isSliceable"):
                build_plate_number = node.callDecoration("getBuildPlateNumber")
                if build_plate_number is not None:
                    num_objects[build_plate_number] += 1
        return num_objects

    def _onSettingChanged(self, instance: SettingInstance,
                          property: str) -> None:
        if property == "value":  # Only reslice if the value has changed.
            self.needsSlicing()
            self._onChanged()

        elif property == "validationState":
            if self._use_timer:
                self._change_timer.stop()

    def needsSlicing(self) -> None:
        self.stopSlicing()
        self.markSliceAll()
        self.processingProgress.emit(0.0)
        self.backendStateChange.emit(BackendState.NotStarted)
        if not self._use_timer:
            self._clearLayerData()

    def markSliceAll(self) -> None:
        for build_plate_number in range(
                self._application.getMultiBuildPlateModel().maxBuildPlate + 1):
            if build_plate_number not in self._build_plates_to_be_sliced:
                self._build_plates_to_be_sliced.append(build_plate_number)

    def _clearLayerData(self, build_plate_numbers: Set = None) -> None:
        self._scene.gcode_dict = {}
        for node in DepthFirstIterator(
                self._scene.getRoot()
        ):  #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
            if node.callDecoration("getLayerData"):
                if not build_plate_numbers or node.callDecoration(
                        "getBuildPlateNumber") in build_plate_numbers:
                    node.getParent().removeChild(node)

    def _onToolOperationStarted(self, tool: Tool) -> None:
        self._tool_active = True  # Do not react on scene change
        self.disableTimer()
        # Restart engine as soon as possible, we know we want to slice afterwards
        if not self._engine_is_fresh:
            self._terminate()

    def _onToolOperationStopped(self, tool: Tool) -> None:
        self._tool_active = False  # React on scene change again
        self.determineAutoSlicing()  # Switch timer on if appropriate
        # Process all the postponed scene changes
        while self._postponed_scene_change_sources:
            source = self._postponed_scene_change_sources.pop(0)
            self._onSceneChanged(source)

    ##  Connect slice function to timer.
    def enableTimer(self) -> None:
        if not self._use_timer:
            self._change_timer.timeout.connect(self.slice)
            self._use_timer = True

    ##  Disconnect slice function from timer.
    #   This means that slicing will not be triggered automatically
    def disableTimer(self) -> None:
        if self._use_timer:
            self._use_timer = False
            self._change_timer.timeout.disconnect(self.slice)

    def _onStackErrorCheckFinished(self) -> None:
        self.determineAutoSlicing()
        if self._is_disabled:
            return

        if not self._slicing and self._build_plates_to_be_sliced:
            self.needsSlicing()
            self._onChanged()

    def _invokeSlice(self) -> None:
        if self._use_timer:
            # if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
            # otherwise business as usual
            if self._machine_error_checker is None:
                self._change_timer.stop()
                return

            if self._machine_error_checker.needToWaitForResult:
                self._change_timer.stop()
            else:
                self._change_timer.start()

    def tickle(self) -> None:
        if self._use_timer:
            self._change_timer.start()

    def _createSocket(self, protocol_file):
        Logger.log("d", "Create Socket")
        pass
Ejemplo n.º 51
0
class PrinterOutputDevice(QObject, OutputDevice):
    printersChanged = pyqtSignal()
    connectionStateChanged = pyqtSignal(str)
    acceptsCommandsChanged = pyqtSignal()

    # Signal to indicate that the material of the active printer on the remote changed.
    materialIdChanged = pyqtSignal()

    # # Signal to indicate that the hotend of the active printer on the remote changed.
    hotendIdChanged = pyqtSignal()

    # Signal to indicate that the info text about the connection has changed.
    connectionTextChanged = pyqtSignal()

    # Signal to indicate that the configuration of one of the printers has changed.
    uniqueConfigurationsChanged = pyqtSignal()

    def __init__(self, device_id: str, parent: QObject = None) -> None:
        super().__init__(device_id = device_id, parent = parent) # type: ignore  # MyPy complains with the multiple inheritance

        self._printers = []  # type: List[PrinterOutputModel]
        self._unique_configurations = []   # type: List[ConfigurationModel]

        self._monitor_view_qml_path = "" #type: str
        self._monitor_component = None #type: Optional[QObject]
        self._monitor_item = None #type: Optional[QObject]

        self._control_view_qml_path = "" #type: str
        self._control_component = None #type: Optional[QObject]
        self._control_item = None #type: Optional[QObject]

        self._accepts_commands = False #type: bool

        self._update_timer = QTimer() #type: QTimer
        self._update_timer.setInterval(2000)  # TODO; Add preference for update interval
        self._update_timer.setSingleShot(False)
        self._update_timer.timeout.connect(self._update)

        self._connection_state = ConnectionState.closed #type: ConnectionState

        self._firmware_updater = None #type: Optional[FirmwareUpdater]
        self._firmware_name = None #type: Optional[str]
        self._address = "" #type: str
        self._connection_text = "" #type: str
        self.printersChanged.connect(self._onPrintersChanged)
        QtApplication.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._updateUniqueConfigurations)

    @pyqtProperty(str, notify = connectionTextChanged)
    def address(self) -> str:
        return self._address

    def setConnectionText(self, connection_text):
        if self._connection_text != connection_text:
            self._connection_text = connection_text
            self.connectionTextChanged.emit()

    @pyqtProperty(str, constant=True)
    def connectionText(self) -> str:
        return self._connection_text

    def materialHotendChangedMessage(self, callback: Callable[[int], None]) -> None:
        Logger.log("w", "materialHotendChangedMessage needs to be implemented, returning 'Yes'")
        callback(QMessageBox.Yes)

    def isConnected(self) -> bool:
        return self._connection_state != ConnectionState.closed and self._connection_state != ConnectionState.error

    def setConnectionState(self, connection_state: ConnectionState) -> None:
        if self._connection_state != connection_state:
            self._connection_state = connection_state
            self.connectionStateChanged.emit(self._id)

    @pyqtProperty(str, notify = connectionStateChanged)
    def connectionState(self) -> ConnectionState:
        return self._connection_state

    def _update(self) -> None:
        pass

    def _getPrinterByKey(self, key: str) -> Optional["PrinterOutputModel"]:
        for printer in self._printers:
            if printer.key == key:
                return printer

        return None

    def requestWrite(self, nodes: List["SceneNode"], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional["FileHandler"] = None, **kwargs: str) -> None:
        raise NotImplementedError("requestWrite needs to be implemented")

    @pyqtProperty(QObject, notify = printersChanged)
    def activePrinter(self) -> Optional["PrinterOutputModel"]:
        if len(self._printers):
            return self._printers[0]
        return None

    @pyqtProperty("QVariantList", notify = printersChanged)
    def printers(self) -> List["PrinterOutputModel"]:
        return self._printers

    @pyqtProperty(QObject, constant = True)
    def monitorItem(self) -> QObject:
        # Note that we specifically only check if the monitor component is created.
        # It could be that it failed to actually create the qml item! If we check if the item was created, it will try to
        # create the item (and fail) every time.
        if not self._monitor_component:
            self._createMonitorViewFromQML()
        return self._monitor_item

    @pyqtProperty(QObject, constant = True)
    def controlItem(self) -> QObject:
        if not self._control_component:
            self._createControlViewFromQML()
        return self._control_item

    def _createControlViewFromQML(self) -> None:
        if not self._control_view_qml_path:
            return
        if self._control_item is None:
            self._control_item = QtApplication.getInstance().createQmlComponent(self._control_view_qml_path, {"OutputDevice": self})

    def _createMonitorViewFromQML(self) -> None:
        if not self._monitor_view_qml_path:
            return

        if self._monitor_item is None:
            self._monitor_item = QtApplication.getInstance().createQmlComponent(self._monitor_view_qml_path, {"OutputDevice": self})

    ##  Attempt to establish connection
    def connect(self) -> None:
        self.setConnectionState(ConnectionState.connecting)
        self._update_timer.start()

    ##  Attempt to close the connection
    def close(self) -> None:
        self._update_timer.stop()
        self.setConnectionState(ConnectionState.closed)

    ##  Ensure that close gets called when object is destroyed
    def __del__(self) -> None:
        self.close()

    @pyqtProperty(bool, notify = acceptsCommandsChanged)
    def acceptsCommands(self) -> bool:
        return self._accepts_commands

    @deprecated("Please use the protected function instead", "3.2")
    def setAcceptsCommands(self, accepts_commands: bool) -> None:
        self._setAcceptsCommands(accepts_commands)

    ##  Set a flag to signal the UI that the printer is not (yet) ready to receive commands
    def _setAcceptsCommands(self, accepts_commands: bool) -> None:
        if self._accepts_commands != accepts_commands:
            self._accepts_commands = accepts_commands

            self.acceptsCommandsChanged.emit()

    # Returns the unique configurations of the printers within this output device
    @pyqtProperty("QVariantList", notify = uniqueConfigurationsChanged)
    def uniqueConfigurations(self) -> List["ConfigurationModel"]:
        return self._unique_configurations

    def _updateUniqueConfigurations(self) -> None:
        self._unique_configurations = list(set([printer.printerConfiguration for printer in self._printers if printer.printerConfiguration is not None]))
        self._unique_configurations.sort(key = lambda k: k.printerType)
        self.uniqueConfigurationsChanged.emit()

    def _onPrintersChanged(self) -> None:
        for printer in self._printers:
            printer.configurationChanged.connect(self._updateUniqueConfigurations)

        # At this point there may be non-updated configurations
        self._updateUniqueConfigurations()

    ##  Set the device firmware name
    #
    #   \param name The name of the firmware.
    def _setFirmwareName(self, name: str) -> None:
        self._firmware_name = name

    ##  Get the name of device firmware
    #
    #   This name can be used to define device type
    def getFirmwareName(self) -> Optional[str]:
        return self._firmware_name

    def getFirmwareUpdater(self) -> Optional["FirmwareUpdater"]:
        return self._firmware_updater

    @pyqtSlot(str)
    def updateFirmware(self, firmware_file: Union[str, QUrl]) -> None:
        if not self._firmware_updater:
            return

        self._firmware_updater.updateFirmware(firmware_file)
Ejemplo n.º 52
0
class RunningDialog(QDialog, Ui_RunningDialog):
    """
    running dialog for all the algorithms  
    
    # Dialog Description  
    
    ## Purpose  
    To run an AI algorithms  
    
    ## Structure  
    The dialog is composed from  
    -#  Horizontal layout that holds 2 Plot containers (viewing of a graph) 
        and a text edit for the messages
    -#  Grid layout with 4 rows (from top to button):  
        -#  labels that will be used to show the time elapse for the action activated by the 
            buttons below them 
        -#  The buttons row to activate the algorithm
        -#  labels that will be used to show the time elapse for independent actions 
        -#  The buttons row for the independent actions 
            
    ## Dialog working method  
    -#  Each one of the buttons is responsible for one step in the processing of the
        algorithm  
    -#  The steps are :  
        -#  Load   
        -#  Normalize  
        -#  Init  
        -#  Repeating steps or Run to end  
        -#  Test  
    -#  Additionally There are independent actions:  
        -#  Activate the scipy version of the algorithm  
        -#  Activating the scikit - Learn version of the algorithm  
    -#  Each button is doing:  
        -#  Get all the inputs needed for the action  
        -#  Activate the activate method  
    -#  The activate method does:  
        -#  Start a timer  
        -#  Call the run method of the model to perform the action  
    -#  While the action is running in a separate thread in the model, the timer is active in the dialog  
    -#  The timer is running as long as the model is running an action    
    -#  When the model finished an action, it sets an event  
    -#  The timer method checks the event and if it is set it does:  
        -#  Stop the timer    
        -#  Generate a finish/error message  
        -#  Set the enable attribute of the buttons according a dictionary in the model  
       
    ## Attributes  
    -#  self.model - The model of the dialog (That performs the actual operations)  
    -#  self.timer - The timer that is presenting the time and activates the end action operations  
    -#  self.action - The action to perform (set by the activate method to be sent to the model)  
    -#  self.timerLabel - Tells the timer event handler where to write the time elapsed when an action is performed  
    -#  self.timerText -  A text for the time label (set by the action method and used by the timer event handler)  
    -#  self.startTime -  The start time of the action to be used by the timer event  
    -#  self.stopEvent - The event that will be used by the model to signal to the timer that it finished  
    
    Args:
        Parent  : (QDialog) - The calling dialog
    """
    def __init__(self, parent):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.pushButton_load.clicked.connect(self.pushButton_load_clicked)
        self.pushButton_normalize.clicked.connect(
            self.pushButton_normalize_clicked)
        self.pushButton_init.clicked.connect(self.pushButton_init_clicked)
        self.pushButton_step.clicked.connect(self.pushButton_step_clicked)
        self.pushButton_runToEnd.clicked.connect(
            self.pushButton_runToEnd_clicked)
        self.pushButton_test.clicked.connect(self.pushButton_test_clicked)
        self.pushButton_scipy.clicked.connect(self.pushButton_scipy_clicked)
        self.pushButton_scikitLearn.clicked.connect(
            self.pushButton_scikitLearn_clicked)
        self.pushButton_exit.clicked.connect(self.reject)
        self.model = RunningDialogModel()
        self.model.view = self
        self.setWidgetsEnable()
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.timerEvent)

    @staticmethod
    def prms(widget):
        """
        This is a static method that returns the parameters that the user can set for the dialog

        The parameters are changed in the ParametersDialog dialog

        Args:
            widget: (QWidget) - The parametersWidget that is used to change the parameters
        """
        parameters = []
        parameters.append(
            StdPrm("Algorithm data file", "", False, ParameterInput.button,
                   ["Select algorithm data file"], widget.fileInput,
                   ["Select data file", DATA_PATH, "csv file (*.csv)"]))

        parameters.append(
            StdPrm("Test data file", "", False, ParameterInput.button,
                   ["Select test data file"], widget.fileInput,
                   ["Select test data file", DATA_PATH, "csv file (*.csv)"]))
        plotHandlers = [
            plotHandler.__name__
            for plotHandler in PythonUtilities.inheritors(PlotHandler)
        ]
        parameters.append(
            StdPrm("Left Plot Handler", plotHandlers[0], False,
                   ParameterInput.comboBox, [plotHandlers],
                   widget.selectionChanged, []))
        parameters.append(
            StdPrm("Right Plot Handler", plotHandlers[0], False,
                   ParameterInput.comboBox, [plotHandlers],
                   widget.selectionChanged, []))

        return parameters

    def setWidgetsEnable(self):
        """
        Sets the widgets enable attribute according to the allowed actions by the model

        Each action in the model decides according to it's results what are the possible actions
        that can be activated after it

        This method enable/disable the buttons according to that decision
       
        """
        self.pushButton_load.setEnabled(
            self.model.enable_actions[Actions.Load])
        self.pushButton_normalize.setEnabled(
            self.model.enable_actions[Actions.Normalize])
        self.pushButton_init.setEnabled(
            self.model.enable_actions[Actions.Init])
        self.pushButton_step.setEnabled(
            self.model.enable_actions[Actions.Step])
        self.spinBox_numSteps.setEnabled(self.model.enable_actions[Actions.Step] or \
            self.model.enable_actions[Actions.Step])
        self.pushButton_runToEnd.setEnabled(
            self.model.enable_actions[Actions.RunToEnd])
        self.pushButton_test.setEnabled(
            self.model.enable_actions[Actions.Test])
        self.pushButton_scipy.setEnabled(
            self.model.enable_actions[Actions.SciPy])
        self.pushButton_scikitLearn.setEnabled(
            self.model.enable_actions[Actions.SciKitLearn])

    def timerEvent(self):
        """
        The event activated by the timer  

        #### This method does:  
        -#  When the timer is active - Show the time elapsed from the beginning of the action    
        -#  When the event is set - Activating ending actions    
        """
        currentTime = datetime.now()
        elapsedTime = currentTime - self.startTime
        self.timerLabel.setText(self.timerText + " time: " + str(elapsedTime))
        self.addMessage()
        if self.stopEvent.isSet():
            QApplication.restoreOverrideCursor()
            self.setWidgetsEnable()
            self.generateEndMessage()
            self.timer.stop()

    def generateEndMessage(self):
        """
        Generate the end action message

        The inputs of the message generation is found in the model

        #### The message is generated in the following way:  
        -#  If the algorithm message is not empty add the algorithm message to the model message   
        -#  Else leave the model message  
        -#  If the step succeeded produce an information message  
        -#  Else produce an error message  
        """
        if self.model.algorithm_message == "":
            message = self.model.end_message
        else:
            message = self.model.end_message + ":\n" + self.model.algorithm_message

        if self.model.success:
            QMessageBox.information(self, "RunningDialog Information", message)
        else:
            QMessageBox.critical(self, "RunningDialog Information", message)

    def initPlot(self):
        """
        Initialize a plot
        """

        self.myQtPlotContainer_left.init(
            eval(self.parameters["Left Plot Handler"]["value"] +
                 "(self.model.algorithm_data)"))
        self.myQtPlotContainer_right.init(
            eval(self.parameters["Right Plot Handler"]["value"] +
                 "(self.model.algorithm_data)"))

    def plot(self):
        """      
        Update a plot
        """

        self.myQtPlotContainer_left.handler.drawStepResult()
        self.myQtPlotContainer_right.handler.drawStepResult()

    def addMessage(self):
        """
        Add all the messages currently in the queue to the
        PlainTextEdit

        - This method is activated while running by the timer
        - Because it is activated by the timer it is not known
          how many messages the model inserted to it between the 
          timer ticks. so we empty all the queue in a loog
        """

        while True:
            try:
                msg = self.messages.get(block=False)
                textCharFormat = QTextCharFormat()
                textCharFormat.setForeground(msg.Color)
                self.plainTextEdit_messages.textCursor().insertText(
                    msg.Message + "\n", textCharFormat)
                self.plainTextEdit_messages.ensureCursorVisible()
            except Empty:
                break

    def activate(self, action: Actions, label: QLabel, text: str):
        """

        Activate an action (The timer and the appropriate action of the model)  
        
        Args:  
            action	: (Actions)	- The action to perform  
            label   : (QLabel)  - The label to write the time result in  
            text    : (string)  - The text for the time label          
        """
        self.messages = Queue()
        self.action = action
        self.timerLabel = label
        self.timerText = text
        self.startTime = datetime.now()
        self.stopEvent = Event()
        self.timer.start(100)
        self.model.run(action, self.stopEvent)

    def initRun(self):
        """
        Clear all the time labels when a load is done
        """
        self.label_loadTime.setText("")
        self.label_normalizeTime.setText("")
        self.label_initTime.setText("")
        self.label_stepTime.setText("")
        self.label_runToEndTime.setText("")
        self.stepCount = 0

    def pushButton_load_clicked(self):
        """
        Set the parameters and load the file

        -#  Activate the Running dialog to get the:
            -#  Parameters for the dialog
            -#  The training method
            -#  The parameters for the training class
            -#  The algorithm
            -#  The parameters for the algorithm
        -#  Check if algorithm data file was inserted
        """

        parametersDialog = ParametersDialog(self)
        if parametersDialog.exec_() == QDialog.Accepted:
            _, self.parameters = parametersDialog.dialogPrms()
            self.model.train, self.model.trainPrms = parametersDialog.trainPrms(
            )
            self.model.algorithm, self.model.algorithmPrms = parametersDialog.algorithmPrms(
            )
            self.model.filename = self.parameters["Algorithm data file"][
                "value"]
            self.model.test_filename = self.parameters["Test data file"][
                "value"]
            self.plainTextEdit_messages.clear()
            self.initRun()
            self.activate(Actions.Load, self.label_loadTime, "Load")

    def pushButton_normalize_clicked(self):
        """
        Normalize data and design the algorithm data

        -#  Start an algorithmDataDesign dialog  
        -#  Set the results of the algorithm data design in the model  
        -#  activate normalize action in the model (by calling to the activate method)  
        """
        # activate the algorithm_data design and set the model.data_matrix (data for the algorithm)
        algorithmDataDesign = AlgorithmDataDesign(
            self, self.model.data_matrix,
            self.parameters["Algorithm data file"]["value"])
        algorithmDataDesign.exec_()
        self.model.normalize_data = algorithmDataDesign.normalizeData
        self.model.data_matrix = algorithmDataDesign.dataMatrix
        _, _, self.model.test_data_matrix = algorithmDataDesign.loadNormalizeData(
            self.model.test_data_matrix)

        # create the model
        self.activate(Actions.Normalize, self.label_normalizeTime, "Normalize")

    def pushButton_init_clicked(self):
        """
        Activate the init action of the algorithm
        """
        self.activate(Actions.Init, self.label_initTime, "Init")
        self.plot()

    def pushButton_step_clicked(self, numSteps=0):
        """
        Activate the step action of the model
        """
        self.model.num_steps = self.spinBox_numSteps.value()
        self.message = ""
        self.activate(Actions.Step, self.label_stepTime, "Step")

    def pushButton_runToEnd_clicked(self):
        """
        Activate the run to end action of the model
        """
        self.model.num_steps = self.spinBox_numSteps.value()
        self.activate(Actions.RunToEnd, self.label_runToEndTime, "RunToEnd")

    def pushButton_test_clicked(self):
        """
        Activate the test action of the algorithm

        -# Gets the file name of the test data    
        -# Set the test file name attribute of the model    
        -# Activate the test action of the model    
        """
        self.activate(Actions.Test, self.label_testTime, "Test")

    def pushButton_scipy_clicked(self):
        """
        Activate the scipy vertion of the algorithm

        -# Gets the file name of the test data  
        -# Set the test file name attribute of the model  
        -# Activate the test action of the model  
        """
        self.activate(Actions.SciPy, self.label_scipyTime, "Scipy")

    def pushButton_scikitLearn_clicked(self):
        """
        Activate the scikit - Learn version aof the algorithm

        -# Gets the file name of the test data  
        -# Set the test file name attribute of the model  
        -# Activate the test action of the model  
        """
        self.activate(Actions.SciKitLearn, self.label_scikitLearnTime,
                      "Scikit-Learn")
        self.initPlot()
Ejemplo n.º 53
0
class Timer(QLabel):
    def __init__(self, parent, toplevel):

        super(QLabel, self).__init__(parent)

        self.toplevel = toplevel
        self.timer = QTimer()
        self.timer.timeout.connect(self.countdown)
        self.setAlignment(Qt.AlignCenter)

    def start(self, gallery, time):

        parent = self.parent()
        self.gallery = iter(gallery)
        self.image = next(self.gallery)
        parent.update(self.image)

        self.setGeometry(int(parent.width() * .85), int(parent.height() * .85),
                         75, 75)
        self.setStyleSheet('background: white; font: 20px')

        self.time, self.current = time, time
        self.updateText()
        self.timer.start(1000)

    def pause(self):

        if self.timer.isActive(): self.timer.stop()
        else: self.timer.start(1000)

    def updateText(self, delta=1):

        self.current = (self.current - delta) % self.time
        self.setText('{}:{:02}'.format(*divmod(self.current, 60)))
        self.setStyleSheet(f'''
            background: white; font: 20px;
            color: {"red" if self.current <= 5 else "black"}
            ''')

    def countdown(self):

        if self.current: self.updateText()

        else:

            worker = Worker(self.toplevel.mysql.execute,
                            UPDATE.format(f'date_used=CURDATE()'),
                            [self.image.data(Qt.UserRole)[0]],
                            emit=0)
            self.toplevel.threadpool.start(worker)
            self.updateText()

            try:
                self.image = next(self.gallery)
                self.parent().update(self.image)

            except StopIteration:

                self.timer.stop()
                self.parent().update()
                self.setText('End of session')
                self.setStyleSheet(
                    'background: black; color: white; font: 20px')
                self.setGeometry(int(self.parent().width() * .4),
                                 int(self.parent().height() * .1), 125, 75)
Ejemplo n.º 54
0
    class AutoSettingsObject(QObject):
        gain_changed = pyqtSignal(float)
        exposure_time_changed = pyqtSignal(float)

        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self._saturation = 0
            self._enabled = False
            self._piUi = 0
            self._last_error = 0
            self._gain = None
            self._exposure_time = None
            self._min_gain = None
            self._max_gain = None
            self._min_exposure_time = None
            self._max_exposure_time = None
            self._setpoint = 85.
            self.Kp = None
            self.Kd = None
            self.Ki = None
            self._interval = 200
            self._timer = QTimer()
            self._timer.timeout.connect(self._run)

        @pyqtSlot(int)
        def set_saturation(self, sat):
            self._saturation = sat

        @pyqtSlot(float)
        def set_exposure_time(self, exposure):
            self._exposure_time = exposure

        @pyqtSlot(float)
        def set_gain(self, gain):
            self._gain = gain

        def set_exposure_range(self, rng):
            self._min_exposure_time, self._max_exposure_time = rng

        def set_gain_range(self, rng):
            self._min_gain, self._max_gain = rng

        def start(self):
            self._timer.start(self._interval)

        def stop(self):
            self._timer.stop()

        def _run(self):
            error = self._saturation - self._setpoint
            ui = self._piUi + error * self._interval / 1000 * self.Ki
            self._piUi = ui
            ud = self._last_error / self._interval * self.Kd
            output = - self.Kp * (error + ui + ud)
            self._last_error = error

            previous_gain = self._gain
            previous_exposure_time = self._exposure_time

            if (error > 0 and self._min_gain < self._gain) or (error < 0 and self._gain < self._max_gain):
                # adjust gain
                db_increase = output / 5
                self._gain = clamp(self._gain + db_increase, self._min_gain, self._max_gain)
            elif (error > 0 and self._min_exposure_time < self._exposure_time) or \
                    (error < 0 and self._exposure_time < self._max_exposure_time):
                self._exposure_time = clamp(self._exposure_time + output, self._min_exposure_time,
                                            self._max_exposure_time)
            else:
                # stuck at the edge...
                self._piUi = 0

            if self._exposure_time != previous_exposure_time:
                self.exposure_time_changed.emit(self._exposure_time)
            if self._gain != previous_gain:
                self.gain_changed.emit(self._gain)
Ejemplo n.º 55
0
class ElectrumGui(Logger):

    @profiler
    def __init__(self, config: 'SimpleConfig', daemon: 'Daemon', plugins: 'Plugins'):
        set_language(config.get('language', get_default_language()))
        Logger.__init__(self)
        # Uncomment this call to verify objects are being properly
        # GC-ed when windows are closed
        #network.add_jobs([DebugMem([Abstract_Wallet, SPV, Synchronizer,
        #                            ElectrumWindow], interval=5)])
        QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads)
        if hasattr(QtCore.Qt, "AA_ShareOpenGLContexts"):
            QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
        if hasattr(QGuiApplication, 'setDesktopFileName'):
            QGuiApplication.setDesktopFileName('electrum-gzro.desktop')
        self.gui_thread = threading.current_thread()
        self.config = config
        self.daemon = daemon
        self.plugins = plugins
        self.windows = []
        self.efilter = OpenFileEventFilter(self.windows)
        self.app = QElectrumApplication(sys.argv)
        self.app.installEventFilter(self.efilter)
        self.app.setWindowIcon(read_QIcon("electrum-gzro.png"))
        # timer
        self.timer = QTimer(self.app)
        self.timer.setSingleShot(False)
        self.timer.setInterval(500)  # msec

        self.network_dialog = None
        self.lightning_dialog = None
        self.network_updated_signal_obj = QNetworkUpdatedSignalObject()
        self._num_wizards_in_progress = 0
        self._num_wizards_lock = threading.Lock()
        # init tray
        self.dark_icon = self.config.get("dark_icon", False)
        self.tray = QSystemTrayIcon(self.tray_icon(), None)
        self.tray.setToolTip('Electrum')
        self.tray.activated.connect(self.tray_activated)
        self.build_tray_menu()
        self.tray.show()
        self.app.new_window_signal.connect(self.start_new_window)
        self.set_dark_theme_if_needed()
        run_hook('init_qt', self)

    def set_dark_theme_if_needed(self):
        use_dark_theme = self.config.get('qt_gui_color_theme', 'default') == 'dark'
        if use_dark_theme:
            try:
                import qdarkstyle
                self.app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
            except BaseException as e:
                use_dark_theme = False
                self.logger.warning(f'Error setting dark theme: {repr(e)}')
        # Apply any necessary stylesheet patches
        patch_qt_stylesheet(use_dark_theme=use_dark_theme)
        # Even if we ourselves don't set the dark theme,
        # the OS/window manager/etc might set *a dark theme*.
        # Hence, try to choose colors accordingly:
        ColorScheme.update_from_widget(QWidget(), force_dark=use_dark_theme)

    def build_tray_menu(self):
        # Avoid immediate GC of old menu when window closed via its action
        if self.tray.contextMenu() is None:
            m = QMenu()
            self.tray.setContextMenu(m)
        else:
            m = self.tray.contextMenu()
            m.clear()
        if self.config.get('lightning'):
            m.addAction(_("Lightning"), self.show_lightning_dialog)
        for window in self.windows:
            name = window.wallet.basename()
            submenu = m.addMenu(name)
            submenu.addAction(_("Show/Hide"), window.show_or_hide)
            submenu.addAction(_("Close"), window.close)
        m.addAction(_("Dark/Light"), self.toggle_tray_icon)
        m.addSeparator()
        m.addAction(_("Exit Electrum"), self.close)

    def tray_icon(self):
        if self.dark_icon:
            return read_QIcon('electrum_dark_icon.png')
        else:
            return read_QIcon('electrum_light_icon.png')

    def toggle_tray_icon(self):
        self.dark_icon = not self.dark_icon
        self.config.set_key("dark_icon", self.dark_icon, True)
        self.tray.setIcon(self.tray_icon())

    def tray_activated(self, reason):
        if reason == QSystemTrayIcon.DoubleClick:
            if all([w.is_hidden() for w in self.windows]):
                for w in self.windows:
                    w.bring_to_top()
            else:
                for w in self.windows:
                    w.hide()

    def close(self):
        for window in self.windows:
            window.close()
        if self.network_dialog:
            self.network_dialog.close()
        if self.lightning_dialog:
            self.lightning_dialog.close()

    def new_window(self, path, uri=None):
        # Use a signal as can be called from daemon thread
        self.app.new_window_signal.emit(path, uri)

    def show_lightning_dialog(self):
        if not self.lightning_dialog:
            self.lightning_dialog = LightningDialog(self)
        self.lightning_dialog.bring_to_top()

    def show_network_dialog(self, parent):
        if not self.daemon.network:
            parent.show_warning(_('You are using Electrum in offline mode; restart Electrum if you want to get connected'), title=_('Offline'))
            return
        if self.network_dialog:
            self.network_dialog.on_update()
            self.network_dialog.show()
            self.network_dialog.raise_()
            return
        self.network_dialog = NetworkDialog(self.daemon.network, self.config,
                                self.network_updated_signal_obj)
        self.network_dialog.show()

    def _create_window_for_wallet(self, wallet):
        w = ElectrumWindow(self, wallet)
        self.windows.append(w)
        self.build_tray_menu()
        # FIXME: Remove in favour of the load_wallet hook
        run_hook('on_new_window', w)
        w.warn_if_testnet()
        w.warn_if_watching_only()
        return w

    def count_wizards_in_progress(func):
        def wrapper(self: 'ElectrumGui', *args, **kwargs):
            with self._num_wizards_lock:
                self._num_wizards_in_progress += 1
            try:
                return func(self, *args, **kwargs)
            finally:
                with self._num_wizards_lock:
                    self._num_wizards_in_progress -= 1
        return wrapper

    @count_wizards_in_progress
    def start_new_window(self, path, uri, *, app_is_starting=False):
        '''Raises the window for the wallet if it is open.  Otherwise
        opens the wallet and creates a new window for it'''
        wallet = None
        try:
            wallet = self.daemon.load_wallet(path, None)
        except BaseException as e:
            self.logger.exception('')
            custom_message_box(icon=QMessageBox.Warning,
                               parent=None,
                               title=_('Error'),
                               text=_('Cannot load wallet') + ' (1):\n' + repr(e))
            # if app is starting, still let wizard to appear
            if not app_is_starting:
                return
        if not wallet:
            try:
                wallet = self._start_wizard_to_select_or_create_wallet(path)
            except (WalletFileException, BitcoinException) as e:
                self.logger.exception('')
                custom_message_box(icon=QMessageBox.Warning,
                                   parent=None,
                                   title=_('Error'),
                                   text=_('Cannot load wallet') + ' (2):\n' + repr(e))
        if not wallet:
            return
        # create or raise window
        try:
            for window in self.windows:
                if window.wallet.storage.path == wallet.storage.path:
                    break
            else:
                window = self._create_window_for_wallet(wallet)
        except BaseException as e:
            self.logger.exception('')
            custom_message_box(icon=QMessageBox.Warning,
                               parent=None,
                               title=_('Error'),
                               text=_('Cannot create window for wallet') + ':\n' + repr(e))
            if app_is_starting:
                wallet_dir = os.path.dirname(path)
                path = os.path.join(wallet_dir, get_new_wallet_name(wallet_dir))
                self.start_new_window(path, uri)
            return
        if uri:
            window.pay_to_URI(uri)
        window.bring_to_top()
        window.setWindowState(window.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)

        window.activateWindow()
        return window

    def _start_wizard_to_select_or_create_wallet(self, path) -> Optional[Abstract_Wallet]:
        wizard = InstallWizard(self.config, self.app, self.plugins)
        try:
            path, storage = wizard.select_storage(path, self.daemon.get_wallet)
            # storage is None if file does not exist
            if storage is None:
                wizard.path = path  # needed by trustedcoin plugin
                wizard.run('new')
                storage = wizard.create_storage(path)
            else:
                wizard.run_upgrades(storage)
        except (UserCancelled, GoBack):
            return
        except WalletAlreadyOpenInMemory as e:
            return e.wallet
        finally:
            wizard.terminate()
        # return if wallet creation is not complete
        if storage is None or storage.get_action():
            return
        wallet = Wallet(storage, config=self.config)
        wallet.start_network(self.daemon.network)
        self.daemon.add_wallet(wallet)
        return wallet

    def close_window(self, window: ElectrumWindow):
        if window in self.windows:
           self.windows.remove(window)
        self.build_tray_menu()
        # save wallet path of last open window
        if not self.windows:
            self.config.save_last_wallet(window.wallet)
        run_hook('on_close_window', window)
        self.daemon.stop_wallet(window.wallet.storage.path)

    def init_network(self):
        # Show network dialog if config does not exist
        if self.daemon.network:
            if self.config.get('auto_connect') is None:
                wizard = InstallWizard(self.config, self.app, self.plugins)
                wizard.init_network(self.daemon.network)
                wizard.terminate()

    def main(self):
        try:
            self.init_network()
        except UserCancelled:
            return
        except GoBack:
            return
        except BaseException as e:
            self.logger.exception('')
            return
        self.timer.start()

        path = self.config.get_wallet_path(use_gui_last_wallet=True)
        if not self.start_new_window(path, self.config.get('url'), app_is_starting=True):
            return
        signal.signal(signal.SIGINT, lambda *args: self.app.quit())

        def quit_after_last_window():
            # keep daemon running after close
            if self.config.get('daemon'):
                return
            # check if a wizard is in progress
            with self._num_wizards_lock:
                if self._num_wizards_in_progress > 0 or len(self.windows) > 0:
                    return
                if self.config.get('persist_daemon'):
                    return
            self.app.quit()
        self.app.setQuitOnLastWindowClosed(False)  # so _we_ can decide whether to quit
        self.app.lastWindowClosed.connect(quit_after_last_window)

        def clean_up():
            # Shut down the timer cleanly
            self.timer.stop()
            # clipboard persistence. see http://www.mail-archive.com/[email protected]/msg17328.html
            event = QtCore.QEvent(QtCore.QEvent.Clipboard)
            self.app.sendEvent(self.app.clipboard(), event)
            self.tray.hide()
        self.app.aboutToQuit.connect(clean_up)

        # main loop
        self.app.exec_()
        # on some platforms the exec_ call may not return, so use clean_up()

    def stop(self):
        self.logger.info('closing GUI')
        self.app.quit()
Ejemplo n.º 56
0
class OpencvImg(QDialog):
    def __init__(self):
        super(OpencvImg, self).__init__()
        loadUi('demo2gui.ui', self)
        self.image = None
        self.start_button.clicked.connect(self.start_webcam)
        self.stop_button.clicked.connect(self.stop_webcam)

        self.commandLinkButton.clicked.connect(self.start_card)

    def start_card(self):
        self.timer.timeout.connect(self.find_card)
        self.timer.start(5)

    def start_webcam(self):

        self.capture = cv2.VideoCapture(1)
        # self.capture = cap
        self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 300)
        self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, 400)

        self.x1_slider.setValue(95)
        self.y1_slider.setValue(53)
        self.x2_slider.setValue(491)
        self.y2_slider.setValue(60)
        self.x3_slider.setValue(45)
        self.y3_slider.setValue(458)
        self.x4_slider.setValue(523)
        self.y4_slider.setValue(457)

        self.card_lh.setValue(46)
        self.card_ls.setValue(0)
        self.card_lv.setValue(127)
        self.card_uh.setValue(179)
        self.card_us.setValue(255)
        self.card_uv.setValue(255)

        self.red_lh.setValue(0)
        self.red_ls.setValue(30)
        self.red_lv.setValue(6)
        self.red_uh.setValue(6)
        self.red_us.setValue(255)
        self.red_uv.setValue(255)

        self.green_lh.setValue(41)
        self.green_ls.setValue(21)
        self.green_lv.setValue(58)
        self.green_uh.setValue(93)
        self.green_us.setValue(255)
        self.green_uv.setValue(246)

        self.blue_lh.setValue(109)
        self.blue_ls.setValue(22)
        self.blue_lv.setValue(131)
        self.blue_uh.setValue(139)
        self.blue_us.setValue(255)
        self.blue_uv.setValue(255)

        self.yellow_lh.setValue(17)
        self.yellow_ls.setValue(46)
        self.yellow_lv.setValue(0)
        self.yellow_uh.setValue(29)
        self.yellow_us.setValue(255)
        self.yellow_uv.setValue(242)

        self.black_lh.setValue(90)
        self.black_ls.setValue(0)
        self.black_lv.setValue(0)
        self.black_uh.setValue(106)
        self.black_us.setValue(83)
        self.black_uv.setValue(220)
        # 115 0 0 166 29 243
        # self.black_lh.setValue(0)
        # self.black_ls.setValue(0)
        # self.black_lv.setValue(0)
        # self.black_uh.setValue(179)
        # self.black_us.setValue(30)
        # self.black_uv.setValue(120)

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_frame)
        self.timer.start(5)

    def update_frame(self):

        ret, self.image = self.capture.read()

        self.notCircle = self.image.copy()

        cv2.circle(self.image,
                   (self.x1_slider.value(), self.y1_slider.value()), 5,
                   (0, 0, 255), -1)
        self.current_value.setText('X1 Y1 -> :' + str(self.x1_slider.value()) +
                                   ' ' + str(self.y1_slider.value()))
        cv2.circle(self.image,
                   (self.x2_slider.value(), self.y2_slider.value()), 5,
                   (0, 0, 255), -1)
        self.current_value2.setText('X2 Y2 -> :' +
                                    str(self.x2_slider.value()) + ' ' +
                                    str(self.y2_slider.value()))
        cv2.circle(self.image,
                   (self.x3_slider.value(), self.y3_slider.value()), 5,
                   (0, 0, 255), -1)
        self.current_value3.setText('X3 Y3 -> :' +
                                    str(self.x3_slider.value()) + ' ' +
                                    str(self.y3_slider.value()))
        cv2.circle(self.image,
                   (self.x4_slider.value(), self.y4_slider.value()), 5,
                   (0, 0, 255), -1)
        self.current_value4.setText('X4 Y4 -> :' +
                                    str(self.x4_slider.value()) + ' ' +
                                    str(self.y4_slider.value()))

        pts1 = np.float32([[self.x1_slider.value(),
                            self.y1_slider.value()],
                           [self.x2_slider.value(),
                            self.y2_slider.value()],
                           [self.x3_slider.value(),
                            self.y3_slider.value()],
                           [self.x4_slider.value(),
                            self.y4_slider.value()]])
        pts2 = np.float32([[0, 0], [500, 0], [0, 500], [500, 500]])
        matrix = cv2.getPerspectiveTransform(pts1, pts2)
        resultWarp = cv2.warpPerspective(self.notCircle, matrix, (500, 500))
        self.imageWarp = resultWarp

        # self.image = cv2.flip(self.image, 1)
        # self.imageWarp = cv2.flip(self.imageWarp, 1)
        # self.displayImage(self.image, self.imageWarp, 1)

        Mblurred = cv2.medianBlur(self.imageWarp, 5)
        hsv = cv2.cvtColor(Mblurred, cv2.COLOR_BGR2HSV)

        color_lower = np.array(
            [self.card_lh.value(),
             self.card_ls.value(),
             self.card_lv.value()], np.uint8)
        color_upper = np.array(
            [self.card_uh.value(),
             self.card_us.value(),
             self.card_uv.value()], np.uint8)

        mask = cv2.inRange(hsv, color_lower, color_upper)
        kernel = np.ones((5, 5), np.uint8)

        red_lower = np.array(
            [self.red_lh.value(),
             self.red_ls.value(),
             self.red_lv.value()], np.uint8)
        red_upper = np.array(
            [self.red_uh.value(),
             self.red_us.value(),
             self.red_uv.value()], np.uint8)
        green_lower = np.array([
            self.green_lh.value(),
            self.green_ls.value(),
            self.green_lv.value()
        ], np.uint8)
        green_upper = np.array([
            self.green_uh.value(),
            self.green_us.value(),
            self.green_uv.value()
        ], np.uint8)
        blue_lower = np.array(
            [self.blue_lh.value(),
             self.blue_ls.value(),
             self.blue_lv.value()], np.uint8)
        blue_upper = np.array(
            [self.blue_uh.value(),
             self.blue_us.value(),
             self.blue_uv.value()], np.uint8)
        yellow_lower = np.array([
            self.yellow_lh.value(),
            self.yellow_ls.value(),
            self.yellow_lv.value()
        ], np.uint8)
        yellow_upper = np.array([
            self.yellow_uh.value(),
            self.yellow_us.value(),
            self.yellow_uv.value()
        ], np.uint8)
        black_lower = np.array([
            self.black_lh.value(),
            self.black_ls.value(),
            self.black_lv.value()
        ], np.uint8)
        black_upper = np.array([
            self.black_uh.value(),
            self.black_us.value(),
            self.black_uv.value()
        ], np.uint8)

        self.card_value.setText('Current Value -> Min :' + str(color_lower) +
                                ' Max: ' + str(color_upper))
        self.red_value.setText('Current Value -> Min :' + str(red_lower) +
                               ' Max: ' + str(red_upper))
        self.green_value.setText('Current Value -> Min :' + str(green_lower) +
                                 ' Max: ' + str(green_upper))
        self.blue_value.setText('Current Value -> Min :' + str(blue_lower) +
                                ' Max: ' + str(blue_upper))
        self.yellow_value.setText('Current Value -> Min :' +
                                  str(yellow_lower) + ' Max: ' +
                                  str(yellow_upper))
        self.black_value.setText('Current Value -> Min :' + str(black_lower) +
                                 ' Max: ' + str(black_upper))

        color_mask = cv2.inRange(hsv, color_lower, color_upper)
        card = cv2.bitwise_and(self.imageWarp, self.imageWarp, mask=color_mask)

        bFilterC2 = cv2.bilateralFilter(color_mask, 9, 75, 75)
        openingC2 = cv2.morphologyEx(bFilterC2, cv2.MORPH_OPEN, kernel)
        closingC2 = cv2.morphologyEx(openingC2, cv2.MORPH_CLOSE, kernel)

        bFilterC = cv2.bilateralFilter(card, 9, 75, 75)
        openingC = cv2.morphologyEx(bFilterC, cv2.MORPH_OPEN, kernel)
        closingC = cv2.morphologyEx(openingC, cv2.MORPH_CLOSE, kernel)
        mask = cv2.erode(closingC2, kernel)
        edged = cv2.Canny(mask, 50, 220)
        contours, _ = cv2.findContours(mask, cv2.RETR_TREE,
                                       cv2.CHAIN_APPROX_SIMPLE)

        red_mask = cv2.inRange(hsv, red_lower, red_upper)
        red = cv2.bitwise_and(self.imageWarp, self.imageWarp, mask=red_mask)
        bFilterR2 = cv2.bilateralFilter(red_mask, 9, 75, 75)
        dilationR2 = cv2.dilate(bFilterR2, kernel, iterations=1)
        openingR2 = cv2.morphologyEx(dilationR2, cv2.MORPH_OPEN, kernel)
        closingR2 = cv2.morphologyEx(openingR2, cv2.MORPH_CLOSE, kernel)

        bFilterR2 = cv2.bilateralFilter(red, 9, 75, 75)
        dilationR = cv2.dilate(bFilterR2, kernel, iterations=1)
        openingR = cv2.morphologyEx(dilationR, cv2.MORPH_OPEN, kernel)
        closingR = cv2.morphologyEx(openingR, cv2.MORPH_CLOSE, kernel)

        green_mask = cv2.inRange(hsv, green_lower, green_upper)
        green = cv2.bitwise_and(self.imageWarp,
                                self.imageWarp,
                                mask=green_mask)
        dilationG2 = cv2.dilate(green_mask, kernel, iterations=1)
        openingG2 = cv2.morphologyEx(dilationG2, cv2.MORPH_OPEN, kernel)
        closingG2 = cv2.morphologyEx(openingG2, cv2.MORPH_CLOSE, kernel)

        dilationG = cv2.dilate(green, kernel, iterations=1)
        openingG = cv2.morphologyEx(dilationG, cv2.MORPH_OPEN, kernel)
        closingG = cv2.morphologyEx(openingG, cv2.MORPH_CLOSE, kernel)

        blue_mask = cv2.inRange(hsv, blue_lower, blue_upper)
        blue = cv2.bitwise_and(self.imageWarp, self.imageWarp, mask=blue_mask)
        dilationB2 = cv2.dilate(blue_mask, kernel, iterations=1)
        openingB2 = cv2.morphologyEx(dilationB2, cv2.MORPH_OPEN, kernel)
        closingB2 = cv2.morphologyEx(openingB2, cv2.MORPH_CLOSE, kernel)

        dilationB = cv2.dilate(blue, kernel, iterations=1)
        openingB = cv2.morphologyEx(dilationB, cv2.MORPH_OPEN, kernel)
        closingB = cv2.morphologyEx(openingB, cv2.MORPH_CLOSE, kernel)

        yellow_mask = cv2.inRange(hsv, yellow_lower, yellow_upper)
        yellow = cv2.bitwise_and(self.imageWarp,
                                 self.imageWarp,
                                 mask=yellow_mask)
        dilationY2 = cv2.dilate(yellow_mask, kernel, iterations=1)
        openingY2 = cv2.morphologyEx(dilationY2, cv2.MORPH_OPEN, kernel)
        closingY2 = cv2.morphologyEx(openingY2, cv2.MORPH_CLOSE, kernel)

        dilationY = cv2.dilate(yellow, kernel, iterations=1)
        openingY = cv2.morphologyEx(dilationY, cv2.MORPH_OPEN, kernel)
        closingY = cv2.morphologyEx(openingY, cv2.MORPH_CLOSE, kernel)

        black_mask = cv2.inRange(hsv, black_lower, black_upper)
        black = cv2.bitwise_and(self.imageWarp,
                                self.imageWarp,
                                mask=black_mask)
        dilationbl2 = cv2.dilate(black_mask, kernel, iterations=1)
        openingBl2 = cv2.morphologyEx(dilationbl2, cv2.MORPH_OPEN, kernel)
        closingBl2 = cv2.morphologyEx(openingBl2, cv2.MORPH_CLOSE, kernel)
        # openingBl2 = cv2.morphologyEx(black, cv2.MORPH_OPEN, kernel)
        # closingBl2 = cv2.morphologyEx(openingBl2, cv2.MORPH_CLOSE, kernel)
        # dilationBl2 = cv2.dilate(closingBl2, kernel, iterations=1)

        dilationBl = cv2.dilate(black, kernel, iterations=1)
        openingBl = cv2.morphologyEx(dilationBl, cv2.MORPH_OPEN, kernel)
        closingBl = cv2.morphologyEx(openingBl, cv2.MORPH_CLOSE, kernel)

        self.displayImage(self.image, closingC, 1)
        self.displayImage(self.image, mask, 2)
        self.displayImage(self.image, edged, 13)
        # self.displayImage(self.image, edge, 14)

        self.displayImage(self.image, closingR2, 3)
        self.displayImage(self.image, closingR, 4)
        self.displayImage(self.image, self.imageWarp, 15)

        self.displayImage(self.image, closingG2, 5)
        self.displayImage(self.image, closingG, 6)

        self.displayImage(self.image, closingB2, 7)
        self.displayImage(self.image, closingB, 8)

        self.displayImage(self.image, closingY2, 9)
        self.displayImage(self.image, closingY, 10)

        self.displayImage(self.image, closingBl2, 11)
        self.displayImage(self.image, closingBl, 12)

    def find_card(self):
        stateWork = 0
        print("find card ... !!!")
        rX = 100
        rY = 100
        gX = 100
        gY = 100
        bX = 100
        bY = 100
        yX = 100
        yY = 100
        blX = 100
        blY = 100
        self.debugTextBrowser.append("Home ...")
        ret, self.image = self.capture.read()

        self.notCircle = self.image.copy()

        cv2.circle(self.image,
                   (self.x1_slider.value(), self.y1_slider.value()), 5,
                   (0, 0, 255), -1)
        self.current_value.setText('X1 Y1 -> :' + str(self.x1_slider.value()) +
                                   ' ' + str(self.y1_slider.value()))
        cv2.circle(self.image,
                   (self.x2_slider.value(), self.y2_slider.value()), 5,
                   (0, 0, 255), -1)
        self.current_value2.setText('X2 Y2 -> :' +
                                    str(self.x2_slider.value()) + ' ' +
                                    str(self.y2_slider.value()))
        cv2.circle(self.image,
                   (self.x3_slider.value(), self.y3_slider.value()), 5,
                   (0, 0, 255), -1)
        self.current_value3.setText('X3 Y3 -> :' +
                                    str(self.x3_slider.value()) + ' ' +
                                    str(self.y3_slider.value()))
        cv2.circle(self.image,
                   (self.x4_slider.value(), self.y4_slider.value()), 5,
                   (0, 0, 255), -1)
        self.current_value4.setText('X4 Y4 -> :' +
                                    str(self.x4_slider.value()) + ' ' +
                                    str(self.y4_slider.value()))

        pts1 = np.float32([[self.x1_slider.value(),
                            self.y1_slider.value()],
                           [self.x2_slider.value(),
                            self.y2_slider.value()],
                           [self.x3_slider.value(),
                            self.y3_slider.value()],
                           [self.x4_slider.value(),
                            self.y4_slider.value()]])
        pts2 = np.float32([[0, 0], [500, 0], [0, 500], [500, 500]])
        matrix = cv2.getPerspectiveTransform(pts1, pts2)
        resultWarp = cv2.warpPerspective(self.notCircle, matrix, (500, 500))
        self.imageWarp = resultWarp

        # self.imageWarp = cv2.flip(self.imageWarp, 1)
        # self.displayImage(self.image, self.imageWarp, 1)

        Mblurred = cv2.medianBlur(self.imageWarp, 5)
        hsv = cv2.cvtColor(Mblurred, cv2.COLOR_BGR2HSV)

        color_lower = np.array(
            [self.card_lh.value(),
             self.card_ls.value(),
             self.card_lv.value()], np.uint8)
        color_upper = np.array(
            [self.card_uh.value(),
             self.card_us.value(),
             self.card_uv.value()], np.uint8)

        mask = cv2.inRange(hsv, color_lower, color_upper)
        kernel = np.ones((5, 5), np.uint8)

        red_lower = np.array(
            [self.red_lh.value(),
             self.red_ls.value(),
             self.red_lv.value()], np.uint8)
        red_upper = np.array(
            [self.red_uh.value(),
             self.red_us.value(),
             self.red_uv.value()], np.uint8)
        green_lower = np.array([
            self.green_lh.value(),
            self.green_ls.value(),
            self.green_lv.value()
        ], np.uint8)
        green_upper = np.array([
            self.green_uh.value(),
            self.green_us.value(),
            self.green_uv.value()
        ], np.uint8)
        blue_lower = np.array(
            [self.blue_lh.value(),
             self.blue_ls.value(),
             self.blue_lv.value()], np.uint8)
        blue_upper = np.array(
            [self.blue_uh.value(),
             self.blue_us.value(),
             self.blue_uv.value()], np.uint8)
        yellow_lower = np.array([
            self.yellow_lh.value(),
            self.yellow_ls.value(),
            self.yellow_lv.value()
        ], np.uint8)
        yellow_upper = np.array([
            self.yellow_uh.value(),
            self.yellow_us.value(),
            self.yellow_uv.value()
        ], np.uint8)
        black_lower = np.array([
            self.black_lh.value(),
            self.black_ls.value(),
            self.black_lv.value()
        ], np.uint8)
        black_upper = np.array([
            self.black_uh.value(),
            self.black_us.value(),
            self.black_uv.value()
        ], np.uint8)

        self.card_value.setText('Current Value -> Min :' + str(color_lower) +
                                ' Max: ' + str(color_upper))
        self.red_value.setText('Current Value -> Min :' + str(red_lower) +
                               ' Max: ' + str(red_upper))
        self.green_value.setText('Current Value -> Min :' + str(green_lower) +
                                 ' Max: ' + str(green_upper))
        self.blue_value.setText('Current Value -> Min :' + str(blue_lower) +
                                ' Max: ' + str(blue_upper))
        self.yellow_value.setText('Current Value -> Min :' +
                                  str(yellow_lower) + ' Max: ' +
                                  str(yellow_upper))
        self.black_value.setText('Current Value -> Min :' + str(black_lower) +
                                 ' Max: ' + str(black_upper))

        color_mask = cv2.inRange(hsv, color_lower, color_upper)
        card = cv2.bitwise_and(self.imageWarp, self.imageWarp, mask=color_mask)
        openingC2 = cv2.morphologyEx(color_mask, cv2.MORPH_OPEN, kernel)
        closingC2 = cv2.morphologyEx(openingC2, cv2.MORPH_CLOSE, kernel)
        openingC = cv2.morphologyEx(card, cv2.MORPH_OPEN, kernel)
        closingC = cv2.morphologyEx(openingC, cv2.MORPH_CLOSE, kernel)
        mask = cv2.erode(closingC2, kernel)
        edged = cv2.Canny(mask, 50, 220)
        contours, _ = cv2.findContours(mask, cv2.RETR_TREE,
                                       cv2.CHAIN_APPROX_SIMPLE)

        for cnt in contours:
            # time.sleep(0.1)
            area = cv2.contourArea(cnt)
            approx = cv2.approxPolyDP(cnt, 0.02 * cv2.arcLength(cnt, True),
                                      True)
            x = approx.ravel()[0]
            y = approx.ravel()[1]
            self.debugTextBrowser.append(str(area))

            if 14000 > area > 5000:
                # stateWork = 1
                if len(approx) == 4:
                    time.sleep(0.5)
                    cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST,
                                            cv2.CHAIN_APPROX_SIMPLE)
                    cnts = imutils.grab_contours(cnts)
                    cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]
                    Approx = approx
                    Outline = cv2.drawContours(self.imageWarp.copy(), [Approx],
                                               -5, (0, 0, 255), 1)
                    ratio = Outline.shape[0] / 500
                    Crop_card = four_point_transform(
                        self.imageWarp,
                        Approx.reshape(4, 2) * ratio)
                    Crop_card = cv2.resize(Crop_card, (int(500), int(500)))
                    img_name = "crop_card.png"

                    # time.sleep(0.1)
                    cv2.imwrite(img_name, Crop_card)
                    imgCrop = cv2.imread("crop_card.png")

                    # -----------------
                    clahe = cv2.createCLAHE(clipLimit=3., tileGridSize=(8, 8))
                    lab = cv2.cvtColor(imgCrop, cv2.COLOR_BGR2LAB)
                    l, a, b = cv2.split(lab)  # split on 3 different channels
                    l2 = clahe.apply(l)  # apply CLAHE to the L-channel
                    lab = cv2.merge((l2, a, b))  # merge channels
                    imgCrop = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)
                    # -----------------
                    # cardCount = 0
                    stateWork = 0
                    countRed = 0
                    countBlue = 0
                    countYellow = 0
                    countGreen = 0
                    countBlack = 0

                    cropBlur = cv2.medianBlur(imgCrop, 5)
                    hsv = cv2.cvtColor(cropBlur, cv2.COLOR_BGR2HSV)

                    red_mask = cv2.inRange(hsv, red_lower, red_upper)
                    red = cv2.bitwise_and(self.imageWarp,
                                          self.imageWarp,
                                          mask=red_mask)

                    bFilterR2 = cv2.bilateralFilter(red_mask, 9, 75, 75)
                    dilationR2 = cv2.dilate(bFilterR2, kernel, iterations=1)
                    openingR2 = cv2.morphologyEx(dilationR2, cv2.MORPH_OPEN,
                                                 kernel)
                    closingR2 = cv2.morphologyEx(openingR2, cv2.MORPH_CLOSE,
                                                 kernel)

                    # bFilterR2 = cv2.bilateralFilter(red_mask, 9, 75, 75)
                    dilationR = cv2.dilate(red, kernel, iterations=1)
                    openingR = cv2.morphologyEx(dilationR, cv2.MORPH_OPEN,
                                                kernel)
                    closingR = cv2.morphologyEx(openingR, cv2.MORPH_CLOSE,
                                                kernel)

                    green_mask = cv2.inRange(hsv, green_lower, green_upper)
                    green = cv2.bitwise_and(self.imageWarp,
                                            self.imageWarp,
                                            mask=green_mask)
                    dilationG2 = cv2.dilate(green_mask, kernel, iterations=1)
                    openingG2 = cv2.morphologyEx(dilationG2, cv2.MORPH_OPEN,
                                                 kernel)
                    closingG2 = cv2.morphologyEx(openingG2, cv2.MORPH_CLOSE,
                                                 kernel)

                    dilationG = cv2.dilate(green, kernel, iterations=1)
                    openingG = cv2.morphologyEx(dilationG, cv2.MORPH_OPEN,
                                                kernel)
                    closingG = cv2.morphologyEx(openingG, cv2.MORPH_CLOSE,
                                                kernel)

                    blue_mask = cv2.inRange(hsv, blue_lower, blue_upper)
                    blue = cv2.bitwise_and(self.imageWarp,
                                           self.imageWarp,
                                           mask=blue_mask)
                    dilationB2 = cv2.dilate(blue_mask, kernel, iterations=1)
                    openingB2 = cv2.morphologyEx(dilationB2, cv2.MORPH_OPEN,
                                                 kernel)
                    closingB2 = cv2.morphologyEx(openingB2, cv2.MORPH_CLOSE,
                                                 kernel)

                    dilationB = cv2.dilate(blue, kernel, iterations=1)
                    openingB = cv2.morphologyEx(dilationB, cv2.MORPH_OPEN,
                                                kernel)
                    closingB = cv2.morphologyEx(openingB, cv2.MORPH_CLOSE,
                                                kernel)

                    yellow_mask = cv2.inRange(hsv, yellow_lower, yellow_upper)
                    yellow = cv2.bitwise_and(self.imageWarp,
                                             self.imageWarp,
                                             mask=yellow_mask)
                    dilationY2 = cv2.dilate(yellow_mask, kernel, iterations=1)
                    openingY2 = cv2.morphologyEx(dilationY2, cv2.MORPH_OPEN,
                                                 kernel)
                    closingY2 = cv2.morphologyEx(openingY2, cv2.MORPH_CLOSE,
                                                 kernel)

                    dilationY = cv2.dilate(yellow, kernel, iterations=1)
                    openingY = cv2.morphologyEx(dilationY, cv2.MORPH_OPEN,
                                                kernel)
                    closingY = cv2.morphologyEx(openingY, cv2.MORPH_CLOSE,
                                                kernel)

                    black_mask = cv2.inRange(hsv, black_lower, black_upper)
                    black = cv2.bitwise_and(self.imageWarp,
                                            self.imageWarp,
                                            mask=black_mask)
                    bFilterBl2 = cv2.bilateralFilter(black_mask, 9, 75, 75)
                    dilationbl2 = cv2.dilate(bFilterBl2, kernel, iterations=1)
                    openingBl2 = cv2.morphologyEx(dilationbl2, cv2.MORPH_OPEN,
                                                  kernel)
                    closingBl2 = cv2.morphologyEx(openingBl2, cv2.MORPH_CLOSE,
                                                  kernel)

                    dilationBl = cv2.dilate(black, kernel, iterations=1)
                    openingBl = cv2.morphologyEx(dilationBl, cv2.MORPH_OPEN,
                                                 kernel)
                    closingBl = cv2.morphologyEx(openingBl, cv2.MORPH_CLOSE,
                                                 kernel)

                    contoursRed, _ = cv2.findContours(closingR2, cv2.RETR_TREE,
                                                      cv2.CHAIN_APPROX_NONE)
                    try:
                        biggest_contoursRed = max(contoursRed,
                                                  key=cv2.contourArea)
                        (x, y, w, h) = cv2.boundingRect(biggest_contoursRed)
                        # cv2.rectangle(imgCrop, (x, y),
                        #             (x+w, y+h), (0, 0, 255), 2)
                        countRed = 1

                        Mred = cv2.moments(biggest_contoursRed)
                        rX = int(Mred["m10"] / Mred["m00"])
                        rY = int(Mred["m01"] / Mred["m00"])
                        cv2.circle(imgCrop, (rX, rY), 5, (0, 0, 255), -1)
                        cv2.putText(
                            imgCrop,
                            'X :' + str(rX) + " Y :" + str(rY),
                            # bottomLeftCornerOfText
                            (rX + 30, rY),
                            cv2.FONT_HERSHEY_SIMPLEX,  # font
                            0.55,  # fontScale
                            (0, 0, 255),  # fontColor
                            1)

                    # cv2.putText(resultWarp, ":" + rX, (rX - 25, rY - 25),
                    #             cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
                    except:
                        pass
                    contoursBlue, _ = cv2.findContours(closingB2,
                                                       cv2.RETR_TREE,
                                                       cv2.CHAIN_APPROX_NONE)
                    try:
                        biggest_contoursBlue = max(contoursBlue,
                                                   key=cv2.contourArea)
                        (x, y, w, h) = cv2.boundingRect(biggest_contoursBlue)
                        # cv2.rectangle(imgCrop, (x, y),
                        #             (x+w, y+h), (255, 0, 0), 2)
                        countBlue = 1

                        Mblue = cv2.moments(biggest_contoursBlue)
                        bX = int(Mblue["m10"] / Mblue["m00"])
                        bY = int(Mblue["m01"] / Mblue["m00"])
                        cv2.circle(imgCrop, (bX, bY), 5, (255, 0, 0), -1)
                        cv2.putText(
                            imgCrop,
                            'X :' + str(bX) + " Y :" + str(bY),
                            # bottomLeftCornerOfText
                            (bX + 30, bY),
                            cv2.FONT_HERSHEY_SIMPLEX,  # font
                            0.55,  # fontScale
                            (255, 0, 0),  # fontColor
                            1)

                    except:
                        pass

                    contoursGreen, _ = cv2.findContours(
                        closingG2, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
                    try:
                        biggest_contoursGreen = max(contoursGreen,
                                                    key=cv2.contourArea)
                        (x, y, w, h) = cv2.boundingRect(biggest_contoursGreen)
                        # cv2.rectangle(imgCrop, (x, y),
                        #             (x+w, y+h), (0, 255, 0), 2)
                        countGreen = 1

                        Mgreen = cv2.moments(biggest_contoursGreen)
                        gX = int(Mgreen["m10"] / Mgreen["m00"])
                        gY = int(Mgreen["m01"] / Mgreen["m00"])
                        cv2.circle(imgCrop, (gX, gY), 5, (0, 255, 0), -1)
                        cv2.putText(
                            imgCrop,
                            'X :' + str(gX) + " Y :" + str(gY),
                            # bottomLeftCornerOfText
                            (gX + 30, gY),
                            cv2.FONT_HERSHEY_SIMPLEX,  # font
                            0.55,  # fontScale
                            (0, 255, 0),  # fontColor
                            1)

                        pGx = gX * 0.8
                        pGy = gY * 0.8
                        cv2.circle(imgCrop, (pGx, pGy), 5, (0, 255, 0), -1)

                    except:
                        pass

                    contoursYellow, _ = cv2.findContours(
                        closingY2, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

                    try:
                        biggest_contoursYellow = max(contoursYellow,
                                                     key=cv2.contourArea)
                        (x, y, w, h) = cv2.boundingRect(biggest_contoursYellow)
                        # cv2.rectangle(imgCrop, (x, y),
                        #             (x+w, y+h), (0, 255, 255), 2)
                        countYellow = 1

                        Myellow = cv2.moments(biggest_contoursYellow)
                        yX = int(Myellow["m10"] / Myellow["m00"])
                        yY = int(Myellow["m01"] / Myellow["m00"])
                        cv2.circle(imgCrop, (yX, yY), 5, (0, 255, 255), -1)
                        cv2.putText(
                            imgCrop,
                            'X :' + str(yX) + " Y :" + str(yY),
                            # bottomLeftCornerOfText
                            (yX + 30, yY),
                            cv2.FONT_HERSHEY_SIMPLEX,  # font
                            0.55,  # fontScale
                            (0, 255, 255),  # fontColor
                            1)

                    except:
                        pass

                    contoursBlack, _ = cv2.findContours(
                        closingBl2, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

                    try:
                        biggest_contoursBlack = max(contoursBlack,
                                                    key=cv2.contourArea)
                        (x, y, w, h) = cv2.boundingRect(biggest_contoursBlack)
                        # cv2.rectangle(imgCrop, (x, y), (x+w, y+h), (0, 0, 0), 2)
                        countBlack = 1

                        Mblack = cv2.moments(biggest_contoursBlack)
                        blX = int(Mblack["m10"] / Mblack["m00"])
                        blY = int(Mblack["m01"] / Mblack["m00"])
                        cv2.circle(imgCrop, (blX, blY), 5, (191, 191, 191), -1)
                        cv2.putText(
                            imgCrop,
                            'X :' + str(blX) + " Y :" + str(blY),
                            # bottomLeftCornerOfText
                            (blX + 30, blY),
                            cv2.FONT_HERSHEY_SIMPLEX,  # font
                            0.55,  # fontScale
                            (0, 0, 0),  # fontColor
                            1)

                    except:
                        pass

                    rX = int(250 - (rX / 2))
                    rY = int(250 - (rY / 2))
                    gX = int(250 - (gX / 2))
                    gY = int(250 - (gY / 2))
                    bX = int(250 - (bX / 2))
                    bY = int(250 - (bY / 2))
                    yX = int(250 - (yX / 2))
                    yY = int(250 - (yY / 2))
                    blX = int(250 - (blX / 2))
                    blY = int(250 - (blY / 2))

                    Send = [rY, rX, gY, gX, bY, bX, yY, yX, blY, blX]
                    # Send = [250,250,0,250,250,0,0,0,0,0]
                    self.debugTextBrowser.append(str(Send))

                    self.displayImage(Outline, imgCrop, 14)

                    if stateWork == 0:
                        print("stateWork = 0")
                        for i in Send:
                            time.sleep(0.2)
                            c = struct.pack('B', i)
                            serialPIC.write(c)
                            # self.debugTextBrowser.append(str(i))
                        # # r g b y black

                        sCount = 0
                        while sCount < 10:
                            # print("start "+ str(sCount))
                            Rpic = serialPIC.read()
                            Radr = serialAD.read()
                            if Rpic == b'1':
                                keep = struct.pack('B', 49)
                                serialAD.write(keep)
                                print("keep")
                            elif Rpic == b'0':
                                paste = struct.pack('B', 48)
                                serialAD.write(paste)
                                print("past")
                            else:
                                continue
                            # print(sCount)
                            sCount += 1

                        stateWork = 1

                        time.sleep(8)
                        print("------FINISH------- & statework =" +
                              str(stateWork))
                        subprocess.call(["afplay", "beep-06.wav"])
                        self.timer.stop()

                    # ------------------

                    # else:
                    #     # pass
                    #     self.timer.stop()
                    #     print("else stop")

                    # print("stop")
                    # self.timer.stop()

    def stop_webcam(self):
        self.capture.release()
        self.timer.stop()

    # def stop_card(self):
    #     # self.capture.release()
    #     self.timer.stop()

    def displayImage(self, img, imageWarp, window=1):
        qformat = QImage.Format_Indexed8
        if len(imageWarp.shape) == 3:  # [0]=rows, [1]=cols, [2]=channels
            if imageWarp.shape[2] == 4:
                qformat = QImage.Format_RGBA8888
            else:
                qformat = QImage.Format_RGB888

        outImage = QImage(img, img.shape[1], img.shape[0], img.strides[0],
                          qformat)
        outImage = outImage.rgbSwapped()

        outImageWarp = QImage(imageWarp, imageWarp.shape[1],
                              imageWarp.shape[0], imageWarp.strides[0],
                              qformat)
        outImageWarp = outImageWarp.rgbSwapped()

        if window == 1:

            self.frame_card1.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_card1.setScaledContents(True)
            self.camera_set.setPixmap(QPixmap.fromImage(outImage))
            self.camera_set.setScaledContents(True)

        if window == 2:
            self.frame_card2.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_card2.setScaledContents(True)
        elif window == 3:
            self.frame_red.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_red.setScaledContents(True)
        elif window == 4:
            self.frame_red2.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_red2.setScaledContents(True)

        elif window == 5:
            self.frame_green.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_green.setScaledContents(True)
        elif window == 6:
            self.frame_green2.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_green2.setScaledContents(True)
        elif window == 7:
            self.frame_blue.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_blue.setScaledContents(True)
        elif window == 8:
            self.frame_blue2.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_blue2.setScaledContents(True)
        elif window == 9:
            self.frame_yellow.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_yellow.setScaledContents(True)
        elif window == 10:
            self.frame_yellow2.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_yellow2.setScaledContents(True)
        elif window == 11:
            self.frame_black.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_black.setScaledContents(True)
        elif window == 12:
            self.frame_black2.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_black2.setScaledContents(True)
        elif window == 13:
            self.frame_card3.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_card3.setScaledContents(True)

        elif window == 14:
            self.card_output.setPixmap(QPixmap.fromImage(outImageWarp))
            self.card_output.setScaledContents(True)
            self.card_output2.setPixmap(QPixmap.fromImage(outImage))
            self.card_output2.setScaledContents(True)
            self.card_output3.setPixmap(QPixmap.fromImage(outImageWarp))
            self.card_output3.setScaledContents(True)

        elif window == 15:
            self.frame_red3.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_red3.setScaledContents(True)
            self.frame_green3.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_green3.setScaledContents(True)
            self.frame_blue3.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_blue3.setScaledContents(True)
            self.frame_yellow3.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_yellow3.setScaledContents(True)
            self.frame_black3.setPixmap(QPixmap.fromImage(outImageWarp))
            self.frame_black3.setScaledContents(True)
Ejemplo n.º 57
0
class NEditor(QPlainTextEdit):
    """Ninja-IDE Editor"""

    # Editor signals
    fontChanged = pyqtSignal('QString')
    zoomChanged = pyqtSignal(int)
    editorFocusObtained = pyqtSignal()
    cursor_position_changed = pyqtSignal(int, int)
    keyPressed = pyqtSignal(QKeyEvent)
    postKeyPressed = pyqtSignal(QKeyEvent)
    painted = pyqtSignal()
    current_line_changed = pyqtSignal(int)
    # FIXME: cambiar esto
    highlight_checker_updated = pyqtSignal('PyQt_PyObject')
    addBackItemNavigation = pyqtSignal()

    @property
    def nfile(self):
        return self._neditable.nfile

    @property
    def neditable(self):
        return self._neditable

    @property
    def file_path(self):
        return self._neditable.file_path

    @property
    def is_modified(self) -> bool:
        return self.document().isModified()

    @property
    def visible_blocks(self) -> Tuple[int, int, QTextBlock]:
        return self.__visible_blocks

    @property
    def default_font(self):
        return self.document().defaultFont()

    @property
    def encoding(self):
        if self.__encoding is not None:
            return self.__encoding
        return 'utf-8'

    @encoding.setter
    def encoding(self, encoding):
        self.__encoding = encoding

    @property
    def cursor_position(self) -> Tuple[int, int]:
        """Get or set the current cursor position.

        :param position: The position to set.
        :type position: tuple(line, column).
        :return: Current cursor position in document.
        :rtype: tuple(line, colum)."""

        cursor = self.textCursor()
        return (cursor.blockNumber(), cursor.columnNumber())

    @cursor_position.setter
    def cursor_position(self, position):
        line, column = position
        line = min(line, self.line_count() - 1)
        column = min(column, len(self.line_text(line)))
        cursor = QTextCursor(self.document().findBlockByNumber(line))
        cursor.setPosition(cursor.block().position() + column,
                           QTextCursor.MoveAnchor)
        self.setTextCursor(cursor)

    @property
    def background_color(self) -> QColor:
        """Get or set the background color.

        :param color: Color to set (name or hexa).
        :type color: QColor or str.
        :return: Background color.
        :rtype: QColor."""

        return self._background_color

    @background_color.setter
    def background_color(self, color):
        if isinstance(color, str):
            color = QColor(color)
        self._background_color = color
        # Refresh stylesheet
        self.__apply_style()

    @property
    def foreground_color(self):
        """Get or set the foreground color.
        :param color: Color to set (name or hexa).
        :type color: QColor or str.
        :return: Foreground color.
        :rtype: QColor"""

        return self._foreground_color

    @foreground_color.setter
    def foreground_color(self, color):
        if isinstance(color, str):
            color = QColor(color)
        self._foreground_color = color
        self.__apply_style()

    @property
    def show_whitespaces(self):
        return self.__show_whitespaces

    @show_whitespaces.setter
    def show_whitespaces(self, show):
        if self.__show_whitespaces != show:
            self.__show_whitespaces = show
            self.__set_whitespaces_flags(show)

    @property
    def margins(self):
        return self.__margins

    def __init__(self, neditable):
        QPlainTextEdit.__init__(self)
        self.setFrameStyle(0)  # Remove border
        self._neditable = neditable
        self.setMouseTracking(True)

        # Style
        self._background_color = QColor(
            resources.get_color('EditorBackground'))
        self._foreground_color = QColor(resources.get_color('Default'))
        self._selection_color = QColor(
            resources.get_color('EditorSelectionColor'))
        self._selection_background_color = QColor(
            resources.get_color('EditorSelectionBackground'))
        self.__apply_style()

        self._init_settings()
        self._highlighter = None
        self.__visible_blocks = []
        self._last_line_position = 0
        self.__encoding = None
        self.__show_whitespaces = settings.SHOW_TABS_AND_SPACES
        self.__side_widgets = []
        # Extra Selections
        self._extra_selections = OrderedDict()
        self._current_line_selection = None
        self.__checker_extra_selections = []
        self.__occurrences = []
        # Load indenter based on language
        self._indenter = indenter.load_indenter(self, neditable.language())
        # Set editor font before build lexer
        self.set_font(settings.FONT)
        self.register_syntax_for(neditable.language())
        # Register all editor extension
        self.__extensions = {}
        self.initialize_extensions()
        # FIXME: based on lang
        self.enable_extension('indentation_guides',
                              settings.SHOW_INDENTATION_GUIDE)
        self.enable_extension('margin_line', settings.SHOW_MARGIN_LINE)
        self.enable_extension('line_highlighter', True)
        self.enable_extension('symbol_highlighter', True)
        self.enable_extension('quotes', True)
        self.enable_extension('braces', True)
        # self._symbol_completer = symbol_completer.SymbolCompleter(self)

        # Mark occurrences timer
        self._highlight_word_timer = QTimer()
        self._highlight_word_timer.setSingleShot(True)
        self._highlight_word_timer.setInterval(800)
        self._highlight_word_timer.timeout.connect(
            self.highlight_selected_word)
        # Install custom scrollbar
        self._scrollbar = scrollbar.NScrollBar(self)
        self._scrollbar.setAttribute(Qt.WA_OpaquePaintEvent, False)
        self.setVerticalScrollBar(self._scrollbar)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.additional_builtins = None
        # Set the editor after initialization
        if self._neditable is not None:
            if self._neditable.editor:
                self.setDocument(self._neditable.document)
            else:
                self._neditable.set_editor(self)
            self._neditable.checkersUpdated.connect(self._highlight_checkers)
        # Widgets on side area
        self._line_number_area = None
        if settings.SHOW_LINE_NUMBERS:
            self._line_number_area = self.add_side_widget(
                line_number_area.LineNumberArea, 2)
        self._lint_area = None
        # if settings.SHOW_LINT_AREA:
        #    self._lint_area = self.add_side_widget(lint_area.LintArea, 3)
        self._marker_area = None
        # if settings.SHOW_MARK_AREA:
        #    self._marker_area = self.add_side_widget(marker_area.MarkerArea, 1)
        self._text_change_area = None
        if settings.SHOW_TEXT_CHANGE_AREA:
            self._text_change_area = self.add_side_widget(
                text_change_area.TextChangeArea, 0)
        # FIXME: we need a method to initialize
        self.__set_whitespaces_flags(self.__show_whitespaces)

        self.cursorPositionChanged.connect(self._on_cursor_position_changed)
        self.cursorPositionChanged.connect(self.viewport().update)

    def dropEvent(self, event):
        if event.type() == Qt.ControlModifier and self.has_selection:
            insertion_cursor = self.cursorForPosition(event.pos())
            insertion_cursor.insertText(self.selected_text())
        else:
            super().dropEvent(event)

    def set_language(self, language):
        self.register_syntax_for(language=language)
        self._indenter = indenter.load_indenter(self, lang=language)

    def register_syntax_for(self, language=None):
        if language is None:
            return
        syntax_registry = IDE.get_service("syntax_registry")
        syntax = syntax_registry.get_syntax_for(language)
        if syntax is None:
            syntax = syntaxhighlighter.build_highlighter_for(language)

        self._highlighter = syntaxhighlighter.SyntaxHighlighter(
            self.document(), syntax.partition_scanner, syntax.scanners,
            syntax.formats)

    def initialize_extensions(self):
        """Register all extensions in this Editor"""

        for Klass in ExtensionRegistry.extensions:
            self.__extensions[Klass.name] = Klass(self)

    def indentation_width(self):
        """Returns the indentation width of current indenter"""

        return self._indenter.width

    def move_up_down(self, up=False):
        cursor = self.textCursor()
        move = cursor
        with self:
            has_selection = cursor.hasSelection()
            start, end = cursor.selectionStart(), cursor.selectionEnd()
            if has_selection:
                move.setPosition(start)
                move.movePosition(QTextCursor.StartOfBlock)
                move.setPosition(end, QTextCursor.KeepAnchor)
                m = QTextCursor.EndOfBlock
                if move.atBlockStart():
                    m = QTextCursor.Left
                move.movePosition(m, QTextCursor.KeepAnchor)
            else:
                move.movePosition(QTextCursor.StartOfBlock)
                move.movePosition(QTextCursor.EndOfBlock,
                                  QTextCursor.KeepAnchor)

            text = cursor.selectedText()
            move.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor)
            move.removeSelectedText()

            if up:
                move.movePosition(QTextCursor.PreviousBlock)
                move.insertBlock()
                move.movePosition(QTextCursor.Left)
            else:
                move.movePosition(QTextCursor.EndOfBlock)
                if move.atBlockStart():
                    move.movePosition(QTextCursor.NextBlock)
                    move.insertBlock()
                    move.movePosition(QTextCursor.Left)
                else:
                    move.insertBlock()

            start = move.position()
            move.clearSelection()
            move.insertText(text)
            end = move.position()
            if has_selection:
                move.setPosition(end)
                move.setPosition(start, QTextCursor.KeepAnchor)
            else:
                move.setPosition(start)
            self.setTextCursor(move)

    def duplicate_line(self):
        cursor = self.textCursor()
        if cursor.hasSelection():
            text = cursor.selectedText()
            start = cursor.selectionStart()
            end = cursor.selectionEnd()
            cursor_at_start = cursor.position() == start
            cursor.setPosition(end)
            cursor.insertText("\n" + text)
            cursor.setPosition(end if cursor_at_start else start)
            cursor.setPosition(start if cursor_at_start else end,
                               QTextCursor.KeepAnchor)
        else:
            position = cursor.position()
            block = cursor.block()
            text = block.text() + "\n"
            cursor.setPosition(block.position())
            cursor.insertText(text)
            cursor.setPosition(position)
        self.setTextCursor(cursor)

    @property
    def occurrences(self):
        return self.__occurrences

    def adjust_scrollbar_ranges(self):
        line_spacing = QFontMetrics(self.font()).lineSpacing()
        if line_spacing == 0:
            return
        offset = self.contentOffset().y()
        self._scrollbar.set_visible_range(
            (self.viewport().rect().height() - offset) / line_spacing)
        self._scrollbar.set_range_offset(offset / line_spacing)

    def _highlight_checkers(self, neditable):
        """Add checker selections to the Editor"""
        # Remove selections if they exists
        self.clear_extra_selections('checker')
        # Get checkers from neditable
        checkers = neditable.sorted_checkers
        self.highlight_checker_updated.emit(checkers)
        selections = []
        # FIXME: generalize it with extra_selection.ExtraSelection
        for items in checkers:
            checker, color, _ = items
            lines = checker.checks.keys()
            for line in lines:
                msg, col = checker.checks[line]
                selection = QTextEdit.ExtraSelection()
                selection.cursor = self.textCursor()
                selection.cursor.movePosition(QTextCursor.Start,
                                              QTextCursor.MoveAnchor)
                selection.cursor.movePosition(QTextCursor.Down,
                                              QTextCursor.MoveAnchor, line)
                selection.cursor.movePosition(QTextCursor.Right,
                                              QTextCursor.MoveAnchor, col - 1)
                selection.cursor.movePosition(QTextCursor.EndOfLine,
                                              QTextCursor.KeepAnchor)
                selection.format.setUnderlineStyle(
                    QTextCharFormat.SingleUnderline)
                selection.format.setUnderlineColor(QColor(color))
                selections.append(selection)

        self.add_extra_selections('checker', selections)

    def extra_selections(self, selection_key):
        return self._extra_selections.get(selection_key, [])

    def add_extra_selections(self, selection_key, selections):
        """Adds a extra selection on a editor instance"""
        self._extra_selections[selection_key] = selections
        self.update_extra_selections()

    def clear_extra_selections(self, selection_key):
        """Removes a extra selection from the editor"""
        if selection_key in self._extra_selections:
            self._extra_selections[selection_key] = []
            self.update_extra_selections()

    def update_extra_selections(self):
        extra_selections = []
        for key, selection in self._extra_selections.items():
            extra_selections.extend(selection)
        self.setExtraSelections(extra_selections)

    def all_extra_selections(self):
        return self._extra_selections

    def _init_settings(self):
        """Set some configuration flags for the Editor"""
        # FIXME:
        self.setCursorWidth(1)
        # Wrap Mode
        wrap_mode = QPlainTextEdit.NoWrap
        if settings.ALLOW_WORD_WRAP:
            wrap_mode = QPlainTextEdit.WidgetWidth
        self.setLineWrapMode(wrap_mode)

    def _update_tab_stop_width(self):
        """Update the tab stop width"""

        width = self.fontMetrics().width(' ') * self._indenter.WIDTH
        self.setTabStopWidth(width)

    def clone(self):
        """Returns an instance of the same class and links this
        instance with its original"""

        line, col = self.cursor_position
        clone = self.__class__(self.neditable)
        clone.cursor_position = line, col
        clone.setExtraSelections(self.extraSelections())
        return clone

    def line_count(self):
        """Returns the number of lines"""

        return self.document().blockCount()

    def set_font(self, font):
        """Set font and update tab stop width"""

        QPlainTextEdit.setFont(self, font)
        self._update_tab_stop_width()

    def line_text(self, line):
        """Returns the text of the specified line.

        :param line: The line number of the text to return.
        :return: Entire lines text.
        :rtype: str.
        """

        block = self.document().findBlockByNumber(line)
        return block.text()

    @property
    def text(self):
        """Get or set the plain text editor's content. The previous contents
        are removed.

        :param text: Text to set in document.
        :type text: string.
        :return: The plain text in document.
        :rtype: string.
        """

        return self.toPlainText()

    @text.setter
    def text(self, text):
        self.setPlainText(text)

    @pyqtSlot()
    def _on_cursor_position_changed(self):
        self.__clear_occurrences()
        line, col = self.cursor_position
        self.cursor_position_changed.emit(line, col)
        if line != self._last_line_position:
            self._last_line_position = line
            self.current_line_changed.emit(line)
        # Create marker for scrollbar
        self.update_current_line_in_scrollbar(line)
        # Mark occurrences
        self._highlight_word_timer.stop()
        self._highlight_word_timer.start()

    def update_current_line_in_scrollbar(self, current_line):
        """Update current line highlight in scrollbar"""

        self._scrollbar.remove_marker('current_line')
        if self._scrollbar.maximum() > 0:
            Marker = scrollbar.marker
            marker = Marker(current_line, 'white', priority=2)
            self._scrollbar.add_marker('current_line', marker)

    def add_side_widget(self, Widget, order=0):
        obj = Widget(self)
        obj.order = order
        self.__side_widgets.append(obj)
        if obj.isVisible:
            self.update_viewport()
        self.__side_widgets.sort(key=lambda widget: widget.order)
        return obj

    def __apply_style(self):
        palette = self.palette()
        palette.setColor(palette.Base, self._background_color)
        palette.setColor(palette.Text, self._foreground_color)
        palette.setColor(palette.HighlightedText, self._selection_color)
        palette.setColor(palette.Highlight, self._selection_background_color)
        self.setPalette(palette)
        # FIXME: no funciona con qpalette
        # self.setStyleSheet('border: 0px solid transparent;')

    def update_viewport(self):
        """
        Recalculates geometry for all the side widgets and the editor viewport
        """

        cr = self.contentsRect()
        current_x = cr.left()
        top = cr.top()
        height = cr.height()

        total_width = 0
        for margin in self.__side_widgets:
            margin.setGeometry(current_x, top, margin.width(), height)
            current_x += margin.width()
            total_width += margin.width()
        self.setViewportMargins(total_width, 0, 0, 0)

    def focusInEvent(self, event):
        super().focusInEvent(event)
        self.editorFocusObtained.emit()

    def resizeEvent(self, event):
        QPlainTextEdit.resizeEvent(self, event)
        self.update_viewport()
        self.adjust_scrollbar_ranges()

    def paintEvent(self, event):
        self._update_visible_blocks()
        # Emit signal for extensions
        self.painted.emit()
        QPlainTextEdit.paintEvent(self, event)

    def mouseReleaseEvent(self, event):
        # if event.modifiers() == Qt.ControlModifier:
        #    self._code_completion.go_to_definition()
        super().mouseReleaseEvent(event)

    def enable_extension(self, extension_name, value):
        # I'm sure the object always exists
        ext_obj = self.__extensions.get(extension_name)
        if ext_obj is None:
            logger.error("Extension '%s' not found" % extension_name)
        else:
            ext_obj.enabled = value
            logger.debug("Loaded '%s' extension" % extension_name)

    def mouseMoveEvent(self, event):
        '''if event.modifiers() == Qt.ControlModifier:
            if self.__link_selection is not None:
                return
            cursor = self.cursorForPosition(event.pos())
            # Check that the mouse was actually on the text somewhere
            on_text = self.cursorRect(cursor).right() >= event.x()
            if on_text:
                cursor.select(QTextCursor.WordUnderCursor)
                selection_start = cursor.selectionStart()
                selection_end = cursor.selectionEnd()
                self.__link_selection = extra_selection.ExtraSelection(
                    cursor,
                    start_pos=selection_start,
                    end_pos=selection_end
                )
                self.__link_selection.set_underline("red")
                self.__link_selection.set_full_width()
                self.add_extra_selection(self.__link_selection)
                self.viewport().setCursor(Qt.PointingHandCursor)'''
        super(NEditor, self).mouseMoveEvent(event)

    def scroll_step_up(self):
        self.verticalScrollBar().triggerAction(
            QAbstractSlider.SliderSingleStepSub)

    def scroll_step_down(self):
        self.verticalScrollBar().triggerAction(
            QAbstractSlider.SliderSingleStepAdd)

    def text_before_cursor(self, text_cursor=None):
        if text_cursor is None:
            text_cursor = self.textCursor()
        text_block = text_cursor.block().text()
        return text_block[:text_cursor.positionInBlock()]

    def keyReleaseEvent(self, event):
        # if event.key() == Qt.Key_Control:
        #    if self.__link_selection is not None:
        #        self.remove_extra_selection(self.__link_selection)
        #        self.__link_selection = None
        #        self.viewport().setCursor(Qt.IBeamCursor)
        super().keyReleaseEvent(event)

    def keyPressEvent(self, event):
        if self.isReadOnly():
            return
        # Emit a signal then plugins can do something
        event.ignore()
        self.keyPressed.emit(event)
        if event.matches(QKeySequence.InsertParagraphSeparator):
            self._indenter.indent_block(self.textCursor())
            return
        if event.key() == Qt.Key_Home:
            self.__manage_key_home(event)
            return
        elif event.key() == Qt.Key_Tab:
            if self.textCursor().hasSelection():
                self._indenter.indent_selection()
            else:
                self._indenter.indent()
            event.accept()
        elif event.key() == Qt.Key_Backspace:
            if self.__smart_backspace():
                event.accept()
        if not event.isAccepted():
            super().keyPressEvent(event)
        # Post key press
        self.postKeyPressed.emit(event)

    def _auto_indent(self):
        cursor = self.textCursor()
        at_start_of_line = cursor.positionInBlock() == 0
        with self:
            cursor.insertBlock()
            if not at_start_of_line:
                indent = self._indenter.indent_block(cursor.block())
                if indent is not None:
                    cursor.insertText(indent)

    def __smart_backspace(self):
        accepted = False
        cursor = self.textCursor()
        text_before_cursor = self.text_before_cursor(cursor)
        text = cursor.block().text()
        indentation = self._indenter.text()
        space_at_start_len = len(text) - len(text.lstrip())
        column_number = cursor.positionInBlock()
        if text_before_cursor.endswith(indentation) and \
                space_at_start_len == column_number and \
                not cursor.hasSelection():
            to_remove = len(text_before_cursor) % len(indentation)
            if to_remove == 0:
                to_remove = len(indentation)
            cursor.setPosition(cursor.position() - to_remove,
                               QTextCursor.KeepAnchor)
            cursor.removeSelectedText()
            accepted = True
        return accepted

    def __manage_key_home(self, event):
        """Performs home key action"""
        cursor = self.textCursor()
        indent = self.line_indent()
        # For selection
        move = QTextCursor.MoveAnchor
        if event.modifiers() == Qt.ShiftModifier:
            move = QTextCursor.KeepAnchor
        # Operation
        if cursor.positionInBlock() == indent:
            cursor.movePosition(QTextCursor.StartOfBlock, move)
        elif cursor.atBlockStart():
            cursor.setPosition(cursor.block().position() + indent, move)
        elif cursor.positionInBlock() > indent:
            cursor.movePosition(QTextCursor.StartOfLine, move)
            cursor.setPosition(cursor.block().position() + indent, move)
        self.setTextCursor(cursor)
        event.accept()

    def __enter__(self):
        self.textCursor().beginEditBlock()

    def __exit__(self, exc_type, exc_value, traceback):
        self.textCursor().endEditBlock()

    def selection_range(self):
        """Returns the start and end number of selected lines"""

        text_cursor = self.textCursor()
        start = self.document().findBlock(
            text_cursor.selectionStart()).blockNumber()
        end = self.document().findBlock(
            text_cursor.selectionEnd()).blockNumber()
        if text_cursor.columnNumber() == 0 and start != end:
            end -= 1
        return start, end

    def _update_visible_blocks(self):
        """Updates the list of visible blocks"""

        self.__visible_blocks = []
        block = self.firstVisibleBlock()
        block_number = block.blockNumber()
        top = self.blockBoundingGeometry(block).translated(
            self.contentOffset()).top()
        bottom = top + self.blockBoundingRect(block).height()
        editor_height = self.height()
        while block.isValid():
            visible = bottom <= editor_height
            if not visible:
                break
            if block.isVisible():
                self.__visible_blocks.append((top, block_number, block))
            block = block.next()
            top = bottom
            bottom = top + self.blockBoundingRect(block).height()
            block_number += 1

    def zoom(self, delta: int):
        font = self.default_font
        previous_point_size = font.pointSize()
        new_point_size = int(max(1, previous_point_size + delta))
        if new_point_size != previous_point_size:
            font.setPointSize(new_point_size)
            self.set_font(font)
            # Set font in all margins
            for side_area in self.__side_widgets:
                side_area.setFont(font)
            # Emit signal for indicator
            default_point_size = settings.FONT.pointSize()
            percent = new_point_size / default_point_size * 100.0
            self.zoomChanged.emit(percent)

    def reset_zoom(self):
        font = self.default_font
        default_point_size = settings.FONT.pointSize()
        if font.pointSize() != default_point_size:
            font.setPointSize(default_point_size)
            self.set_font(font)
            # Set font in all margins
            for margin in self.__side_widgets:
                margin.setFont(font)
            # Emit signal for indicator
            self.zoomChanged.emit(100)

    def __set_whitespaces_flags(self, show):
        """Sets white spaces flag"""

        doc = self.document()
        options = doc.defaultTextOption()
        if show:
            options.setFlags(options.flags() | QTextOption.ShowTabsAndSpaces)
        else:
            options.setFlags(options.flags() & ~QTextOption.ShowTabsAndSpaces)
        doc.setDefaultTextOption(options)

    def selected_text(self):
        """Returns the selected text"""

        return self.textCursor().selectedText()

    def has_selection(self):
        return self.textCursor().hasSelection()

    def get_right_word(self):
        """Gets the word on the right of the text cursor"""

        cursor = self.textCursor()
        cursor.movePosition(QTextCursor.WordRight, QTextCursor.KeepAnchor)
        return cursor.selectedText().strip()

    def get_right_character(self):
        """Gets the right character on the right of the text cursor"""

        right_word = self.get_right_word()
        right_char = None
        if right_word:
            right_char = right_word[0]
        return right_char

    def is_code(self, cursor):
        user_data = cursor.block().userData()
        if user_data is not None:
            pass

        # this_syntax = syntax_registry.get_syntax_for()
        # if syntax is not None:
        #    self._hig = syntaxhighlighter.SyntaxHighlighter(
        #        self.document(),
        #        syntax.partition_scanner,
        #        syntax.scanners,
        #        syntax.formats
        #    )

        # syntax = syntaxhighlighter.load_syntax(language)
        # if syntax is not None:
        #    partition_scanner = syntax.partition_scanner
        #    scanners = syntax.scanners
        #    formats = syntax.formats
        #    self._highlighter = syntaxhighlighter.SyntaxHighlighter(
        #        self.document(), partition_scanner, scanners, formats)

    def line_indent(self, line=-1):
        """Returns the indentation level of `line`"""

        if line == -1:
            line, _ = self.cursor_position
        text = self.document().findBlockByNumber(line).text()
        indentation = len(text) - len(text.lstrip())
        return indentation

    def replace_match(self,
                      word_old,
                      word_new,
                      cs=False,
                      wo=False,
                      all_words=False,
                      selection=False):
        """
        Find if searched text exists and replace it with new one.
        If there is a selection just do it inside it and exit
        """

        cursor = self.textCursor()
        if selection:
            if not cursor.hasSelection():
                return
            start, end = cursor.selectionStart(), cursor.selectionEnd()
            text = cursor.selectedText()
            old_len = len(text)
            text = text.replace(word_old, word_new)
            new_len = len(text)
            cursor.insertText(text)
            # Set selection
            cursor.setPosition(start)
            cursor.setPosition(end + new_len - old_len, QTextCursor.KeepAnchor)
            self.setTextCursor(cursor)
            return

        # Replace once
        cursor.insertText(word_new)
        if not all_words:
            return

        # Replace all
        flags = QTextDocument.FindFlags()
        if wo:
            flags |= QTextDocument.FindWholeWords
        if cs:
            flags |= QTextDocument.FindCaseSensitively

        self.moveCursor(QTextCursor.Start)

        cursor.beginEditBlock()

        while all_words:
            found = self.find(word_old, flags)
            if found:
                cursor = self.textCursor()
                if cursor.hasSelection():
                    cursor.insertText(word_new)
            else:
                break
        # Set cursor on last replace text
        self.setTextCursor(cursor)
        cursor.endEditBlock()

    def find_matches(self,
                     search,
                     case_sensitive=False,
                     whole_word=False,
                     backward=False,
                     find_next=True):
        flags = QTextDocument.FindFlags()
        if case_sensitive:
            flags = QTextDocument.FindCaseSensitively
        if whole_word:
            flags |= QTextDocument.FindWholeWords
        if backward:
            flags |= QTextDocument.FindBackward
        if find_next or backward:
            self.moveCursor(QTextCursor.NoMove)
        else:
            self.moveCursor(QTextCursor.StartOfWord)
        found = self.find(search, flags)
        cursor = self.textCursor()
        if not found:
            # Original cursor is saved for restore later
            cursor = self.textCursor()
            # Move cursor to beginning of document to search again
            if backward:
                self.moveCursor(QTextCursor.End)
            else:
                self.moveCursor(QTextCursor.Start)
            found = self.find(search, flags)
            if not found:
                # self.clear_extra_selections()
                # Restore cursor
                self.setTextCursor(cursor)
                self.clear_extra_selections('searchs')
                return 0, []
        index, results = self._get_find_index_results(search, case_sensitive,
                                                      whole_word)
        # TODO: obtain line numbers for highlight in scrollbar
        # TODO: cambiar el 2
        # FIXME: clear its ok?
        self.clear_extra_selections('searchs')

        if len(search) > 2:
            ss = []
            append = ss.append
            results = results[:500]
            for start, end in results:
                s = extra_selection.ExtraSelection(self.textCursor(),
                                                   start_pos=start,
                                                   end_pos=end)
                s.set_full_width()
                c = QColor('yellow')
                c.setAlpha(40)
                s.set_background(c)
                s.set_outline('gray')
                append(s)
            self.add_extra_selections('searchs', ss)

        return index, results

    def is_comment(self, block):
        """Check if the block is a inline comment"""

        text_block = block.text().lstrip()
        return text_block.startswith('#')  # FIXME: generalize it

    def _get_find_index_results(self, expr: str, cs: bool,
                                wo: bool) -> Tuple[int, list]:
        text = self.text
        has_search = len(expr) > 0
        if not has_search:
            return 0, 0

        def find_all_iter(string, sub, flags):
            for i in re.finditer(sub, string, flags):
                yield i.span()

        flags = re.UNICODE
        search = expr  # for count whole words
        if wo:
            expr = "\\b{}\\b".format(expr)
        if not cs:
            text = text.lower()
            expr = expr.lower()
            flags |= re.IGNORECASE
        results = list(find_all_iter(text, expr, flags))
        pos = self.textCursor().position()
        index = text[:pos].count(search)
        return index, results

    def __clear_occurrences(self):
        """Clear extra selection occurrences from editor and scrollbar"""

        self.__occurrences.clear()
        self._scrollbar.remove_marker('occurrence')
        self.clear_extra_selections('occurrences')

    def highlight_selected_word(self):
        import keyword
        self.__clear_occurrences()

        text = self._text_under_cursor()
        if not text:
            return
        # Do not highlight keywords
        if text in keyword.kwlist or text == 'self':
            return
        result = self._get_find_index_results(text, False, True)[1]
        selections = []
        for start, end in result:
            selection = extra_selection.ExtraSelection(self.textCursor(),
                                                       start_pos=start,
                                                       end_pos=end)
            selection.set_full_width()
            # FIXME: from theme
            selection.set_background(resources.get_color('SearchResult'))
            selections.append(selection)
            line = selection.cursor.blockNumber()
            Marker = scrollbar.marker
            marker = Marker(line, resources.get_color('SearchResult'), 0)
            self._scrollbar.add_marker('occurrence', marker)
        self.add_extra_selections('occurrences', selections)

    def line_from_position(self, position):
        height = self.fontMetrics().height()
        for top, line, block in self.__visible_blocks:
            if top <= position <= top + height:
                return line
        return -1

    def _text_under_cursor(self):
        text_cursor = self.textCursor()
        text_cursor.select(QTextCursor.WordUnderCursor)
        match = re.findall(r'([^\d\W]\w*)', text_cursor.selectedText())
        if match:
            return match[0]

    def go_to_line(self, lineno, column=0, center=True):
        """Go to an specific line

        :param lineno: The line number to go
        :param column: The column number to go
        :param center: If True scrolls the document in order to center the
        cursor vertically.
        :type lineno: int"""

        if self.line_count() >= lineno:
            self.cursor_position = lineno, column
            if center:
                self.centerCursor()
            else:
                self.ensureCursorVisible()
        self.addBackItemNavigation.emit()

    def set_brace_matching(self, value):
        pass

    def comment(self):
        pass

    def save_state(self):
        state = {}
        state['vscrollbar'] = self.verticalScrollBar().value()
        return state
Ejemplo n.º 58
0
class KeyhintWidget(QLabel):
    """Widget to display partial matches above the statusbar.

    The widget is shown when there are partial keybinding matches, e.g. on 'g'. It is
    filled with a list of possible keybindings to fulfill a command.

    Attributes:
        _show_timer: Timer used to show the widget on partial matches after a delay.
        _suffix_color: Color used to display the remaining keys for a match.
        _mainwindow_bottom: y-coordinate of the bottom of the mainwindow.
    """

    STYLESHEET = """
    QLabel {
        font: {statusbar.font};
        color: {statusbar.fg};
        background: {statusbar.bg};
        padding: {keyhint.padding};
        border-top-right-radius: {keyhint.border_radius};
    }
    """

    def __init__(self, parent):
        super().__init__(parent=parent)
        self._show_timer = QTimer(self)
        self._show_timer.setSingleShot(True)
        self._show_timer.setInterval(api.settings.keyhint.delay.value)
        self._show_timer.timeout.connect(self.show)

        self._suffix_color = styles.get("keyhint.suffix_color")
        self._mainwindow_bottom = 0

        styles.apply(self)
        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Minimum)
        self.setTextFormat(Qt.RichText)

        partial_handler = eventhandler.EventHandlerMixin.partial_handler
        partial_handler.partial_matches.connect(self._on_partial_matches)
        partial_handler.partial_cleared.connect(self._on_partial_cleared)
        api.settings.keyhint.delay.changed.connect(self._on_delay_changed)

        self.hide()

    def update_geometry(self, _window_width, window_bottom):
        """Adapt location when main window geometry changes."""
        self._mainwindow_bottom = window_bottom
        self._update_geometry()

    def _update_geometry(self):
        """Update geometry according to current text content and window location."""
        self.adjustSize()
        y = self._mainwindow_bottom - self.height()
        self.setGeometry(0, y, self.width(), self.height())

    def _on_partial_matches(self, prefix: str, matches: Iterator[Tuple[str,
                                                                       str]]):
        """Initialize widget when partial matches exist.

        Args:
            prefix: Key(s) pressed for which there are partial matches.
            matches: List of keybindings and commands that can complete the partial.
        """
        self._show_timer.start()
        text = ""
        for keybinding, command in matches:
            suffix = keybinding[len(prefix):]
            prefix = utils.escape_html(prefix)
            suffix = utils.escape_html(suffix)
            text += ("<tr>"
                     f"<td>{prefix}</td>"
                     f"<td style='color: {self._suffix_color}'>{suffix}</td>"
                     f"<td style='padding-left: 2ex'>{command}</td>"
                     "</tr>")
        self.setText(f"<table>{text}</table>")
        self._update_geometry()

    @utils.slot
    def _on_partial_cleared(self):
        """Stop timer and hide widget when there are no partial matches to show."""
        self._show_timer.stop()
        self.hide()

    def _on_delay_changed(self, value: int):
        """Update timer interval if the keyhint delay setting changed."""
        self._show_timer.setInterval(value)

    def show(self):
        """Override show to always raise the widget in addition."""
        super().show()
        self.raise_()
Ejemplo n.º 59
0
class Game(QWidget):
    def __init__(self):
        super().__init__()
        self.spinner = Spinner()
        self.tries_remaining = 9
        self.game_end_fail = False
        self.game_end_triumph = False
        self.game_timeout = False
        self.screen_updated = False
        self.long_message_length = 10
        self.game_time = 1500
        self.default_message = 'Waiting for database key ...'
        self.game_failure_message = 'Database shutdown, system intrusion detected'
        self.secret_keys = {
            '9291184': False,
            '117193': False,
            '861364710132011141107': False
        }
        self.secret_password = '******'
        self.background_image_path = self.resource_path("Webster_Notes.png")
        self.font = "Times"
        self.font_size = 8
        self.initUI()

    def initUI(self):
        myfont = QFont(self.font, self.font_size)

        oImage = QImage(self.background_image_path)
        sImage = oImage.scaled(QSize(1500, 800))
        palette = QPalette()
        palette.setBrush(10, QBrush(sImage))
        self.setPalette(palette)

        self.timer_label = QLabel(self)
        self.timer = QTimer()

        self.qline_edit = QLineEdit(self)
        self.qline_edit.setFont(myfont)
        self.message_label = QLabel(self.default_message, self)
        self.message_label.setFont(myfont)
        self.code_label = QLabel(self)
        self.code_label.setFont(myfont)
        self.timer_label.setFont(myfont)
        self.success_message_label = QLabel(self)
        self.success_message_label.setFont(myfont)
        self.qbutton = QPushButton('Submit', self)
        self.qbutton.setFont(myfont)

        self.qline_edit.move(600, 380)
        self.qbutton.move(800, 375)
        self.message_label.move(600, 280)
        self.success_message_label.move(600, 480)
        self.code_label.move(755, 480)
        self.timer_label.move(500, 260)

        self.qline_edit.returnPressed.connect(self.qbutton.click)
        self.qbutton.clicked.connect(self.on_qbutton_clicked)
        self.timer.timeout.connect(self.timerTick)

        self.qbutton.resize(self.qbutton.sizeHint())
        self.setGeometry(300, 300, 1500, 800)
        self.setWindowTitle('Secure Database')
        self.show()

    def resource_path(self, relative_path):
        try:
            # PyInstaller creates a temp folder and stores path in _MEIPASS
            base_path = sys._MEIPASS
        except Exception:
            base_path = os.path.abspath(".")

        return os.path.join(base_path, relative_path)

    def updateTimerDisplay(self):
        time = "%d:%02d" % (self.time_left / 60, self.time_left % 60)
        self.update_label(self.timer_label, time)

    def startTimer(self):
        self.time_left = self.game_time
        self.updateTimerDisplay()
        self.timer.start(1000)

    def timerTick(self):
        self.time_left -= 1
        self.updateTimerDisplay()
        if self.time_left <= 0:
            self.timer.stop()
            self.game_timeout = True
            self.game_end_timeout(self.screen_updated)

    def tries_left(self):
        self.tries_remaining = self.tries_remaining - 1
        return self.tries_remaining

    def update_label(self, label, text):
        label.setText(text)
        label.adjustSize()
        QApplication.processEvents()

    def check_message(self, text):
        if len(text) >= self.long_message_length:

            # If the message is too long, cut it down
            text = text[0:9] + "..."

        return 'Entering key ({0}) to the secure database {1}'.format(
            text, self.spinner.get_next())

    def not_found_message(self):
        message = 'Key not accepted, you have {0} {1} remaining'
        if self.tries_remaining == 2:
            return message.format(self.tries_left(), 'try')
        else:
            return message.format(self.tries_left(), 'tries')

    def check_test_sleep(self, text):
        for i in self.spinner.calculate_rotations():
            self.update_label(self.message_label, self.check_message(text))
            sleep(self.spinner.speed)
        self.update_label(self.message_label, 'Done')
        self.spinner.reset()
        sleep(0.5)

    def check_text(self, text):
        self.check_test_sleep(text)
        code_text = None
        success_message_text = ''
        num_guessed = 0

        for key, value in self.secret_keys.items():
            if text == key:
                if self.secret_keys[key] == False:
                    message_text = 'Key entered'
                    code_text = 'Key accepted'
                    self.secret_keys[key] = True
                else:
                    message_text = 'Key already entered'
                    code_text = ''
                break

        if code_text is None:
            message_text = self.not_found_message()
            code_text = ''

        for key, value in self.secret_keys.items():
            if value:
                num_guessed += 1

        if num_guessed == len(self.secret_keys.keys()):
            message_text = 'Top Secret access granted. Welcome Agent 69-88'
            success_message_text = ''

            code_text = self.secret_password
            self.game_end_triumph = True

        # Game over
        if self.tries_remaining == 0 and code_text == '':
            message_text = self.game_failure_message
            self.game_end_fail = True

        return message_text, code_text, success_message_text

    def game_end_timeout(self, screen_updated):
        if not screen_updated:
            message_text = self.game_failure_message
            self.update_label(self.message_label, message_text)
        self.qline_edit.hide()
        self.code_label.hide()
        self.success_message_label.hide()
        self.qbutton.hide()
        self.timer.stop()
        self.timer_label.hide()
        self.game_end_fail = True

    def game_end_failure(self, screen_updated):
        if not screen_updated:
            message_text = self.game_failure_message
            self.update_label(self.message_label, message_text)
        self.qline_edit.hide()
        self.code_label.hide()
        self.success_message_label.hide()
        self.qbutton.hide()
        self.timer.stop()
        self.timer_label.hide()
        self.game_end_fail = True

    def game_end_success(self):
        self.qline_edit.hide()
        self.qbutton.hide()
        self.timer.stop()
        self.game_end_triumph = True

    def game_end_check(self, screen_updated):
        if self.game_end_fail:
            self.game_end_failure(screen_updated)
        if self.game_timeout:
            self.game_end_timeout(screen_updated)
        if self.game_end_triumph:
            self.game_end_success()

    @pyqtSlot()
    def on_qbutton_clicked(self):
        input_text = self.qline_edit.text()
        self.qline_edit.clear()
        self.update_label(self.code_label, '')
        message_text, code_text, success_message_text = self.check_text(
            input_text)

        # When we click the button make sure we update the messages after processing
        self.update_label(self.message_label, message_text)
        self.update_label(self.success_message_label, success_message_text)
        self.update_label(self.code_label, code_text)

        self.screen_updated = True

        # Check for game end before returning to the main screen
        if not self.game_end_fail and not self.game_end_triumph and not self.game_timeout:
            sleep(2)
            self.update_label(self.message_label, self.default_message)
            self.update_label(self.code_label, '')

            # Reset the screen status
            self.screen_updated = False
        else:
            self.screen_updated = False
            self.game_end_check(self.screen_updated)
Ejemplo n.º 60
0
class ZhuceWindow(QMainWindow, Ui_ZhuceWindow):
    def __init__(self, company_list, company_name_sum, generate_name,
                 generate_data):
        super(ZhuceWindow, self).__init__()
        self.setupUi(self)
        self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
        self.count = 60
        self.time = QTimer(self)
        self.time.setInterval(1000)

        self.url = 'https://www.tianyancha.com/'
        self.driver = webdriver.Chrome()

        self.control_init()

        self.generate_name = generate_name
        self.generate_data = generate_data
        self.company_list = company_list
        self.company_name_sum = company_name_sum

        self.auto_Bracket = None

    # 页面组件设置
    def control_init(self):
        screen = QDesktopWidget().screenGeometry()
        self.move(0, screen.height() * 0.3)

        self.tel_line.textChanged.connect(self.check_get_func)
        self.verification_line.textChanged.connect(self.check_input_func)

        self.auto_verification.toggled.connect(self.tuokuai_verification)
        self.manual_verification.toggled.connect(self.tuokuai_verification)

        self.zc_active_button.setEnabled(False)
        self.get_verification_button.setEnabled(False)
        self.verification_line.setVisible(False)
        self.verification_label.setVisible(False)

        self.get_verification_button.clicked.connect(self.check_get_button)
        self.zc_active_button.clicked.connect(self.check_active_button)

        self.zc_active_button.clicked.connect(self.check_active_button)
        self.zc_active_button.clicked.connect(self.closewin)

        self.time.timeout.connect(self.Refresh)

    # 获取验证码按钮设置
    def check_get_func(self):
        if self.tel_line.text():
            self.get_verification_button.setEnabled(True)
        else:
            self.get_verification_button.setEnabled(False)

    # 获得验证码按钮
    def check_get_button(self):
        self.get_verification_button.setEnabled(False)
        telephone = self.tel_line.text()
        self.driver.get(self.url)
        script = 'Object.defineProperty(navigator,"webdriver",{get:() => false,});'
        self.driver.execute_script(script)
        self.driver.maximize_window()
        time.sleep(1)
        self.driver.find_element_by_xpath(
            '/html/body/div[1]/div/div[1]/div[1]/div[1]/div/div[2]/div/div[5]/a'
        ).click()
        time.sleep(1)
        self.driver.find_element_by_xpath(
            '/html/body/div[9]/div[2]/div/div[2]/div/div/div[3]/div[3]/div[1]/div[1]'
        ).click()
        time.sleep(0.5)
        try:
            input_tel = self.driver.find_element_by_xpath(
                '/html/body/div[9]/div[2]/div/div[2]/div/div/div[3]/div[3]/div[3]/div[1]/div[1]/input'
            )
            time.sleep(1)
            input_tel.send_keys(telephone)
        except:
            input_tel = self.driver.find_element_by_xpath(
                '/html/body/div[10]/div[2]/div/div[2]/div/div/div[3]/div[3]/div[3]/div[1]/div[1]/input'
            )
            time.sleep(1)
            input_tel.send_keys(input_tel)
        time.sleep(1)
        print('f**k!')
        try:
            self.driver.find_element_by_xpath(
                '/html/body/div[9]/div[2]/div/div[2]/div/div/div[3]/div[3]/div[3]/div[2]/div[1]'
            ).click()
        except:
            self.driver.find_element_by_xpath(
                '/html/body/div[10]/div[2]/div/div[2]/div/div/div[3]/div[3]/div[3]/div[2]/div[1]'
            ).click()
        self.toukuai_choose_run()
        try:
            self.driver.find_element_by_class_name('input-group-btn.-disabled')
            self.time.start()
            self.verification_label.setVisible(True)
            self.verification_line.setVisible(True)
        except:
            self.close()

    # 完整背景图
    def before_deal_image(self):
        time.sleep(1)
        self.location = self.driver.find_element_by_class_name(
            'gt_box_holder').location
        self.size = self.driver.find_element_by_class_name(
            'gt_box_holder').size
        time.sleep(1)
        self.driver.save_screenshot('capture1.png')
        self.left = self.location['x']
        self.top = self.location['y']
        self.right = self.location['x'] + self.size['width']
        self.bottom = self.location['y'] + self.size['height']
        self.im = Image.open('capture1.png')
        self.im = self.im.crop((self.left, self.top, self.right, self.bottom))
        self.im.save('ele_capture1.png')
        print("完整背景截取成功------")

    # 缺口背景图
    def after_deal_image(self):
        time.sleep(1)
        slider = self.driver.find_element_by_xpath(
            '/html/body/div[10]/div[2]/div[2]/div[2]/div[2]')
        ActionChains(self.driver).click_and_hold(slider).perform()
        ActionChains(self.driver).move_by_offset(xoffset=198,
                                                 yoffset=0).perform()
        time.sleep(1)
        self.driver.save_screenshot('capture2.png')
        ActionChains(self.driver).release().perform()
        time.sleep(1)
        self.left = self.location['x']
        self.top = self.location['y']
        self.right = self.location['x'] + self.size['width']
        self.bottom = self.location['y'] + self.size['height']

        self.im = Image.open('capture2.png')
        self.im = self.im.crop((self.left, self.top, self.right, self.bottom))
        self.im.save('ele_capture2.png')
        print("缺口背景截取成功------")
        time.sleep(1)

    # 获得滑动距离
    def slide_distance(self, image1, image2):
        cut_image = Image.open(image2)
        full_image = Image.open(image1)
        threshold = 85
        for i in range(cut_image.size[0]):
            for j in range(cut_image.size[1]):
                pixel1 = cut_image.load()[i, j]
                pixel2 = full_image.load()[i, j]
                res_R = abs(pixel1[0] - pixel2[0])
                res_G = abs(pixel1[1] - pixel2[1])
                res_B = abs(pixel1[2] - pixel2[2])
                if res_R >= threshold and res_G >= threshold and res_B >= threshold:
                    print("缺口相对所截取图片位置", i)
                    num = i - 7 - 14 - 2 - 2
                    num = int(num)
                    return num

    # 拖动托块
    def get_track_move(self, distance):
        track = []
        current = 0
        mid = distance * 3 / 4
        t = random.uniform(0.5, 0.8)
        v = 0
        while current <= distance:
            if current <= mid:
                a = 2
            else:
                a = -3
            v0 = v
            v = v0 + a * t
            move = v0 * t + 1 / 2 * a * t * t
            current += move
            track.append(round(move))
        slider = self.driver.find_element_by_class_name(
            "gt_slider_knob.gt_show")
        ActionChains(self.driver).click_and_hold(slider).perform()
        time.sleep(0.5)
        track += [5, -7, 3, -1]
        print('移动轨迹:', track)
        # time.sleep(0.5)
        for i in track:
            ActionChains(self.driver).move_by_offset(xoffset=i,
                                                     yoffset=0).perform()
            a = random.uniform(0.1, 0.8)
            time.sleep(a)
        ActionChains(self.driver).release(slider).perform()

    # 循环尝试拖块部分
    def for_tuokuai(self):
        print('开始重试。。。')
        for i in range(5):
            try:
                time.sleep(1)
                self.driver.find_element_by_class_name(
                    'gt_refresh_button').click()
                time.sleep(1)
                self.driver.find_element_by_class_name(
                    'gt_popup_cross').click()
                time.sleep(0.5)
                self.driver.find_element_by_xpath(
                    '/html/body/div[9]/div[2]/div/div[2]/div/div/div[3]/div[3]/div[3]/div[2]/div[1]'
                ).click()
                time.sleep(1)
                self.before_deal_image()
                time.sleep(1)
                self.after_deal_image()
                time.sleep(1)
                self.track = self.slide_distance('ele_capture1.png',
                                                 'ele_capture2.png')
                print('按钮移动距离:', self.track)
                self.get_track_move(self.track)
                time.sleep(1)
                self.driver.find_element_by_xpath(
                    '/html/body/div[9]/div[2]/div/div[2]/div/div/div[3]/div[3]/div[3]/div[4]'
                )
                self.j += 1
                return self.track
            except:
                pass
            if self.j > 0:
                break
        self.driver.close()
        self.error_func()

    # 验证码刷新
    def Refresh(self):
        self.time.start()
        if self.count >= 0:
            self.get_verification_button.setText(str(self.count) + '秒')
            self.count -= 1
        else:
            self.time.stop()
            self.get_verification_button.setEnabled(True)
            self.get_verification_button.setText('重新获取验证码')
            self.count = 60

    # 输入框设置
    def check_input_func(self):
        if self.verification_line.text():
            self.zc_active_button.setEnabled(True)
        else:
            self.zc_active_button.setEnabled(False)

    # 托块验证设置
    def tuokuai_verification(self):
        radiobutton = self.sender()
        if radiobutton.isChecked():
            if radiobutton == self.auto_verification:
                self.auto_Bracket = True
            else:
                self.auto_Bracket = False
        else:
            if radiobutton == self.auto_verification:
                self.auto_Bracket = True
            else:
                self.auto_Bracket = False

    # 对托块验证的选择运行
    def toukuai_choose_run(self):
        if self.auto_Bracket == True:
            self.before_deal_image()
            self.after_deal_image()
            time.sleep(1)
            self.track = self.slide_distance('ele_capture1.png',
                                             'ele_capture2.png')
            print('按钮移动距离:', self.track)
            self.get_track_move(self.track)
            try:
                self.driver.find_element_by_class_name('home-main-search')
                print('登录成功!')
                return self.track
            except:
                print('拖块存在问题,正在重试...')
                self.for_tuokuai()
        else:
            self.driver.implicitly_wait(10)
            try:
                self.driver.find_element_by_xpath(
                    '/html/body/div[9]/div[2]/div/div[2]/div/div/div[3]/div[3]/div[3]/div[4]'
                )
            except:
                self.driver.find_element_by_xpath(
                    '/html/body/div[10]/div[2]/div[2]/div[2]/div[2]')
                reply = QMessageBox.question(self, '提示', '请手动进行托块验证!',
                                             QMessageBox.Yes)
                if reply == QMessageBox.Yes:
                    self.driver.implicitly_wait(10)
                self.driver.find_element_by_xpath(
                    '/html/body/div[9]/div[2]/div/div[2]/div/div/div[3]/div[3]/div[3]/div[4]'
                )

    # 注册按钮操作
    def check_active_button(self):
        verification = self.verification_line.text()
        input_verification = self.driver.find_element_by_xpath(
            '/html/body/div[9]/div[2]/div/div[2]/div/div/div[3]/div[3]/div[3]/div[2]/input'
        )
        time.sleep(1)
        input_verification.send_keys(verification)
        try:
            self.driver.find_element_by_xpath(
                '/html/body/div[9]/div[2]/div/div[2]/div/div/div[3]/div[3]/div[3]/div[4]'
            ).click()
            self.Ui_processwindow = ProcessWindow(self.company_list,
                                                  self.company_name_sum,
                                                  self.driver,
                                                  self.generate_name,
                                                  self.generate_data)
            self.Ui_processwindow.show()
        except:
            self.close()

    def closewin(self):
        self.close()