Exemplo n.º 1
0
class myScrollBar(QScrollBar):
    def __init__(self, color=Qt.white, parent=None):
        QScrollBar.__init__(self, parent)
        self._color = color
        # self.setAttribute(Qt.WA_TranslucentBackground)
        self.timer = QTimer()
        self.timer.setInterval(500)
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(lambda: self.parent().hideWidget(self))
        self.valueChanged.connect(lambda v: self.timer.start())
        self.valueChanged.connect(lambda: self.parent().showWidget(self))

    def setColor(self, color):
        self._color = color

    def paintEvent(self, event):
        opt = QStyleOptionSlider()
        self.initStyleOption(opt)
        style = qApp.style()
        painter = QPainter(self)

        # Background (Necessary with Qt 5.2 it seems, not with 5.4)
        # painter.save()
        # painter.setPen(Qt.NoPen)
        # painter.setBrush(self.palette().brush(QPalette.Base))
        # painter.drawRect(event.rect())
        # painter.restore()

        # slider
        r = style.subControlRect(style.CC_ScrollBar, opt, style.SC_ScrollBarSlider)
        painter.fillRect(r, self._color)
        painter.end()
Exemplo n.º 2
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)
Exemplo n.º 3
0
 def register_timer(self, time_delta, callback):
     """Registers a callback function to be run after time_delta ms."""
     timer = QTimer(self.window)
     timer.setSingleShot(True)
     timer.timeout.connect(callback)
     timer.setInterval(time_delta)
     timer.start()
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)
Exemplo n.º 5
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)
Exemplo n.º 6
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()
Exemplo n.º 7
0
    def initUI(self):
        self.setGeometry(300, 100, 400, 200)

        self.clock = QtWidgets.QLCDNumber(self)
        self.clock.setDigitCount(8)

        self.setWindowIcon(QtGui.QIcon('clock-icon.png'))

        tray_icon = SystemTrayIcon(QtGui.QIcon('clock-icon.png'), self)
        tray_icon.show()

        self.progress_bar = QtWidgets.QProgressBar(self)

        timer = QTimer(self)
        timer.timeout.connect(self.update_time)
        timer.setInterval(100)
        timer.start()

        self.btn = QtWidgets.QPushButton("Copy")
        self.btn.clicked.connect(self.copy_files)

        self.status_bar = QtWidgets.QStatusBar(self)

        grid = QtWidgets.QGridLayout()
        grid.addWidget(self.clock, 0, 0)
        grid.addWidget(self.progress_bar, 1, 0)
        grid.addWidget(self.btn, 2, 0)

        self.setLayout(grid)
        self.show()
Exemplo n.º 8
0
    def test_simulator_graphics_view(self):
        self.__setup_project()
        self.add_all_signals_to_simulator()
        stc = self.form.simulator_tab_controller  # type: SimulatorTabController
        self.assertGreater(len(stc.simulator_config.get_all_items()), 0)

        self.assertEqual(len(stc.simulator_scene.selectedItems()), 0)

        # select first message
        messages = stc.simulator_scene.get_all_message_items()
        pos = stc.ui.gvSimulator.mapFromScene(messages[0].scenePos())

        QTest.mouseClick(stc.ui.gvSimulator.viewport(), Qt.LeftButton, Qt.NoModifier, pos)

        self.assertEqual(len(stc.simulator_scene.selectedItems()), 1)
        self.assertIsInstance(stc.simulator_scene.selectedItems()[0], MessageItem)

        rules = [item for item in stc.simulator_scene.items() if isinstance(item, RuleItem)]
        self.assertEqual(len(rules), 0)
        self.menus_to_ignore = [w for w in QApplication.topLevelWidgets() if isinstance(w, QMenu)]
        timer = QTimer(self.form)
        timer.setInterval(1)
        timer.setSingleShot(True)
        timer.timeout.connect(self.__on_context_menu_simulator_graphics_view_timer_timeout)
        timer.start()

        stc.ui.gvSimulator.contextMenuEvent(QContextMenuEvent(QContextMenuEvent.Mouse, pos))

        rules = [item for item in stc.simulator_scene.items() if isinstance(item, RuleItem)]
        self.assertEqual(len(rules), 1)
Exemplo n.º 9
0
class MemoryInfo(QObject):
    def __init__(self, parent=None):
        super(MemoryInfo, self).__init__(parent)

        self.__usage = 0
        self.load_usage()
        self.__timer = QTimer(self)
        self.__timer.setInterval(1000)
        self.__timer.setSingleShot(False)
        self.__timer.timeout.connect(self.load_usage)
        self.__timer.start()

    def load_usage(self):
        with open("/proc/self/status") as f:
            data = f.read()
        index = data.index("VmRSS:")
        split = data[index:].split(None, 3)
        self.__usage = int(split[1])
        self.usage_changed.emit()

    usage_changed = pyqtSignal(name="usageChanged")

    @pyqtProperty(int, notify=usage_changed)
    def usage(self):
        return self.__usage

    @usage.setter
    def usage(self, usage):
        if self.__usage != usage:
            self.__usage = usage
            self.usage_changed.emit()
Exemplo n.º 10
0
class SC_QtGUIModule(sc_module.SCModule):
    def __init__(self, sc_state):
        super(SC_QtGUIModule, self).__init__(sc_state, "qt_gui", "qt_gui module")
        self.__app = QApplication([])
        
        self.mapWidget = MapWidget()
        self.mapWidget.show()

        self.__dashboardDialog = DashboardDialog(self.sc_state)
        self.__dashboardDialog.show()

        #periodic updates...
        self.__updater = QTimer()
        self.__updater.setInterval(500)
        self.__updater.timeout.connect(self.time_to_update)
        self.__updater.start();

        #more frequent updates...
        self.__updaterFrequent = QTimer()
        self.__updaterFrequent.setInterval(40)
        self.__updaterFrequent.timeout.connect(self.time_to_update_frequent)
        self.__updaterFrequent.start();

        #zoom to default location:
        self.mapWidget.zoomTo(16, 35.716888, -120.7646408)

        #slots
        self.mapWidget.getView().just_selected_uav.connect(self.on_uav_select)

    def start_app(self):
        sys.exit(self.__app.exec_())

    def time_to_update_frequent(self):
        #update dashboard and map:
        self.__dashboardDialog.update_uav_states()

        #update icons on map
        for id, uav_state in self.sc_state.swarm_state.uav_states.items():
            self.mapWidget.updateIcon(id, uav_state)

    def time_to_update(self):
        #check for new textures for the map:
        self.mapWidget.checkForNewTextures()

    def unload(self):
        #THIS NEEDS TO WORK (IT DOES CURRENTLY)
        #The closeEvent handler of the dialog is what saves config:
        self.__dashboardDialog.close()
        
        #Doesn't work -- dunno why:
        #self.__mapWidget.close()

        #do any cleanup here
        self.mapWidget.done(0)
        self.__dashboardDialog.done(0)
        
        QApplication.quit()

    def on_uav_select(self, id):
        self.__dashboardDialog.selectUAV(id)
Exemplo n.º 11
0
 def __init_log_writer(self):
     timer = QTimer()
     timer.setSingleShot(False)
     timer.setInterval(self.__app_log_write_interval)
     timer.timeout.connect(self.__write_app_log)
     timer.start()
     return timer
Exemplo n.º 12
0
Arquivo: gost.py Projeto: 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()
Exemplo n.º 13
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)
Exemplo n.º 14
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()
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()
Exemplo n.º 16
0
class EconomyFrame(Frame):

    def __init__(self, map_limits, queue, shutdown):

        Frame.__init__(self)

        self.circle = dict()

        self.map_limits = map_limits

        self.map_type_of_agent = Manager().dict()

        self.agent_type_color_map = \
            {0: "black",
             1: "blue",
             2: "green"}
        self.information_getter = InformationGetter(self.map_type_of_agent, queue, shutdown)
        self.information_getter.start()

        self.timer = QTimer()
        self.timer.setInterval(0)
        self.timer.timeout.connect(self.repaint)
        self.timer.start()

    def draw(self):

        map_type_of_agent = self.map_type_of_agent.copy()
        
        for position, to_print in map_type_of_agent.items():
                
            rectangle = QRect(position[0]*self.circle["width"],
                              position[1]*self.circle["height"],
                              self.circle["width"],
                              self.circle["height"])

            agent_type = to_print[0]
            agent_object = to_print[1]

            set_angle = 0
            size = 180*16

            self.painter.setBrush(self.brush[self.agent_type_color_map[agent_type]])
            self.painter.drawPie(rectangle, set_angle, size)

            set_angle = 180 * 16
            size = 180 * 16
            self.painter.setBrush(self.brush[self.agent_type_color_map[agent_object]])
            self.painter.drawPie(rectangle, set_angle, size)

            # self.painter.drawEllipse(rectangle)

    def adapt_size_to_window(self, window_size):

        for i in ["width", "height"]:
            self.circle[i] = window_size[i] / float(self.map_limits[i])
            
        self.repaint()
Exemplo n.º 17
0
Arquivo: qt.py Projeto: P4ncake/weboob
    def schedule(self, interval, function, *args):
        timer = QTimer()
        timer.setInterval(interval * 1000)
        timer.setSingleShot(True)

        self.params[timer] = (None, function, args)

        timer.timeout.connect(self.timeout)
        timer.start()
Exemplo n.º 18
0
class ProtoObj(object):
    def __init__(self, posX=0, posY=0, radX=30, radY=30, parent=None):
        super().__init__()
        self.posX = posX
        self.posY = posY
        self.radX = radX
        self.radY = radY
        self.parent = parent
        self.ellipse = QGraphicsEllipseItem(posX, posY, radX, radY)
        self.sheet = QPixmap('linkEdit.png')
        self.stand = []
        self.step = 0
        self.state = 0
        self.animation_timer = QTimer()
        self.animation_timer.timeout.connect(self.animate)
        self.animation_timer.setInterval(1000)
        self.animation_timer.start()

    def initObj(self):
        self.ellipse.setPos(self.posX, self.posY)
        self.ellipse.setPen(QPen(Qt.transparent, 1))
        self.ellipse.setBrush(QBrush(Qt.darkGreen))
        self.ellipse.setZValue(0)
        self.ellipse.setOpacity(1)
        effect = QGraphicsDropShadowEffect(self.parent)
        effect.setBlurRadius(15)
        effect.setColor(Qt.black)
        self.ellipse.setGraphicsEffect(effect)
        self.stand.append(self.sheet.copy(10, 15, 100, 120))
        self.stand.append(self.sheet.copy(130, 15, 100, 120))
        self.stand.append(self.sheet.copy(250, 15, 100, 120))
        self.pix = self.parent.m_scene.addPixmap(self.stand[0])
        self.pix.setPos(self.posX, self.posY)
        self.pix.setOffset(-20, -56)
        self.pix.setZValue(2)
        self.pix.setScale(1)

    def getObj(self):
        return [self.ellipse]

    def moveObj(self, velX, velY):
        self.posX += velX
        self.posY += velY
        self.pix.setPos(self.posX, self.posY)

    def animate(self):
        if self.state == 0:
            self.step += 1
            if self.step > 2:
                self.step = 0
            'standing'
            #self.pix = self.parent.m_scene.addPixmap(self.stand[self.step])
            self.pix.setPixmap(self.stand[self.step])
            self.pix.setPos(self.posX, self.posY)
            #self.pix.setOffset(-20, -70)
            self.pix.setZValue(2)
Exemplo n.º 19
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()
Exemplo n.º 20
0
class MultiBuildPlateModel(ListModel):

    maxBuildPlateChanged = pyqtSignal()
    activeBuildPlateChanged = pyqtSignal()
    selectionChanged = pyqtSignal()

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

        self._update_timer = QTimer()
        self._update_timer.setInterval(100)
        self._update_timer.setSingleShot(True)
        self._update_timer.timeout.connect(self._updateSelectedObjectBuildPlateNumbers)

        self._application = Application.getInstance()
        self._application.getController().getScene().sceneChanged.connect(self._updateSelectedObjectBuildPlateNumbersDelayed)
        Selection.selectionChanged.connect(self._updateSelectedObjectBuildPlateNumbers)

        self._max_build_plate = 1  # default
        self._active_build_plate = -1

    def setMaxBuildPlate(self, max_build_plate):
        if self._max_build_plate != max_build_plate:
            self._max_build_plate = max_build_plate
            self.maxBuildPlateChanged.emit()

    ##  Return the highest build plate number
    @pyqtProperty(int, notify = maxBuildPlateChanged)
    def maxBuildPlate(self):
        return self._max_build_plate

    def setActiveBuildPlate(self, nr):
        if self._active_build_plate != nr:
            self._active_build_plate = nr
            self.activeBuildPlateChanged.emit()

    @pyqtProperty(int, notify = activeBuildPlateChanged)
    def activeBuildPlate(self):
        return self._active_build_plate

    def _updateSelectedObjectBuildPlateNumbersDelayed(self, *args):
        if not isinstance(args[0], Camera):
            self._update_timer.start()

    def _updateSelectedObjectBuildPlateNumbers(self, *args):
        result = set()
        for node in Selection.getAllSelectedObjects():
            result.add(node.callDecoration("getBuildPlateNumber"))
        self._selection_build_plates = list(result)
        self.selectionChanged.emit()

    @pyqtProperty("QVariantList", notify = selectionChanged)
    def selectionBuildPlates(self):
        return self._selection_build_plates
Exemplo n.º 21
0
def collapse(callback):
    timer = QTimer()
    timer.setSingleShot(True)
    timer.setInterval(200)
    def out(*pargs, **kwargs):
        out.pargs = pargs
        out.kwargs = kwargs
        timer.start()
    out.stop = lambda: timer.stop()
    timer.timeout.connect(lambda: callback(*out.pargs, **out.kwargs))
    return out
Exemplo n.º 22
0
 def _handle_reply(self, reply):
     """Handle a new QNetworkReply."""
     if reply.isFinished():
         self.on_reply_finished(reply)
     else:
         timer = QTimer(self)
         timer.setInterval(10000)
         timer.timeout.connect(reply.abort)
         timer.start()
         self._timers[reply] = timer
         reply.finished.connect(functools.partial(
             self.on_reply_finished, reply))
Exemplo n.º 23
0
class ArbiterGUI(ArbiterModule):
    def __init__(self, state):
        super(ArbiterGUI, self).__init__(state)
        self.__app = QApplication([])

        self.map_win = MapWidget()

        self.blue_swarm = ACS_SwarmState()
        self.red_swarm = ACS_SwarmState()

        self.dialog = ArbiterDialog(self.blue_swarm, self.red_swarm)

        #periodic updates...
        self.__updater = QTimer()
        self.__updater.setInterval(500)
        self.__updater.timeout.connect(self.update)
        self.__updater.start();

        #more frequent updates...
        self.__updaterFrequent = QTimer()
        self.__updaterFrequent.setInterval(40)
        self.__updaterFrequent.timeout.connect(self.update_frequent)
        self.__updaterFrequent.start();


    def on_blue_message_received(self, msg):
        self.blue_swarm.process_msg(msg)
        self.dialog.process_msg(ArbiterDialog.BLUE, msg)

    def on_red_message_received(self, msg):
        self.red_swarm.process_msg(msg)
        self.dialog.process_msg(ArbiterDialog.RED, msg)


    def update(self):
        #check for new textures for the map:
        self.map_win.checkForNewTextures()


    def update_frequent(self):
        #update red and blue icons on map
        for id, uav_state in self.blue_swarm.uav_states.items():
            self.map_win.updateIcon(id, uav_state)
        for id, uav_state in self.red_swarm.uav_states.items():
            self.map_win.updateIcon(id, uav_state, 1)
        self.dialog.update_summaries()


    def start_gui(self):
        self.map_win.show()
        self.map_win.zoomTo(16, 35.716888, -120.7646408)
        self.dialog.show()
        self.__app.exec_()
Exemplo n.º 24
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
Exemplo n.º 25
0
    def __init__(self, stickMan, keyReceiver):
        self.m_stickMan = stickMan
        self.m_keyReceiver = keyReceiver

        # Create animation group to be used for all transitions.
        self.m_animationGroup = QParallelAnimationGroup()
        stickManNodeCount = self.m_stickMan.nodeCount()
        self._pas = []
        for i in range(stickManNodeCount):
            pa = QPropertyAnimation(self.m_stickMan.node(i), b'pos')
            self._pas.append(pa)
            self.m_animationGroup.addAnimation(pa)

        # Set up intial state graph.
        self.m_machine = QStateMachine()
        self.m_machine.addDefaultAnimation(self.m_animationGroup)

        self.m_alive = QState(self.m_machine)
        self.m_alive.setObjectName('alive')

        # Make it blink when lightning strikes before entering dead animation.
        lightningBlink = QState(self.m_machine)
        lightningBlink.assignProperty(self.m_stickMan.scene(),
                'backgroundBrush', Qt.white)
        lightningBlink.assignProperty(self.m_stickMan, 'penColor', Qt.black)
        lightningBlink.assignProperty(self.m_stickMan, 'fillColor', Qt.white)
        lightningBlink.assignProperty(self.m_stickMan, 'isDead', True)

        timer = QTimer(lightningBlink)
        timer.setSingleShot(True)
        timer.setInterval(100)
        lightningBlink.entered.connect(timer.start)
        lightningBlink.exited.connect(timer.stop)

        self.m_dead = QState(self.m_machine)
        self.m_dead.assignProperty(self.m_stickMan.scene(), 'backgroundBrush',
                Qt.black)
        self.m_dead.assignProperty(self.m_stickMan, 'penColor', Qt.white)
        self.m_dead.assignProperty(self.m_stickMan, 'fillColor', Qt.black)
        self.m_dead.setObjectName('dead')

        # Idle state (sets no properties).
        self.m_idle = QState(self.m_alive)
        self.m_idle.setObjectName('idle')

        self.m_alive.setInitialState(self.m_idle)

        # Lightning strikes at random.
        self.m_alive.addTransition(LightningStrikesTransition(lightningBlink))
        lightningBlink.addTransition(timer.timeout, self.m_dead)

        self.m_machine.setInitialState(self.m_alive)
Exemplo n.º 26
0
    def enable_asyncore_loop(self):
        """Hooks our asyncore loop into Qt's event queue."""
        def beat():
            asyncore.loop(count=1, timeout=0)

        # Yep, this isn't especially real-time IO, but it's fine for what we do.
        timer = QTimer()
        timer.timeout.connect(beat)
        timer.setSingleShot(False)
        timer.setInterval(15)
        timer.start()

        self._timer = timer
Exemplo n.º 27
0
class ShapeWidget(QWidget):  
	def __init__(self,parent=None):  
		super(ShapeWidget,self).__init__(parent)
		self.i = 1
		self.mypix()
		self.timer = QTimer()
		self.timer.setInterval(500)  # 500毫秒
		self.timer.timeout.connect(self.timeChange)   
		self.timer.start()

    # 显示不规则 pic
	def mypix(self):
		self.update()
		if self.i == 5:
			self.i = 1
		self.mypic = {1: './images/left.png', 2: "./images/up.png", 3: './images/right.png', 4: './images/down.png'}
		self.pix = QPixmap(self.mypic[self.i], "0", Qt.AvoidDither | Qt.ThresholdDither | Qt.ThresholdAlphaDither)   
		self.resize(self.pix.size())
		self.setMask(self.pix.mask())  
		self.dragPosition = None

	def mousePressEvent(self, event):
		if event.button() == Qt.LeftButton:
			self.m_drag=True
			self.m_DragPosition=event.globalPos()-self.pos()
			event.accept()
			self.setCursor(QCursor(Qt.OpenHandCursor))

	def mouseMoveEvent(self, QMouseEvent):
		if Qt.LeftButton and self.m_drag:
			self.move(QMouseEvent.globalPos()- self.m_DragPosition )
			QMouseEvent.accept()
	
	def mouseReleaseEvent(self, QMouseEvent):
		self.m_drag=False
		self.setCursor(QCursor(Qt.ArrowCursor))
        
	def paintEvent(self, event):
		painter = QPainter(self)
		painter.drawPixmap(0, 0, self.pix.width(),self.pix.height(),self.pix)
    
	# 鼠标双击事件
	def mouseDoubleClickEvent(self, event):
		if event.button() == 1:
			self.i += 1
			self.mypix()

    # 每500毫秒修改paint
	def timeChange(self):
		self.i += 1
		self.mypix()
Exemplo n.º 28
0
class MyGlWidget(QGLWidget):
    "PySideApp uses Qt library to create an opengl context, listen to keyboard events, and clean up"

    def __init__(self, renderer, glformat, app):
        "Creates an OpenGL context and a window, and acquires OpenGL resources"
        super(MyGlWidget, self).__init__(glformat)
        self.renderer = renderer
        self.app = app
        # Use a timer to rerender as fast as possible
        self.timer = QTimer(self)
        self.timer.setSingleShot(True)
        self.timer.setInterval(0)
        self.timer.timeout.connect(self.render_vr)
        # Accept keyboard events
        self.setFocusPolicy(Qt.StrongFocus)

    def __enter__(self):
        "setup for RAII using 'with' keyword"
        return self

    def __exit__(self, type_arg, value, traceback):
        "cleanup for RAII using 'with' keyword"
        self.dispose_gl()

    def initializeGL(self):
        if self.renderer is not None:
            self.renderer.init_gl()
        self.timer.start()

    def paintGL(self):
        "render scene one time"
        self.renderer.render_scene()
        self.swapBuffers() # Seems OK even in single-buffer mode
        
    def render_vr(self):
        self.makeCurrent()
        self.paintGL()
        self.doneCurrent()
        self.timer.start() # render again real soon now

    def disposeGL(self):
        if self.renderer is not None:
            self.makeCurrent()
            self.renderer.dispose_gl()
            self.doneCurrent()

    def keyPressEvent(self, event):
        "press ESCAPE to quit the application"
        key = event.key()
        if key == Qt.Key_Escape:
            self.app.quit()
Exemplo n.º 29
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()
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
Exemplo n.º 31
0
class ExtrudersModel(UM.Qt.ListModel.ListModel):
    # The ID of the container stack for the extruder.
    IdRole = Qt.UserRole + 1

    ##  Human-readable name of the extruder.
    NameRole = Qt.UserRole + 2
    ##  Is the extruder enabled?
    EnabledRole = Qt.UserRole + 9

    ##  Colour of the material loaded in the extruder.
    ColorRole = Qt.UserRole + 3

    ##  Index of the extruder, which is also the value of the setting itself.
    #
    #   An index of 0 indicates the first extruder, an index of 1 the second
    #   one, and so on. This is the value that will be saved in instance
    #   containers.
    IndexRole = Qt.UserRole + 4

    # The ID of the definition of the extruder.
    DefinitionRole = Qt.UserRole + 5

    # The material of the extruder.
    MaterialRole = Qt.UserRole + 6

    # The variant of the extruder.
    VariantRole = Qt.UserRole + 7
    StackRole = Qt.UserRole + 8

    ##  List of colours to display if there is no material or the material has no known
    #   colour.
    defaultColors = [
        "#ffc924", "#86ec21", "#22eeee", "#245bff", "#9124ff", "#ff24c8"
    ]

    ##  Initialises the extruders model, defining the roles and listening for
    #   changes in the data.
    #
    #   \param parent Parent QtObject of this list.
    def __init__(self, parent=None):
        super().__init__(parent)

        self.addRoleName(self.IdRole, "id")
        self.addRoleName(self.NameRole, "name")
        self.addRoleName(self.EnabledRole, "enabled")
        self.addRoleName(self.ColorRole, "color")
        self.addRoleName(self.IndexRole, "index")
        self.addRoleName(self.DefinitionRole, "definition")
        self.addRoleName(self.MaterialRole, "material")
        self.addRoleName(self.VariantRole, "variant")
        self.addRoleName(self.StackRole, "stack")

        self._update_extruder_timer = QTimer()
        self._update_extruder_timer.setInterval(100)
        self._update_extruder_timer.setSingleShot(True)
        self._update_extruder_timer.timeout.connect(self.__updateExtruders)

        self._simple_names = False

        self._active_machine_extruders = []  # type: Iterable[ExtruderStack]
        self._add_optional_extruder = False

        # Listen to changes
        Application.getInstance().globalContainerStackChanged.connect(
            self._extrudersChanged
        )  # When the machine is swapped we must update the active machine extruders
        Application.getInstance(
        ).getExtruderManager().extrudersChanged.connect(
            self._extrudersChanged
        )  # When the extruders change we must link to the stack-changed signal of the new extruder
        Application.getInstance(
        ).getContainerRegistry().containerMetaDataChanged.connect(
            self._onExtruderStackContainersChanged
        )  # When meta data from a material container changes we must update
        self._extrudersChanged()  # Also calls _updateExtruders

    addOptionalExtruderChanged = pyqtSignal()

    def setAddOptionalExtruder(self, add_optional_extruder):
        if add_optional_extruder != self._add_optional_extruder:
            self._add_optional_extruder = add_optional_extruder
            self.addOptionalExtruderChanged.emit()
            self._updateExtruders()

    @pyqtProperty(bool,
                  fset=setAddOptionalExtruder,
                  notify=addOptionalExtruderChanged)
    def addOptionalExtruder(self):
        return self._add_optional_extruder

    ##  Set the simpleNames property.
    def setSimpleNames(self, simple_names):
        if simple_names != self._simple_names:
            self._simple_names = simple_names
            self.simpleNamesChanged.emit()
            self._updateExtruders()

    ##  Emitted when the simpleNames property changes.
    simpleNamesChanged = pyqtSignal()

    ##  Whether or not the model should show all definitions regardless of visibility.
    @pyqtProperty(bool, fset=setSimpleNames, notify=simpleNamesChanged)
    def simpleNames(self):
        return self._simple_names

    ##  Links to the stack-changed signal of the new extruders when an extruder
    #   is swapped out or added in the current machine.
    #
    #   \param machine_id The machine for which the extruders changed. This is
    #   filled by the ExtruderManager.extrudersChanged signal when coming from
    #   that signal. Application.globalContainerStackChanged doesn't fill this
    #   signal; it's assumed to be the current printer in that case.
    def _extrudersChanged(self, machine_id=None):
        if machine_id is not None:
            if Application.getInstance().getGlobalContainerStack() is None:
                # No machine, don't need to update the current machine's extruders
                return
            if machine_id != Application.getInstance().getGlobalContainerStack(
            ).getId():
                # Not the current machine
                return

        # Unlink from old extruders
        for extruder in self._active_machine_extruders:
            extruder.containersChanged.disconnect(
                self._onExtruderStackContainersChanged)

        # Link to new extruders
        self._active_machine_extruders = []
        extruder_manager = Application.getInstance().getExtruderManager()
        for extruder in extruder_manager.getActiveExtruderStacks():
            if extruder is None:  #This extruder wasn't loaded yet. This happens asynchronously while this model is constructed from QML.
                continue
            extruder.containersChanged.connect(
                self._onExtruderStackContainersChanged)
            self._active_machine_extruders.append(extruder)

        self._updateExtruders(
        )  # Since the new extruders may have different properties, update our own model.

    def _onExtruderStackContainersChanged(self, container):
        # Update when there is an empty container or material change
        if container.getMetaDataEntry(
                "type"
        ) == "material" or container.getMetaDataEntry("type") is None:
            # The ExtrudersModel needs to be updated when the material-name or -color changes, because the user identifies extruders by material-name
            self._updateExtruders()

    modelChanged = pyqtSignal()

    def _updateExtruders(self):
        self._update_extruder_timer.start()

    ##  Update the list of extruders.
    #
    #   This should be called whenever the list of extruders changes.
    @UM.FlameProfiler.profile
    def __updateExtruders(self):
        extruders_changed = False

        if self.rowCount() != 0:
            extruders_changed = True

        items = []

        global_container_stack = Application.getInstance(
        ).getGlobalContainerStack()
        if global_container_stack:

            # get machine extruder count for verification
            machine_extruder_count = global_container_stack.getProperty(
                "machine_extruder_count", "value")

            for extruder in Application.getInstance().getExtruderManager(
            ).getActiveExtruderStacks():
                position = extruder.getMetaDataEntry(
                    "position", default="0")  # Get the position
                try:
                    position = int(position)
                except ValueError:
                    # Not a proper int.
                    position = -1
                if position >= machine_extruder_count:
                    continue

                default_color = self.defaultColors[
                    position] if 0 <= position < len(
                        self.defaultColors) else self.defaultColors[0]
                color = extruder.material.getMetaDataEntry(
                    "color_code", default=default_color
                ) if extruder.material else default_color

                # construct an item with only the relevant information
                item = {
                    "id":
                    extruder.getId(),
                    "name":
                    extruder.getName(),
                    "enabled":
                    extruder.isEnabled,
                    "color":
                    color,
                    "index":
                    position,
                    "definition":
                    extruder.getBottom().getId(),
                    "material":
                    extruder.material.getName() if extruder.material else "",
                    "variant":
                    extruder.variant.getName()
                    if extruder.variant else "",  # e.g. print core
                    "stack":
                    extruder,
                }

                items.append(item)
                extruders_changed = True

        if extruders_changed:
            # sort by extruder index
            items.sort(key=lambda i: i["index"])

            # We need optional extruder to be last, so add it after we do sorting.
            # This way we can simply interpret the -1 of the index as the last item (which it now always is)
            if self._add_optional_extruder:
                item = {
                    "id": "",
                    "name": catalog.i18nc("@menuitem", "Not overridden"),
                    "enabled": True,
                    "color": "#ffffff",
                    "index": -1,
                    "definition": ""
                }
                items.append(item)

            self.setItems(items)
            self.modelChanged.emit()
Exemplo n.º 32
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 = 0.6 * (psutil.virtual_memory().free / 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()
Exemplo n.º 33
0
class Test(QMainWindow):
    def __init__(self, window_zoom=0.1):
        super().__init__()
        signal.signal(signal.SIGINT, self.exit)
        # WINDOW
        # self._screen_size = QApplication.primaryScreen().size()
        self.resize(1800, 1200)
        self._center_widget = QWidget(self)
        self._center_widget.setWindowFlags(Qt.NoDropShadowWindowHint)
        self._center_widget.resize(1800, 1200)
        self._vtk_widget = QVTKRenderWindowInteractor(self._center_widget)
        self._vtk_widget.resize(1800, 1200)
        self.setCentralWidget(self._center_widget)
        self._center_widget.setFocus()
        self._ren_win = self._vtk_widget.GetRenderWindow()
        self._interactor = self._ren_win.GetInteractor()
        QApplication.setStyle('Fusion')
        self.setWindowFlags(Qt.NoDropShadowWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        # LABEL_WIDGET
        self._label_widget = QWidget(self._center_widget)
        self._label_widget.resize(300, 200)
        # POS_LABEL
        self._pos_label = QLabel(self._label_widget)
        self._pos_label.move(20, 50)
        self._pos_label.resize(200, 30)
        self._pos_label.setStyleSheet('QLabel {color: red}')
        self._pos_label.setText('pos: ')
        # TOUCH_POINT_LABEL
        self._touch_points = 0
        self._touch_point_label = QLabel(self._label_widget)
        self._touch_point_label.move(20, 80)
        self._touch_point_label.resize(100, 30)
        self._touch_point_label.setStyleSheet('QLabel {color: red}')
        self._touch_point_label.setText('touch points: ')
        self._label_widget.setAttribute(Qt.WA_TranslucentBackground)
        self._label_widget.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
        self._label_widget.show()
        # WIDGET
        self._widget = QWidget(self._center_widget)
        self._widget.resize(1800, 1200)
        self._widget.event = types.MethodType(self._handle_event, self._widget)
        self._widget.setAttribute(Qt.WA_AcceptTouchEvents)  # 与半透明分开设置
        # # POS_LABEL
        # self._pos_label = QLabel(self._widget)
        # self._pos_label.move(1500, 50)
        # self._pos_label.resize(250, 30)
        # self._pos_label.setStyleSheet('QLabel {color: red}')
        # self._pos_label.setText('pos: ')
        # # TOUCH_POINT_LABEL
        # self._touch_points = 0
        # self._touch_point_label = QLabel(self._widget)
        # self._touch_point_label.move(1500, 80)
        # self._touch_point_label.resize(100, 30)
        # self._touch_point_label.setStyleSheet('QLabel {color: red}')
        # self._touch_point_label.setText('touch points: ')
        self._timer = QTimer()
        self._widget.setAttribute(Qt.WA_TranslucentBackground)
        self._widget.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
        self._widget.show()
        # TIMER
        self._timer.setInterval(int(1000 / 60))
        self._timer.timeout.connect(self._render_once)
        self._timer.start()
        # RENDER
        self._renderer = vtk.vtkOpenGLRenderer()
        self._renderer.SetBackground((0, 0, 0))
        self._renderer.AddActor(self._axis_actor())
        self._ren_win.AddRenderer(self._renderer)
        # CAMERA
        self._camera = self._renderer.GetActiveCamera()
        self._camera_pose = [0, 0, 13]
        self._last_camera_pose = [0, 0, 0]
        self._camera_view = [0, 0, 0]
        self._last_camera_view = [0, 0, 0]
        self.set_camera_pose(pos=[0, 0, 13.0],
                             center=[0, 0, 0],
                             view=[0, 0, 0])
        self._vtk_widget.Start()
        # MOUSE_POS_THREAD
        self._thread_switch = False
        self._mouse_pos = self._widget.mapFromGlobal(QCursor.pos())
        self._last_mouse_pos = self._widget.mapFromGlobal(QCursor.pos())
        self._thread = None

    def _get_mouse_pos(self):
        while self._thread_switch:
            self._mouse_pos = self._widget.mapFromGlobal(QCursor.pos())
            if self._last_mouse_pos != self._mouse_pos:
                move_x = self._mouse_pos.x() - self._last_mouse_pos.x()
                move_y = self._mouse_pos.y() - self._last_mouse_pos.y()
                self._last_mouse_pos = self._mouse_pos
                if move_x > 0:
                    self._camera_pose[0] += 0.05
                elif move_x < 0:
                    self._camera_pose[0] -= 0.05
            time.sleep(1 / 60)

    # @staticmethod
    def _handle_event(self, widget, event):
        if event.type() in [
                QEvent.TouchBegin, QEvent.TouchUpdate, QEvent.TouchEnd
        ]:
            touch_points = event.touchPoints()
            self._touch_points = len(touch_points)
            print(len(touch_points))
            if len(touch_points) == 1 and touch_points[0].state(
            ) == Qt.TouchPointMoved:
                pos = touch_points[0].pos()
                last_pos = touch_points[0].lastPos()
                dif_pos_x = pos.x() - last_pos.x()
                dif_pos_y = pos.y() - last_pos.y()
                if dif_pos_x > 0:
                    self._camera_pose[0] += 0.05
                elif dif_pos_x < 0:
                    self._camera_pose[0] -= 0.05
            elif len(touch_points) == 2:
                if touch_points[0].state(
                ) == Qt.TouchPointMoved or touch_points[1].state(
                ) == Qt.TouchPointMoved:
                    pos1 = touch_points[0].pos()
                    last_pos1 = touch_points[0].lastPos()
                    pos2 = touch_points[1].pos()
                    last_pos2 = touch_points[1].lastPos()
                    diff_new = pos1 - pos2
                    diff_old = last_pos1 - last_pos2
                    if diff_new.y() - diff_old.y() > 0:
                        self._camera_pose[1] -= 0.05
                    elif diff_new.y() - diff_old.y() < 0:
                        self._camera_pose[1] += 0.05
            elif len(touch_points) == 3:
                if touch_points[0].state() == Qt.TouchPointMoved or touch_points[1].state() == Qt.TouchPointMoved or \
                        touch_points[2].state() == Qt.TouchPointMoved:
                    pos1 = touch_points[0].pos()
                    last_pos1 = touch_points[0].lastPos()
                    pos2 = touch_points[1].pos()
                    last_pos2 = touch_points[1].lastPos()
                    pos3 = touch_points[2].pos()
                    last_pos3 = touch_points[2].lastPos()
                    if pos1.x() - last_pos1.x() > 0:
                        self._camera_pose[0] += 0.05
                    elif pos1.x() - last_pos1.x() < 0:
                        self._camera_pose[0] -= 0.05
                    if pos1.y() - last_pos1.y() > 0:
                        self._camera_pose[1] += 0.05
                    elif pos1.y() - last_pos1.y() < 0:
                        self._camera_pose[1] -= 0.05
            elif len(touch_points) == 4:
                pass
            elif len(touch_points) == 5:
                pass
        elif event.type() == QEvent.MouseButtonPress:
            if event.button() == Qt.LeftButton:
                self._thread_switch = True
                self._thread = threading.Thread(target=self._get_mouse_pos)
                self._thread.daemon = True
                self._thread.start()
        elif event.type() == QEvent.MouseButtonRelease:
            if event.button() == Qt.LeftButton:
                self._thread_switch = False
        widget.update()
        return True

    # CAMERA
    def set_camera_pose(self, pos, center, view):
        self._camera.SetPosition(pos)
        self._camera.SetFocalPoint(center)
        self._camera.SetViewUp(view)
        self._render_once()

    def _render_once(self):
        self._touch_point_label.setText(f'touch points: {self._touch_points}')
        for idx, pos in enumerate(self._last_camera_pose):
            if self._last_camera_pose[idx] != self._camera_pose[idx]:
                self._last_camera_pose[idx] = self._camera_pose[idx]
                self._pos_label.setText(
                    f'pos: {round(self._camera_pose[0], 2)}  {round(self._camera_pose[1], 2)}  '
                    f'{round(self._camera_pose[2], 2)}')
                self._camera.SetPosition(self._camera_pose)
                self._interactor.Render()
            if self._last_camera_view[idx] != self._camera_view[idx]:
                self._last_camera_view[idx] = self._camera_view[idx]
                self._camera.SetViewUp(self._camera_view)
                self._interactor.Render()

    def mousePressEvent(self, event):
        print('mouse pressed')

    def mouseReleaseEvent(self, event):
        print('mouse release')

    @staticmethod
    def _axis_actor():
        actor = vtk.vtkAxesActor()
        actor.SetTotalLength(2, 2, 2)
        actor.SetShaftType(0)
        actor.SetCylinderRadius(0.01)
        actor.GetXAxisCaptionActor2D().SetWidth(0.01)
        actor.GetYAxisCaptionActor2D().SetWidth(0.01)
        actor.GetZAxisCaptionActor2D().SetWidth(0.01)
        return actor

    def exit(self, signum=None, frame=None):
        self._ren_win.GetInteractor().DestroyTimer()
        self._timer.stop()
        self.close()

    def test(self):
        self._touch_points += 1
        self._pos_label.setText(f'{self._touch_points}')
Exemplo n.º 34
0
class SensirionSB(QWidget):
    # Each plot is individually notified of a new value,
    # but if one measurement fails, no signal is sent.
    # This is to prevent plot phase shift when 1 measurement was successful and another was not.
    sht85TemperatureReady = pyqtSignal(float)
    sht85HumidityReady = pyqtSignal(float)
    sht85AnalogReady = pyqtSignal(float)
    stc31ConcentrationReady = pyqtSignal(float)
    stc31AnalogReady = pyqtSignal(float)

    def __init__(self):
        super().__init__()

        self.ssbGroup = QGroupBox("Sensirion Sensorbridge control")
        self.portLabel = QLabel("Serial port: not connected")
        self.device: SensorBridgeShdlcDevice = None

        self.portOnePowerEnabled = False
        self.portOnePowerButton = QPushButton("Enable supply")
        self.portOnePowerButton.clicked.connect(self.port_one_supply_clicked)
        self.portOnePowerLabel = QLabel("Unknown state")

        self.portTwoPowerEnabled = False
        self.portTwoPowerButton = QPushButton("Enable supply")
        self.portTwoPowerButton.clicked.connect(self.port_two_supply_clicked)
        self.portTwoPowerLabel = QLabel("Unknown state")

        self.sht85device: SHT85 = None

        self.sht85PortDropdown = QComboBox()
        self.sht85PortDropdown.addItems(SensirionSensor.PORTS.keys())
        self.sht85PortDropdown.setCurrentIndex(1)
        self.sht85PortDropdown.currentTextChanged.connect(self.sht85_port_changed)

        self.sht85compensationEnabled = False
        self.sht85compensationCheckbox = QCheckBox("Compensate STC31 on measurement")
        self.sht85compensationCheckbox.clicked.connect(self.compensate_changed)

        self.sht85TemperatureLabel = QLabel("Temp: ? ℃")
        self.sht85HumidityLabel = QLabel("Humidity: ? % RH")
        self.sht85AnalogLabel = QLabel("Analog: ? V")

        self.sht85BlinkButton = QPushButton("Blink")
        self.sht85BlinkButton.clicked.connect(lambda: self.sht85device.blink())

        self.stc31device: STC31 = None

        self.stc31PortDropdown = QComboBox()
        self.stc31PortDropdown.addItems(SensirionSensor.PORTS.keys())
        self.stc31PortDropdown.setCurrentIndex(1)
        self.stc31PortDropdown.currentTextChanged.connect(self.stc31_port_changed)

        self.stc31BinaryGasDropdown = QComboBox()
        self.stc31BinaryGasDropdown.addItems(STC31.BINARY_GAS.keys())
        self.stc31BinaryGasDropdown.currentTextChanged.connect(
            lambda: self.stc31device.set_binary_gas(self.stc31BinaryGasDropdown.currentText()))

        self.stc31RelativeHumidityEdit = QDoubleSpinBox()
        self.stc31RelativeHumidityEdit.setRange(0.0, 100.0)
        self.stc31RelativeHumidityEdit.setValue(0.0)
        self.stc31RelativeHumidityEdit.setSuffix("  % RH")
        self.stc31RelativeHumidityEdit.editingFinished.connect(
            lambda: self.stc31device.set_relative_humidity(self.stc31RelativeHumidityEdit.value()))

        self.stc31TemperatureEdit = QDoubleSpinBox()
        self.stc31TemperatureEdit.setRange(-163.84, 163.835)
        self.stc31TemperatureEdit.setSuffix("  ℃")
        self.stc31TemperatureEdit.editingFinished.connect(
            lambda: self.stc31device.set_temperature(self.stc31TemperatureEdit.value()))

        self.stc31PressureEdit = QSpinBox()
        self.stc31PressureEdit.setRange(0, 65535)
        self.stc31PressureEdit.setValue(1013)
        self.stc31PressureEdit.setSuffix("  mbar")
        self.stc31PressureEdit.editingFinished.connect(
            lambda: self.stc31device.set_pressure(self.stc31PressureEdit.value()))

        self.stc31ForcedRecalibrationEdit = QSpinBox()
        self.stc31ForcedRecalibrationEdit.setRange(0, 65535)
        self.stc31ForcedRecalibrationEdit.setValue(0)
        self.stc31ForcedRecalibrationEdit.setSuffix(" % vol")
        self.stc31ForcedRecalibrationEdit.editingFinished.connect(
            lambda: self.stc31device.forced_recalibration(self.stc31ForcedRecalibrationEdit.value()))

        self.stc31GasConcentrationLabel = QLabel("Gas concentration: ? %")
        self.stc31AnalogLabel = QLabel("Analog: ? V")

        self.stc31AutoSelfCalibrationCheckbox = QCheckBox("Automatic self calibration")
        self.stc31AutoSelfCalibrationCheckbox.setChecked(False)
        self.stc31AutoSelfCalibrationCheckbox.clicked.connect(
            lambda: self.stc31device.automatic_self_calibration(self.stc31AutoSelfCalibrationCheckbox.isChecked()))

        self.stc31SelfTestButton = QPushButton("Self test")
        self.stc31SelfTestButton.clicked.connect(self.stc31_self_test)

        self.stc31SoftResetButton = QPushButton("Soft reset")
        self.stc31SoftResetButton.clicked.connect(lambda: self.stc31device.soft_reset())

        self.stc31BlinkButton = QPushButton("Blink")
        self.stc31BlinkButton.clicked.connect(lambda: self.stc31device.blink())

        self.timer = QTimer()
        self.timer.timeout.connect(self.on_timeout)

        self.intervalEdit = QLineEdit("1")
        self.intervalEdit.setValidator(QRegExpValidator(QRegExp("[0-9]*(|\\.[0-9]*)")))
        self.intervalEdit.setMaximumWidth(150)
        self.intervalEdit.editingFinished.connect(
            lambda: self.timer.setInterval(int(60 * 1000 * float(self.intervalEdit.text()))))

        self.csvFile = None
        self.savingEnabled = False
        self.savingButton = QPushButton("Start saving to file")
        self.savingButton.clicked.connect(self.saving_button_clicked)

        self.bufferSizeEdit = QLineEdit()
        self.bufferSizeEdit.setValidator(QIntValidator())
        self.bufferSizeEdit.setText("128")
        self.bufferSizeEdit.editingFinished.connect(self.update_buffer_sizes)

        self.sht85TemperaturePlotWidget = SensirionSBPlot("SHT85 temperature", (255, 32, 0), 128)
        self.sht85HumidityPlotWidget = SensirionSBPlot("SHT85 relative humidity", (0, 127, 255), 128)
        self.sht85AnalogPlotWidget = SensirionSBPlot("SHT85 analog", (255, 127, 0), 128)
        self.stc31ConcentrationPlotWidget = SensirionSBPlot("STC31 concentration", (200, 200, 200), 128)
        self.stc31AnalogPlotWidget = SensirionSBPlot("STC31 analog", (255, 127, 0), 128)

        self.sht85TemperatureReady.connect(self.sht85TemperaturePlotWidget.update_plot)
        self.sht85HumidityReady.connect(self.sht85HumidityPlotWidget.update_plot)
        self.sht85AnalogReady.connect(self.sht85AnalogPlotWidget.update_plot)
        self.stc31ConcentrationReady.connect(self.stc31ConcentrationPlotWidget.update_plot)
        self.stc31AnalogReady.connect(self.stc31AnalogPlotWidget.update_plot)

        self.setLayout(self.create_layout())

    def stc31_self_test(self):
        result = int.from_bytes(self.stc31device.self_test(), 'big')
        if result == 0x00:
            self.stc31SelfTestButton.setText("Self test: OK")
            QTimer().singleShot(5000, lambda: self.stc31SelfTestButton.setText("Self test"))
        else:
            self.stc31SelfTestButton.setText("Self test: FAIL (0x{:04x})".format(result))
            QTimer().singleShot(5000, lambda: self.stc31SelfTestButton.setText("Self test"))

    def update_buffer_sizes(self):
        value = int(self.bufferSizeEdit.text())
        self.sht85TemperaturePlotWidget.change_capacity(value)
        self.sht85HumidityPlotWidget.change_capacity(value)
        self.sht85AnalogPlotWidget.change_capacity(value)
        self.stc31ConcentrationPlotWidget.change_capacity(value)
        self.stc31AnalogPlotWidget.change_capacity(value)

    # Function to create the layout
    def create_layout(self):
        masterLayout = QHBoxLayout()
        # Create a vertical layout for the left column
        leftColumnLayout = QVBoxLayout()

        self.ssbGroup.setCheckable(True)
        self.ssbGroup.setChecked(False)
        self.ssbGroup.clicked.connect(self.update_ssb_group)
        ssbLayout = QVBoxLayout()
        self.ssbGroup.setLayout(ssbLayout)
        self.ssbGroup.setFixedWidth(405)

        ssbLayout.addWidget(self.portLabel)

        powerGroup = QGroupBox("Power supply")
        powerLayout = QHBoxLayout()

        layout = QVBoxLayout()
        layout.addWidget(QLabel("Port 1"), alignment=Qt.AlignCenter)
        layout.addWidget(self.portOnePowerButton)
        layout.addWidget(self.portOnePowerLabel, alignment=Qt.AlignCenter)
        powerLayout.addLayout(layout)

        layout = QVBoxLayout()
        layout.addWidget(QLabel("Port 2"), alignment=Qt.AlignCenter)
        layout.addWidget(self.portTwoPowerButton)
        layout.addWidget(self.portTwoPowerLabel, alignment=Qt.AlignCenter)
        powerLayout.addLayout(layout)

        powerGroup.setLayout(powerLayout)
        ssbLayout.addWidget(powerGroup)

        shtGroup = QGroupBox("SHT85 control")
        shtLayout = QVBoxLayout()

        layout = QHBoxLayout()
        layout.addWidget(QLabel("Sensorbridge port"))
        layout.addWidget(self.sht85PortDropdown)
        shtLayout.addLayout(layout)

        shtLayout.addWidget(self.sht85compensationCheckbox)

        shtLayout.addWidget(self.sht85TemperatureLabel)
        shtLayout.addWidget(self.sht85HumidityLabel)
        shtLayout.addWidget(self.sht85AnalogLabel)

        shtLayout.addWidget(self.sht85BlinkButton)

        shtGroup.setLayout(shtLayout)
        ssbLayout.addWidget(shtGroup)

        stcGroup = QGroupBox("STC31 control")
        stcLayout = QVBoxLayout()

        layout = QHBoxLayout()
        layout.addWidget(QLabel("Sensorbridge port"))
        layout.addWidget(self.stc31PortDropdown)
        stcLayout.addLayout(layout)

        layout = QHBoxLayout()
        layout.addWidget(QLabel("Binary gas"))
        layout.addWidget(self.stc31BinaryGasDropdown)
        stcLayout.addLayout(layout)

        layout = QHBoxLayout()
        layout.addWidget(QLabel("Relative humidity"))
        layout.addWidget(self.stc31RelativeHumidityEdit)
        stcLayout.addLayout(layout)

        layout = QHBoxLayout()
        layout.addWidget(QLabel("Temperature"))
        layout.addWidget(self.stc31TemperatureEdit)
        stcLayout.addLayout(layout)

        layout = QHBoxLayout()
        layout.addWidget(QLabel("Pressure"))
        layout.addWidget(self.stc31PressureEdit)
        stcLayout.addLayout(layout)

        layout = QHBoxLayout()
        layout.addWidget(QLabel("Forced calibration"))
        layout.addWidget(self.stc31ForcedRecalibrationEdit)
        stcLayout.addLayout(layout)

        stcLayout.addWidget(self.stc31AutoSelfCalibrationCheckbox)
        stcLayout.addWidget(self.stc31GasConcentrationLabel)
        stcLayout.addWidget(self.stc31AnalogLabel)
        stcLayout.addWidget(self.stc31SelfTestButton)
        stcLayout.addWidget(self.stc31SoftResetButton)
        stcLayout.addWidget(self.stc31BlinkButton)

        stcGroup.setLayout(stcLayout)
        ssbLayout.addWidget(stcGroup)

        layout = QHBoxLayout()
        layout.addWidget(QLabel("Measurement interval"))
        layout.addWidget(self.intervalEdit)
        layout.addWidget(QLabel("minutes"))
        layout.setStretch(0, 10)
        ssbLayout.addLayout(layout)

        ssbLayout.addWidget(self.savingButton)
        button = QPushButton("I2C scan")
        i2cLabel = QLabel("I2C devices: unknown")
        button.clicked.connect(lambda: i2cLabel.setText(
            "I2C devices: " + ", ".join([hex(address) for address in self.device.scan_i2c(SensorBridgePort.ONE)] + [hex(address) for address in self.device.scan_i2c(SensorBridgePort.TWO)])))
        ssbLayout.addWidget(button)
        ssbLayout.addWidget(i2cLabel)

        layout = QHBoxLayout()
        layout.addWidget(QLabel("Buffer size"))
        layout.addWidget(self.bufferSizeEdit)
        layout.addWidget(QLabel("samples"))
        layout.setStretch(0, 10)
        ssbLayout.addLayout(layout)

        leftColumnLayout.addWidget(self.ssbGroup, alignment=Qt.AlignTop)
        leftColumnLayout.setStretch(2, 10)

        masterLayout.addLayout(leftColumnLayout)

        # Layout housing all the plot widgets
        rightColumnLayout = QVBoxLayout()
        rightColumnLayout.addWidget(self.sht85TemperaturePlotWidget)
        rightColumnLayout.addWidget(self.sht85HumidityPlotWidget)
        rightColumnLayout.addWidget(self.sht85AnalogPlotWidget)
        rightColumnLayout.addWidget(self.stc31ConcentrationPlotWidget)
        rightColumnLayout.addWidget(self.stc31AnalogPlotWidget)

        masterLayout.addLayout(rightColumnLayout)

        return masterLayout

    def sht85_port_changed(self):
        self.sht85device.bridgePort = self.sht85device.PORTS[self.sht85PortDropdown.currentText()]

    def stc31_port_changed(self):
        self.stc31device.bridgePort = self.stc31device.PORTS[self.stc31PortDropdown.currentText()]

    def port_one_supply_clicked(self):
        if not self.portTwoPowerEnabled:
            self.portOnePowerButton.setText("Disable supply")
            self.portOnePowerLabel.setText("Supply state: enabled")
            self.portOnePowerEnabled = True
            self.device.switch_supply_on(SensorBridgePort.ONE)
            QTimer().singleShot(200, self.stc31device.disable_crc)
            QTimer().singleShot(300, lambda: self.stc31device.set_binary_gas(self.stc31BinaryGasDropdown.currentText()))
        else:
            self.portOnePowerButton.setText("Enable supply")
            self.portOnePowerLabel.setText("Supply state: disabled")
            self.portOnePowerEnabled = False
            self.device.switch_supply_off(SensorBridgePort.ONE)

    def port_two_supply_clicked(self):
        if not self.portTwoPowerEnabled:
            self.portTwoPowerButton.setText("Disable supply")
            self.portTwoPowerLabel.setText("Supply state: enabled")
            self.portTwoPowerEnabled = True
            self.device.switch_supply_on(SensorBridgePort.TWO)
            QTimer().singleShot(200, self.stc31device.disable_crc)
            QTimer().singleShot(300, lambda: self.stc31device.set_binary_gas(self.stc31BinaryGasDropdown.currentText()))
        else:
            self.portTwoPowerButton.setText("Enable supply")
            self.portTwoPowerLabel.setText("Supply state: disabled")
            self.portTwoPowerEnabled = False
            self.device.switch_supply_off(SensorBridgePort.TWO)

    def compensate_changed(self):
        if self.sht85compensationCheckbox.isChecked():
            self.sht85compensationEnabled = True
            self.stc31TemperatureEdit.setEnabled(False)
            self.stc31RelativeHumidityEdit.setEnabled(False)
        else:
            self.sht85compensationEnabled = False
            self.stc31TemperatureEdit.setEnabled(True)
            self.stc31RelativeHumidityEdit.setEnabled(True)

    def compensate_stc31(self, temperature, humidity):
        self.stc31TemperatureEdit.setValue(temperature)
        self.stc31RelativeHumidityEdit.setValue(humidity)
        # manually trigger updates
        self.stc31device.set_temperature(temperature)
        self.stc31device.set_relative_humidity(humidity)

    def on_timeout(self):
        try:
            temperature, humidity = self.sht85device.get_measurements()
            analog1 = self.sht85device.analog_measurement()
            self.sht85TemperatureLabel.setText(f"Temp: {temperature:.5f} ℃")
            self.sht85HumidityLabel.setText(f"Humidity: {humidity:.5f} % RH")
            self.sht85AnalogLabel.setText(f"Analog: {analog1:.5f} V")

            if self.sht85compensationEnabled:
                self.compensate_stc31(temperature, humidity)

            concentration = self.stc31device.measure_gas_concentration()
            analog2 = self.stc31device.analog_measurement()
            self.stc31GasConcentrationLabel.setText(f"Concentration: {concentration:.5f} %")
            self.stc31AnalogLabel.setText(f"Analog: {analog2:.5f} V")

            self.sht85TemperatureReady.emit(temperature)
            self.sht85HumidityReady.emit(humidity)
            self.sht85AnalogReady.emit(analog1)
            self.stc31ConcentrationReady.emit(concentration)
            self.stc31AnalogReady.emit(analog2)
        except Exception:
            return

        if self.savingEnabled:
            self.append_to_csv(temperature, humidity, concentration, analog1, analog2)

    def saving_button_clicked(self):
        if not self.savingEnabled:
            filename = datetime.now().strftime(f"sensorbridge_%Y-%m-%d_%H-%M-%S.csv")
            self.csvFile = open(filename, 'w')
            self.csvFile.write(
                "{},{},{},{},{},{}\n".format("Temperature", "Humidity", "Concentration", "Analog 1", "Analog 2",
                                             "Timestamp"))
            self.csvFile.close()
            self.savingEnabled = True
            self.savingButton.setText("Disable saving to file")
        else:
            self.csvFile.close()
            self.csvFile = None
            self.savingEnabled = False
            self.savingButton.setText("Start saving to file")

    def append_to_csv(self, temperature, humidity, concentration, analog1, analog2):
        self.csvFile = open(self.csvFile.name, 'a')
        self.csvFile.write("{},{},{},{},{},{}\n".format(temperature, humidity, concentration, analog1, analog2,
                                                        datetime.now().strftime("%Y/%m/%d-%H:%M:%S")))
        self.csvFile.close()

    def update_devices(self, values):
        self.portLabel.setText(f"Serial port: {values['port']}")
        self.device = SensorBridgeShdlcDevice(
            ShdlcConnection(ShdlcSerialPort(values['port'], baudrate=values['baudrate'])),
            slave_address=values['address'])
        self.sht85device = SHT85(self.device, SensirionSensor.PORTS[self.sht85PortDropdown.currentText()])
        self.stc31device = STC31(self.device, SensirionSensor.PORTS[self.stc31PortDropdown.currentText()])

    def update_ssb_group(self):
        if self.ssbGroup.isChecked():
            dg = SSBDialog()
            dg.accepted.connect(self.update_devices)
            # if unsuccessful, disable the group
            if dg.exec_() == 0:
                self.ssbGroup.setChecked(False)
            else:
                self.timer.start(int(60 * 1000 * float(self.intervalEdit.text())))
        else:
            # Stop all timers, disconnect device to free up serial port
            self.timer.stop()
            self.device = None
            self.sht85device = None
            self.stc31device = None
            self.portLabel.setText("Serial port: not connected")
            if self.savingEnabled:
                self.csvFile.close()
                self.csvFile = None
                self.savingEnabled = False
Exemplo n.º 35
0
class MachineErrorChecker(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)

        self._global_stack = None

        self._has_errors = True  # Result of the error check, indicating whether there are errors in the stack
        self._error_keys = set()  # A set of settings keys that have errors
        self._error_keys_in_progress = set(
        )  # The variable that stores the results of the currently in progress check

        self._stacks_and_keys_to_check = None  # a FIFO queue of tuples (stack, key) to check for errors

        self._need_to_check = False  # Whether we need to schedule a new check or not. This flag is set when a new
        # error check needs to take place while there is already one running at the moment.
        self._check_in_progress = False  # Whether there is an error check running in progress at the moment.

        self._application = Application.getInstance()
        self._machine_manager = self._application.getMachineManager()

        self._start_time = 0  # measure checking time

        # This timer delays the starting of error check so we can react less frequently if the user is frequently
        # changing settings.
        self._error_check_timer = QTimer(self)
        self._error_check_timer.setInterval(100)
        self._error_check_timer.setSingleShot(True)

    def initialize(self) -> None:
        self._error_check_timer.timeout.connect(self._rescheduleCheck)

        # Reconnect all signals when the active machine gets changed.
        self._machine_manager.globalContainerChanged.connect(
            self._onMachineChanged)

        # Whenever the machine settings get changed, we schedule an error check.
        self._machine_manager.globalContainerChanged.connect(
            self.startErrorCheck)
        self._machine_manager.globalValueChanged.connect(self.startErrorCheck)

        self._onMachineChanged()

    def _onMachineChanged(self) -> None:
        if self._global_stack:
            self._global_stack.propertyChanged.disconnect(self.startErrorCheck)
            self._global_stack.containersChanged.disconnect(
                self.startErrorCheck)

            for extruder in self._global_stack.extruders.values():
                extruder.propertyChanged.disconnect(self.startErrorCheck)
                extruder.containersChanged.disconnect(self.startErrorCheck)

        self._global_stack = self._machine_manager.activeMachine

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

            for extruder in self._global_stack.extruders.values():
                extruder.propertyChanged.connect(self.startErrorCheck)
                extruder.containersChanged.connect(self.startErrorCheck)

    hasErrorUpdated = pyqtSignal()
    needToWaitForResultChanged = pyqtSignal()
    errorCheckFinished = pyqtSignal()

    @pyqtProperty(bool, notify=hasErrorUpdated)
    def hasError(self) -> bool:
        return self._has_errors

    @pyqtProperty(bool, notify=needToWaitForResultChanged)
    def needToWaitForResult(self) -> bool:
        return self._need_to_check or self._check_in_progress

    # Starts the error check timer to schedule a new error check.
    def startErrorCheck(self, *args) -> None:
        if not self._check_in_progress:
            self._need_to_check = True
            self.needToWaitForResultChanged.emit()
        self._error_check_timer.start()

    # This function is called by the timer to reschedule a new error check.
    # If there is no check in progress, it will start a new one. If there is any, it sets the "_need_to_check" flag
    # to notify the current check to stop and start a new one.
    def _rescheduleCheck(self) -> None:
        if self._check_in_progress and not self._need_to_check:
            self._need_to_check = True
            self.needToWaitForResultChanged.emit()
            return

        self._error_keys_in_progress = set()
        self._need_to_check = False
        self.needToWaitForResultChanged.emit()

        global_stack = self._machine_manager.activeMachine
        if global_stack is None:
            Logger.log("i", "No active machine, nothing to check.")
            return

        # Populate the (stack, key) tuples to check
        self._stacks_and_keys_to_check = deque()
        for stack in [global_stack] + list(global_stack.extruders.values()):
            for key in stack.getAllKeys():
                self._stacks_and_keys_to_check.append((stack, key))

        self._application.callLater(self._checkStack)
        self._start_time = time.time()
        Logger.log("d", "New error check scheduled.")

    def _checkStack(self) -> None:
        if self._need_to_check:
            Logger.log(
                "d",
                "Need to check for errors again. Discard the current progress and reschedule a check."
            )
            self._check_in_progress = False
            self._application.callLater(self.startErrorCheck)
            return

        self._check_in_progress = True

        # If there is nothing to check any more, it means there is no error.
        if not self._stacks_and_keys_to_check:
            # Finish
            self._setResult(False)
            return

        # Get the next stack and key to check
        stack, key = self._stacks_and_keys_to_check.popleft()

        enabled = stack.getProperty(key, "enabled")
        if not enabled:
            self._application.callLater(self._checkStack)
            return

        validation_state = stack.getProperty(key, "validationState")
        if validation_state is None:
            # Setting is not validated. This can happen if there is only a setting definition.
            # We do need to validate it, because a setting definitions value can be set by a function, which could
            # be an invalid setting.
            definition = stack.getSettingDefinition(key)
            validator_type = SettingDefinition.getValidatorForType(
                definition.type)
            if validator_type:
                validator = validator_type(key)
                validation_state = validator(stack)
        if validation_state in (ValidatorState.Exception,
                                ValidatorState.MaximumError,
                                ValidatorState.MinimumError):
            # Finish
            self._setResult(True)
            return

        # Schedule the check for the next key
        self._application.callLater(self._checkStack)

    def _setResult(self, result: bool) -> None:
        if result != self._has_errors:
            self._has_errors = result
            self.hasErrorUpdated.emit()
            self._machine_manager.stacksValidationChanged.emit()
        self._need_to_check = False
        self._check_in_progress = False
        self.needToWaitForResultChanged.emit()
        self.errorCheckFinished.emit()
        Logger.log("i", "Error check finished, result = %s, time = %0.1fs",
                   result,
                   time.time() - self._start_time)
class QtReactor(posixbase.PosixReactorBase):
    # implements(IReactorFDSet)

    def __init__(self):
        self._reads = {}
        self._writes = {}
        self._notifiers = {}
        self._timer = QTimer()
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self.iterate)

        if QCoreApplication.instance() is None:
            # Application Object has not been started yet
            self.qApp = QCoreApplication([])
            self._ownApp = True
        else:
            self.qApp = QCoreApplication.instance()
            self._ownApp = False
        self._blockApp = None
        posixbase.PosixReactorBase.__init__(self)

    def _add(self, xer, primary, type):
        """
        Private method for adding a descriptor from the event loop.

        It takes care of adding it if  new or modifying it if already added
        for another state (read -> read/write for example).
        """

        if xer not in primary:
            primary[xer] = TwistedSocketNotifier(None, self, xer, type)

    def addReader(self, reader):
        """
        Add a FileDescriptor for notification of data available to read.
        """

        self._add(reader, self._reads, QSocketNotifier.Read)

    def addWriter(self, writer):
        """
        Add a FileDescriptor for notification of data available to write.
        """

        self._add(writer, self._writes, QSocketNotifier.Write)

    def _remove(self, xer, primary):
        """
        Private method for removing a descriptor from the event loop.

        It does the inverse job of _add, and also add a check in case of the fd
        has gone away.
        """

        if xer in primary:
            notifier = primary.pop(xer)
            notifier.shutdown()

    def removeReader(self, reader):
        """
        Remove a Selectable for notification of data available to read.
        """

        self._remove(reader, self._reads)

    def removeWriter(self, writer):
        """
        Remove a Selectable for notification of data available to write.
        """

        self._remove(writer, self._writes)

    def removeAll(self):
        """
        Remove all selectables, and return a list of them.
        """

        rv = self._removeAll(self._reads, self._writes)
        return rv

    def getReaders(self):
        return self._reads.keys()

    def getWriters(self):
        return self._writes.keys()

    def callLater(self, howlong, *args, **kargs):
        rval = super(QtReactor, self).callLater(howlong, *args, **kargs)
        self.reactorInvocation()
        return rval

    def reactorInvocation(self):
        self._timer.stop()
        self._timer.setInterval(0)
        self._timer.start()

    def _iterate(self, delay=None, fromqt=False):
        """
        See twisted.internet.interfaces.IReactorCore.iterate.
        """

        self.runUntilCurrent()
        self.doIteration(delay, fromqt)

    iterate = _iterate

    def doIteration(self, delay=None, fromqt=False):
        """
        This method is called by a Qt timer or by network activity on a file descriptor
        """

        if not self.running and self._blockApp:
            self._blockApp.quit()
        self._timer.stop()
        if delay is None:
            delay = 0
        delay = max(delay, 1)
        if not fromqt:
            self.qApp.processEvents(QEventLoop.AllEvents, delay * 1000)
        if self.timeout() is None:
            timeout = 0.1
        elif self.timeout() == 0:
            timeout = 0
        else:
            timeout = self.timeout()
        self._timer.setInterval(timeout * 1000)
        self._timer.start()

    def runReturn(self, installSignalHandlers=True):
        self.startRunning(installSignalHandlers=installSignalHandlers)
        self.reactorInvocation()

    def run(self, installSignalHandlers=True):
        if self._ownApp:
            self._blockApp = self.qApp
        else:
            self._blockApp = QEventLoop()
        self.runReturn()
        self._blockApp.exec_()
        if self.running:
            self.stop()
            self.runUntilCurrent()
class stopwatch(QWidget):
    def __init__(self):
        super(stopwatch, self).__init__()
        self.createMenu()
        self.start_button = QPushButton("Start", self)
        self.reset_button = QPushButton("Reset", self)
        self.int_button = QPushButton("Int", self)
        self.setGeometry(200, 200, 450, 220)
        self.setWindowTitle("StopWatch")
        self.table = QTableWidget(self)
        self.table.setColumnCount(1)
        self.table.setRowCount(100)
        
        
        self.LCD = QLCDNumber(self)
        self.gridGroupBox = QGroupBox()
        self.grid = QGridLayout()
        
        self.grid.addWidget(self.LCD, 0, 0, 1, 3)
        self.grid.addWidget(self.table, 0, 3, 0, 3)
        self.grid.setRowMinimumHeight(0, 200)
        self.grid.addWidget(self.start_button, 1, 0)
        self.grid.addWidget(self.int_button, 1, 1)
        self.grid.addWidget(self.reset_button, 1, 2)
        mainLayout = QVBoxLayout()
        mainLayout.setMenuBar(self.menuBar)
        
        
        self.gridGroupBox.setLayout(self.grid)
        mainLayout.addWidget(self.gridGroupBox)
        
        
        self.setLayout(mainLayout)
        
        self.LCD.setDigitCount(12)
        self.time = 0
        self.ms = 0
        self.sec = 0
        self.min = 0
        self.hour = 0
        self.rezults = 0
        self.LCD.display("00:00:00:00")
        self.timer = QTimer()
        self.timer.setInterval(10)
        self.timer.timeout.connect(self.display)
        self.start_button.clicked.connect(self.start)
        self.reset_button.clicked.connect(self.reset)
        self.int_button.clicked.connect(self.int_)
        
    def createMenu(self):
        self.menuBar = QMenuBar()

        self.fileMenu = QMenu("&File", self)
        self.exitAction = self.fileMenu.addAction("E&xit")
        self.menuBar.addMenu(self.fileMenu)    
    def start(self):
        self.timer.start()
        self.start_button.setText("Pause")
        self.start_button.clicked.disconnect()
        self.start_button.clicked.connect(self.stop)
        
    
    def stop(self):
        self.timer.stop()
        self.start_button.setText("Start")
        self.start_button.clicked.disconnect()
        self.start_button.clicked.connect(self.start)
        

    def int_(self):
        item = ("%02d:%02d:%02d:%02d" % (self.hour, self.min, self.sec, self.ms))
        self.table.setItem(0, self.rezults, QTableWidgetItem(item))
        self.rezults += 1
        

    def reset(self):
        self.timer.stop()
        self.time = 0
        self.ms = 0
        self.sec = 0
        self.min = 0
        self.hour = 0
        self.rezults = 0
        self.table.clearContents()
        self.stop()
        self.display()
        
        
    def display(self):
              
        self.LCD.display("%02d:%02d:%02d:%02d" % (self.hour, self.min, self.sec, self.ms))
        if self.ms != 99:
            self.ms += 1
        else:
            self.ms = 0
            if self.sec != 59:
                self.sec += 1
            else:
                self.sec = 0
                if self.min != 59:
                    self.min +=1
                else:
                    self.min = 0
                    if self.hour != 23:
                        self.hour += 1
                    else:
                        self.hour = 0
Exemplo n.º 38
0
class Animation(object):
    """
    Abstract class for setting up a 3D scene and animating it.
    """
    def __init__(self, title, frame_rate, run_time):
        """
        Create a new Animation.

        Arguments:
            title: str, the window title
            frame_rate: float, the number of frames to display per second
            run_time: float, the number of seconds to run the animation
        """
        self.title = title
        assert 0 < frame_rate < 1000
        self.frame_rate = frame_rate
        assert run_time > 0
        self.run_time = run_time

        self.frame = 0
        self.prev_update_time = None

        # import OpenGL so Qt can use it for rendering
        from OpenGL import GL
        # create the 3D window
        self.view = Qt3DWindow()
        self.view.setX(100)
        self.view.setY(100)

        self.view.setTitle(self.title)

        self.qml_engine = QQmlEngine(self.view)

    def load_qml(self, path, parent):
        """
        Helper method to load an object using Qt's QML system.

        Arguments:
            path: str, a path to a QML file
            parent: QObject, the parent of the object to be loaded

        Returns:
            the loaded QObject
        """
        component = QQmlComponent(self.qml_engine, path, parent)
        obj = component.create()
        if obj is None:
            print('Error loading {}'.format(path))
            for error in component.errors():
                print(error.toString())
            exit()
        return obj

    def add_light(self, position, intensity=1.0, color=util.hsl(0, 0, 100)):
        """
        Helper method to add a simple point light to the scene.

        Arguments:
            position: QVector3D, the position of the light in the scene
            intensity: float, the intensity of the light
            color: QColor, the color of the light
        """
        light_entity = QEntity(self.scene)
        light = QPointLight(light_entity)
        light.setColor(color)
        light.setIntensity(intensity)
        light_transform = QTransform(self.scene)
        light_transform.setTranslation(position)
        light_entity.addComponent(light)
        light_entity.addComponent(light_transform)

    def add_rgb_cube(self, w, h, d):
        """
        Helper method to add an RGB-textured cube to the scene.

        Arguments:
            w: float, the width of the cube
            h: float, the height of the cube
            d: float, the depth of the cube

        Returns:
            the QTransform of the cube
        """
        cube_entity = QEntity(self.scene)
        cube_mesh = QCuboidMesh()
        cube_mesh.setXExtent(w)
        cube_mesh.setYExtent(h)
        cube_mesh.setZExtent(d)
        cube_entity.addComponent(cube_mesh)
        cube_transform = QTransform(self.scene)
        cube_entity.addComponent(cube_transform)
        if not hasattr(self, 'rgb_cube_material'):
            # load material definition from QML
            # this was the easiest way I could find to create a custom shader in Qt3D...
            self.rgb_cube_material = self.load_qml(
                os.path.join(os.path.dirname(__file__), 'RGBCubeMaterial.qml'),
                self.scene)
        cube_entity.addComponent(self.rgb_cube_material)

        return cube_transform

    def add_sphere(self, r):
        """
        Helper method to add a sphere to the scene.

        Arguments:
            r: float, the radius of the sphere

        Returns:
            the QTransform of the sphere
        """
        sphere_entity = QEntity(self.scene)
        sphere_mesh = QSphereMesh()
        sphere_mesh.setRadius(r)
        sphere_entity.addComponent(sphere_mesh)
        sphere_transform = QTransform(self.scene)
        sphere_entity.addComponent(sphere_transform)
        if not hasattr(self, 'sphere_material'):
            self.sphere_material = QPhongMaterial(self.scene)
            self.sphere_material.setAmbient(util.hsl(0, 0, 50))
        sphere_entity.addComponent(self.sphere_material)

        return sphere_transform

    def add_path(self, *pts):
        """
        Helper method to add a path to the scene.

        Arguments:
            pts: list of QVector3D's, the points in the path

        Returns:
            a list of the entities added to the scene
        """
        # make a bunch of cylinder objects aligned along the path
        entities = []
        prev_pt = None
        for pt in pts:
            if prev_pt is not None:
                if prev_pt != pt:
                    # for each adjacent pair of points that are different
                    # make a cylinder
                    path_entity = QEntity(self.scene)
                    path_mesh = QCylinderMesh()
                    path_mesh.setRadius(0.05)  # very thin
                    path_mesh.setLength((prev_pt - pt).length(
                    ))  # length is the distance between the points
                    path_entity.addComponent(path_mesh)
                    path_transform = QTransform(self.scene)
                    path_transform.setRotation(
                        QQuaternion.fromDirection(
                            QVector3D(0, 0, -1),
                            prev_pt - pt))  # rotate to point along path
                    path_transform.setTranslation(
                        (pt + prev_pt) / 2)  # center between points
                    path_entity.addComponent(path_transform)
                    if not hasattr(self, 'path_material'):
                        self.path_material = QPhongMaterial(self.scene)
                        self.path_material.setAmbient(util.hsl(0, 0, 50))
                    path_entity.addComponent(self.path_material)
                    entities.append(path_entity)
            prev_pt = pt
        return entities

    def setup_scene(self, background_color, camera_position, camera_lookat):
        """
        Sets up the scene. Should be called before running the animation.

        Arguments:
            background_color: QColor, background color of the scene
            camera_position: QVector3D, the position of the camera in the scene
            camera_lookat: QVector3D, the point that the camera should be pointing at in the scene
        """
        # clear color is the background color
        self.view.defaultFrameGraph().setClearColor(background_color)

        self.scene = QEntity()

        # let subclass populate scene
        self.make_scene()

        # set up camera
        camera = self.view.camera()
        camera.setPosition(camera_position)
        camera.setViewCenter(camera_lookat)

        self.view.setRootEntity(self.scene)

    def make_scene(self):
        """
        Abstract method. Populates the scene with objects.
        """
        raise NotImplementedError()

    def _update(self):
        """
        Updates the animation, rendering the next frame.
        """
        # current animation time in seconds
        t = util.lerp(self.frame, 0, self.frame_rate, 0, 1)
        # change in time since the last frame
        dt = 1 / self.frame_rate if self.prev_update_time is None else t - self.prev_update_time
        # call subclass's frame update
        self.update(self.frame, t, dt)

        # stop the animation and close the window if run past run_time
        if t >= self.run_time:
            self.animation_timer.stop()
            self.view.close()

        self.prev_update_time = t
        self.frame += 1

    def update(self, frame, t, dt):
        """
        Abstract method. Updates one frame of the animation.

        Arguments:
            frame: int, the current frame number
            t: float, the current animation time in seconds
            dt: float, the number of seconds since the last update
        """
        raise NotImplementedError()

    def run(self):
        """
        Runs the animation asynchronously. The animation runs in the background for self.run_time seconds.
        """
        self.animation_timer = QTimer(self.view)
        # timer interval in msecs
        self.animation_timer.setInterval(1000 / self.frame_rate)
        # call update on each timeout of the timer
        self.animation_timer.timeout.connect(self._update)
        self.animation_timer.start()

        # show the main window
        self.view.show()
Exemplo n.º 39
0
class LabelableSegmentationEdgesLayer(SegmentationEdgesLayer):
    """
    Shows a set of user-labeled edges.
    """

    labelsChanged = pyqtSignal(dict)  # { id_pair, label_class }

    def __init__(self, datasource, label_class_pens, initial_labels={}, delay_ms=1000):
        # Class 0 (no label) is the default pen
        super(LabelableSegmentationEdgesLayer, self).__init__(datasource, default_pen=label_class_pens[0])
        self._delay_ms = delay_ms
        self._label_class_pens = label_class_pens

        # Initialize the labels and pens
        self.overwrite_edge_labels(initial_labels)

        self._buffered_updates = {}

        # To avoid sending lots of single updates if the user is clicking quickly,
        # we buffer the updates into a dict that is only sent after a brief delay.
        self._timer = QTimer(self)
        self._timer.setInterval(self._delay_ms)
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self._signal_buffered_updates)

    def overwrite_edge_labels(self, new_edge_labels):
        self._edge_labels = defaultdict(lambda: 0, new_edge_labels)

        # Change the pens accordingly
        pen_table = {}
        for id_pair, label_class in list(self._edge_labels.items()):
            # Omit unlabeled edges; there are usually a lot of them
            # and the default is class 0 anyway.
            if label_class != 0:
                pen_table[id_pair] = self._label_class_pens[label_class]
        self.pen_table.overwrite(pen_table)

    def handle_edge_clicked(self, id_pair, event):
        """
        Overridden from SegmentationEdgesLayer
        """
        old_class = self._edge_labels[id_pair]

        if event.buttons() == Qt.LeftButton:
            new_class = 1
        elif event.buttons() == Qt.RightButton:
            new_class = 2
        else:
            return

        if new_class == old_class:
            new_class = 0

        # For now, edge_labels dictionary will still contain 0-labeled edges.
        # We could delete them, but why bother?
        self._edge_labels[id_pair] = new_class

        # Update the display immediately
        self.pen_table[id_pair] = self._label_class_pens[new_class]

        # Buffer the update for listeners
        self._buffered_updates[id_pair] = new_class

        # Reset the timer
        self._timer.start()

        event.accept()

    def handle_edge_swiped(self, id_pair, event):
        if event.buttons() == Qt.LeftButton:
            new_class = 1
        elif event.buttons() == Qt.RightButton:
            new_class = 2
        elif event.buttons() == (Qt.LeftButton | Qt.RightButton):
            new_class = 0
        else:
            return

        self._edge_labels[id_pair] = new_class

        # Update the display immediately
        self.pen_table[id_pair] = self._label_class_pens[new_class]

        # Notify listeners immediately
        self.labelsChanged.emit({id_pair: new_class})

        event.accept()

    def _signal_buffered_updates(self):
        updates = self._buffered_updates
        self._buffered_updates = {}
        if updates:
            self.labelsChanged.emit(updates)
Exemplo n.º 40
0
class CuraEngineBackend(Backend):
    def __init__(self):
        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.
        default_engine_location = os.path.join(Application.getInstallPrefix(), "bin", "CuraEngine")
        if hasattr(sys, "frozen"):
            default_engine_location = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "CuraEngine")
        if sys.platform == "win32":
            default_engine_location += ".exe"
        default_engine_location = os.path.abspath(default_engine_location)
        Preferences.getInstance().addPreference("backend/location", default_engine_location)

        self._scene = Application.getInstance().getController().getScene()
        self._scene.sceneChanged.connect(self._onSceneChanged)

        # Workaround to disable layer view processing if layer view is not active.
        self._layer_view_active = False
        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
        self._onActiveViewChanged()
        self._stored_layer_data = None

        self._settings = None
        Application.getInstance().activeMachineChanged.connect(self._onActiveMachineChanged)
        self._onActiveMachineChanged()

        self._change_timer = QTimer()
        self._change_timer.setInterval(500)
        self._change_timer.setSingleShot(True)
        self._change_timer.timeout.connect(self.slice)

        self._message_handlers[Cura_pb2.SlicedObjectList] = self._onSlicedObjectListMessage
        self._message_handlers[Cura_pb2.Progress] = self._onProgressMessage
        self._message_handlers[Cura_pb2.GCodeLayer] = self._onGCodeLayerMessage
        self._message_handlers[Cura_pb2.GCodePrefix] = self._onGCodePrefixMessage
        self._message_handlers[Cura_pb2.ObjectPrintTime] = self._onObjectPrintTimeMessage

        self._slicing = False
        self._restart = False

        self._save_gcode = True
        self._save_polygons = True
        self._report_progress = True

        self._enabled = True

        self.backendConnected.connect(self._onBackendConnected)

    def getEngineCommand(self):
        return [Preferences.getInstance().getValue("backend/location"),"connect", "127.0.0.1:{0}".format(self._port),  "-j", Resources.getPath(Resources.SettingsLocation, "fdmprinter.json"), "-vv"]

    ##  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 whne the slicing process is aborted forcefully.
    slicingCancelled = Signal()

    ##  Perform a slice of the scene with the given set of settings.
    #
    #   \param kwargs Keyword arguments.
    #                 Valid values are:
    #                 - settings: The settings to use for the slice. The default is the active machine.
    #                 - save_gcode: True if the generated gcode should be saved, False if not. True by default.
    #                 - save_polygons: True if the generated polygon data should be saved, False if not. True by default.
    #                 - force_restart: True if the slicing process should be forcefully restarted if it is already slicing.
    #                                  If False, this method will do nothing when already slicing. True by default.
    #                 - report_progress: True if the slicing progress should be reported, False if not. Default is True.
    def slice(self, **kwargs):
        if not self._enabled:
            return

        if self._slicing:
            if not kwargs.get("force_restart", True):
                return

            self._slicing = False
            self._restart = True
            if self._process is not None:
                Logger.log("d", "Killing engine process")
                try:
                    self._process.terminate()
                except: # terminating a process that is already terminating causes an exception, silently ignore this.
                    pass
            self.slicingCancelled.emit()
            return

        object_groups = []
        if self._settings.getSettingValueByKey("print_sequence") == "One at a time":
            for node in OneAtATimeIterator(self._scene.getRoot()):
                temp_list = []
                children = node.getAllChildren()
                children.append(node)
                for child_node in children:
                    if type(child_node) is SceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None:
                        temp_list.append(child_node)
                object_groups.append(temp_list)
        else:
            temp_list = []
            for node in DepthFirstIterator(self._scene.getRoot()):
                if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
                    if not getattr(node, "_outside_buildarea", False):
                        temp_list.append(node)
            if len(temp_list) == 0:
                return
            object_groups.append(temp_list)
        #for node in DepthFirstIterator(self._scene.getRoot()):
        #    if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
        #        if not getattr(node, "_outside_buildarea", False):
        #            objects.append(node)

        if len(object_groups) == 0:
            return #No point in slicing an empty build plate

        if kwargs.get("settings", self._settings).hasErrorValue():
            return #No slicing if we have error values since those are by definition illegal values.

        self._slicing = True
        self.slicingStarted.emit()

        self._report_progress = kwargs.get("report_progress", True)
        if self._report_progress:
            self.processingProgress.emit(0.0)

        self._sendSettings(kwargs.get("settings", self._settings))

        self._scene.acquireLock()

        # Set the gcode as an empty list. This will be filled with strings by GCodeLayer messages.
        # This is done so the gcode can be fragmented in memory and does not need a continues memory space.
        # (AKA. This prevents MemoryErrors)
        self._save_gcode = kwargs.get("save_gcode", True)
        if self._save_gcode:
            setattr(self._scene, "gcode_list", [])

        self._save_polygons = kwargs.get("save_polygons", True)

        slice_message = Cura_pb2.Slice()

        for group in object_groups:
            group_message = slice_message.object_lists.add()
            for object in group:
                mesh_data = object.getMeshData().getTransformed(object.getWorldTransformation())

                obj = group_message.objects.add()
                obj.id = id(object)
                
                verts = numpy.array(mesh_data.getVertices())
                verts[:,[1,2]] = verts[:,[2,1]]
                verts[:,1] *= -1
                obj.vertices = verts.tostring()

        self._scene.releaseLock()
        self._socket.sendMessage(slice_message)

    def _onSceneChanged(self, source):
        if (type(source) is not SceneNode) or (source is self._scene.getRoot()) or (source.getMeshData() is None):
            return

        if(source.getMeshData().getVertices() is None):
            return

        self._onChanged()

    def _onActiveMachineChanged(self):
        if self._settings:
            self._settings.settingChanged.disconnect(self._onSettingChanged)

        self._settings = Application.getInstance().getActiveMachine()
        if self._settings:
            self._settings.settingChanged.connect(self._onSettingChanged)
            self._onChanged()

    def _onSettingChanged(self, setting):
        self._onChanged()

    def _onSlicedObjectListMessage(self, message):
        if self._save_polygons:
            if self._layer_view_active:
                job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message)
                job.start()
            else :
                self._stored_layer_data = message

    def _onProgressMessage(self, message):
        if message.amount >= 0.99:
            self._slicing = False

        if self._report_progress:
            self.processingProgress.emit(message.amount)

    def _onGCodeLayerMessage(self, message):
        if self._save_gcode:
            job = ProcessGCodeJob.ProcessGCodeLayerJob(message)
            job.start()

    def _onGCodePrefixMessage(self, message):
        if self._save_gcode:
            self._scene.gcode_list.insert(0, message.data.decode("utf-8", "replace"))

    def _onObjectPrintTimeMessage(self, message):
        self.printDurationMessage.emit(message.time, message.material_amount)
        self.processingProgress.emit(1.0)

    def _createSocket(self):
        super()._createSocket()
        
        self._socket.registerMessageType(1, Cura_pb2.Slice)
        self._socket.registerMessageType(2, Cura_pb2.SlicedObjectList)
        self._socket.registerMessageType(3, Cura_pb2.Progress)
        self._socket.registerMessageType(4, Cura_pb2.GCodeLayer)
        self._socket.registerMessageType(5, Cura_pb2.ObjectPrintTime)
        self._socket.registerMessageType(6, Cura_pb2.SettingList)
        self._socket.registerMessageType(7, Cura_pb2.GCodePrefix)

    def _onChanged(self):
        if not self._settings:
            return

        self._change_timer.start()

    def _sendSettings(self, settings):
        msg = Cura_pb2.SettingList()
        for setting in settings.getAllSettings(include_machine=True):
            s = msg.settings.add()
            s.name = setting.getKey()
            s.value = str(setting.getValue()).encode("utf-8")

        self._socket.sendMessage(msg)

    def _onBackendConnected(self):
        if self._restart:
            self._onChanged()
            self._restart = False

    def _onToolOperationStarted(self, tool):
        self._enabled = False

    def _onToolOperationStopped(self, tool):
        self._enabled = True
        self._onChanged()

    def _onActiveViewChanged(self):
        if Application.getInstance().getController().getActiveView():
            view = Application.getInstance().getController().getActiveView()
            if view.getPluginId() == "LayerView":
                self._layer_view_active = True
                if self._stored_layer_data:
                    job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(self._stored_layer_data)
                    job.start()
            else:
                self._layer_view_active = False
Exemplo n.º 41
0
class GlobalStacksModel(ListModel):
    NameRole = Qt.UserRole + 1
    IdRole = Qt.UserRole + 2
    HasRemoteConnectionRole = Qt.UserRole + 3
    ConnectionTypeRole = Qt.UserRole + 4
    MetaDataRole = Qt.UserRole + 5
    DiscoverySourceRole = Qt.UserRole + 6  # For separating local and remote printers in the machine management page
    RemovalWarningRole = Qt.UserRole + 7
    IsOnlineRole = Qt.UserRole + 8

    def __init__(self, parent=None) -> None:
        super().__init__(parent)

        self._catalog = i18nCatalog("cura")

        self.addRoleName(self.NameRole, "name")
        self.addRoleName(self.IdRole, "id")
        self.addRoleName(self.HasRemoteConnectionRole, "hasRemoteConnection")
        self.addRoleName(self.MetaDataRole, "metadata")
        self.addRoleName(self.DiscoverySourceRole, "discoverySource")
        self.addRoleName(self.IsOnlineRole, "isOnline")

        self._change_timer = QTimer()
        self._change_timer.setInterval(200)
        self._change_timer.setSingleShot(True)
        self._change_timer.timeout.connect(self._update)

        self._filter_connection_type = None  # type: Optional[ConnectionType]
        self._filter_online_only = False

        # Listen to changes
        CuraContainerRegistry.getInstance().containerAdded.connect(
            self._onContainerChanged)
        CuraContainerRegistry.getInstance().containerMetaDataChanged.connect(
            self._onContainerChanged)
        CuraContainerRegistry.getInstance().containerRemoved.connect(
            self._onContainerChanged)
        self._updateDelayed()

    filterConnectionTypeChanged = pyqtSignal()

    def setFilterConnectionType(self,
                                new_filter: Optional[ConnectionType]) -> None:
        self._filter_connection_type = new_filter

    @pyqtProperty(int,
                  fset=setFilterConnectionType,
                  notify=filterConnectionTypeChanged)
    def filterConnectionType(self) -> int:
        """
        The connection type to filter the list of printers by.

        Only printers that match this connection type will be listed in the
        model.
        """
        if self._filter_connection_type is None:
            return -1
        return self._filter_connection_type.value

    filterOnlineOnlyChanged = pyqtSignal()

    def setFilterOnlineOnly(self, new_filter: bool) -> None:
        self._filter_online_only = new_filter

    @pyqtProperty(bool,
                  fset=setFilterOnlineOnly,
                  notify=filterOnlineOnlyChanged)
    def filterOnlineOnly(self) -> bool:
        """
        Whether to filter the global stacks to show only printers that are online.
        """
        return self._filter_online_only

    def _onContainerChanged(self, container) -> None:
        """Handler for container added/removed events from registry"""

        # We only need to update when the added / removed container GlobalStack
        if isinstance(container, GlobalStack):
            self._updateDelayed()

    def _updateDelayed(self) -> None:
        self._change_timer.start()

    def _update(self) -> None:
        items = []

        container_stacks = CuraContainerRegistry.getInstance(
        ).findContainerStacks(type="machine")
        for container_stack in container_stacks:
            if self._filter_connection_type is not None:  # We want to filter on connection types.
                if not any((connection_type == self._filter_connection_type
                            for connection_type in
                            container_stack.configuredConnectionTypes)):
                    continue  # No connection type on this printer matches the filter.

            has_remote_connection = False

            for connection_type in container_stack.configuredConnectionTypes:
                has_remote_connection |= connection_type in [
                    ConnectionType.NetworkConnection.value,
                    ConnectionType.CloudConnection.value
                ]

            if parseBool(container_stack.getMetaDataEntry("hidden", False)):
                continue

            is_online = container_stack.getMetaDataEntry("is_online", False)
            if self._filter_online_only and not is_online:
                continue

            device_name = container_stack.getMetaDataEntry(
                "group_name", container_stack.getName())
            section_name = "Connected printers" if has_remote_connection else "Preset printers"
            section_name = self._catalog.i18nc("@info:title", section_name)

            default_removal_warning = self._catalog.i18nc(
                "@label {0} is the name of a printer that's about to be deleted.",
                "Are you sure you wish to remove {0}? This cannot be undone!",
                device_name)
            removal_warning = container_stack.getMetaDataEntry(
                "removal_warning", default_removal_warning)

            items.append({
                "name": device_name,
                "id": container_stack.getId(),
                "hasRemoteConnection": has_remote_connection,
                "metadata": container_stack.getMetaData().copy(),
                "discoverySource": section_name,
                "removalWarning": removal_warning,
                "isOnline": is_online
            })
        items.sort(key=lambda i: (not i["hasRemoteConnection"], i["name"]))
        self.setItems(items)
Exemplo n.º 42
0
class CameraDevice(QObject):

    frame_ready = pyqtSignal(QImage)

    def __init__(self, device_id=0):
        super().__init__()
        self.capture = cv2.VideoCapture(device_id)
        print(type(self.capture))
        self.timer = QTimer()

        if not self.capture.isOpened():
            raise ValueError("Device not found")

        self.timer.timeout.connect(self.read_frame)
        self.timer.setInterval(1000 / (self.fps or 30))
        self.timer.start()

    def __del__(self):
        self.timer.stop()
        self.capture.release()

    @property
    def fps(self):
        """Frames per second."""
        return int(self.capture.get(cv2.CAP_PROP_FPS))

    @property
    def size(self):
        """Returns the size of the video frames: (width, height)."""
        width = int(self.capture.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(self.capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
        return (width, height)

    def read_frame(self):
        """Read frame into QImage and emit it."""
        success, frame = self.capture.read()
        if success:
            img = _convert_array_to_qimage(frame)
            self.frame_ready.emit(img)
        else:
            raise ValueError("Failed to read frame")

    def start(self):
        threading.Thread(target=self.trackingBlueObject, args=())

    def stop(self):
        threading.Thread(target=self.stoptracking, args=())

    def stoptracking(self):
        print('stop')

    def trackingBlueObject(self):
        grabbed, frame = self.capture.read()

        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        mask = cv2.inRange(hsv, blueLower, blueUpper)
        mask = cv2.erode(mask, None, iterations=2)
        mask = cv2.dilate(mask, None, iterations=2)
        cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
                                cv2.CHAIN_APPROX_SIMPLE)[-2]
        center = None
        if len(cnts) > 0:
            c = max(cnts, key=cv2.contourArea)
            ((x, y), radius) = cv2.minEnclosingCircle(c)
            M = cv2.moments(c)
            center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
            print("{}\t{}".format(center[0], center[1]))
            if radius > 10:
                cv2.circle(frame, (int(x), int(y)), int(radius), (0, 255, 255),
                           2)
                cv2.circle(frame, center, 5, (0, 0, 255), -1)
            '''    
Exemplo n.º 43
0
class Right_ToolBar(QFrame):
    def __init__(self, parent=None):
        super().__init__(parent)  #调用父类构造函数
        self.ui = Ui_Right_ToolBar()  #创建ui对象
        self.ui.setupUi(self)  #构造ui对象

        #设置窗口背景透明
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)

        #和动画配合使用的计时器
        self.timer = QTimer()
        self.timer.setInterval(555)
        self.timer.timeout.connect(self.animation_over)
        self.bool_hidden = False  #辅助判断是否需要隐藏窗体的变量

        #========窗口拖拽相关
        self.bool_darg = False
        self.point_mouseStart = None
        self.point_windowTopLeft = None

#1# ==========自定义功能函数=========================================
# 出现动画

    def appear(self, int_x, int_y):
        #动画实现
        animation = QPropertyAnimation(self)
        animation.setTargetObject(self)
        animation.setPropertyName(b"pos")
        animation.setStartValue(
            QPoint(int_x + 440 - self.geometry().width(), int_y + 82))
        animation.setEndValue(QPoint(int_x + 440, int_y + 82))
        animation.setDuration(550)
        animation.setEasingCurve(QEasingCurve.OutBounce)
        animation.start()
        #准备结束处理
        self.timer.start()
        self.bool_hidden = False
        #设置优先显示
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
        self.show()

    #消失动画
    def fade(self):
        #动画实现
        animation = QPropertyAnimation(self)
        animation.setTargetObject(self)
        animation.setPropertyName(b"pos")
        animation.setStartValue(QPoint(self.pos().x(), self.pos().y()))
        animation.setEndValue(
            QPoint(self.pos().x() - self.geometry().width(),
                   self.pos().y()))
        animation.setDuration(550)
        animation.setEasingCurve(QEasingCurve.OutBounce)
        animation.start()
        #准备后事
        self.timer.start()
        self.bool_hidden = True
        #设置优先显示
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
        self.show()

    #动画结束后的处理
    def animation_over(self):
        self.timer.stop()
        if self.bool_hidden:
            self.hide()
        else:
            self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool
                                | Qt.WindowStaysOnTopHint)
            self.show()

    #移动函数,和父控件一起移动
    def move_distance(self, int_x, int_y):
        self.move(self.pos().x() + int_x, self.pos().y() + int_y)

#2# ==========事件处理函数===========================================

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.bool_darg = True
            self.point_mouseStart = event.globalPos()
            self.point_windowTopLeft = self.frameGeometry().topLeft()

    def mouseMoveEvent(self, event):
        if self.bool_darg:
            point_distance = event.globalPos() - self.point_mouseStart
            self.move(self.point_windowTopLeft + point_distance)

    def mouseReleaseEvent(self, event):
        if event.button == Qt.LeftButton:
            self.bool_darg = False
Exemplo n.º 44
0
class EditorAssembly(QWidget):
    """
    Class implementing the editor assembly widget containing the navigation
    combos and the editor widget.
    """
    def __init__(self,
                 dbs,
                 fn=None,
                 vm=None,
                 filetype="",
                 editor=None,
                 tv=None):
        """
        Constructor
        
        @param dbs reference to the debug server object
        @param fn name of the file to be opened (string). If it is None,
            a new (empty) editor is opened
        @param vm reference to the view manager object
            (ViewManager.ViewManager)
        @param filetype type of the source file (string)
        @param editor reference to an Editor object, if this is a cloned view
        @param tv reference to the task viewer object
        """
        super(EditorAssembly, self).__init__()

        self.__layout = QGridLayout(self)
        self.__layout.setContentsMargins(0, 0, 0, 0)
        self.__layout.setSpacing(1)

        self.__globalsCombo = QComboBox()
        self.__membersCombo = QComboBox()
        from .Editor import Editor
        self.__editor = Editor(dbs, fn, vm, filetype, editor, tv)

        self.__layout.addWidget(self.__globalsCombo, 0, 0)
        self.__layout.addWidget(self.__membersCombo, 0, 1)
        self.__layout.addWidget(self.__editor, 1, 0, 1, -1)

        self.__module = None

        self.__globalsCombo.activated[int].connect(self.__globalsActivated)
        self.__membersCombo.activated[int].connect(self.__membersActivated)
        self.__editor.cursorLineChanged.connect(self.__editorCursorLineChanged)

        self.__parseTimer = QTimer(self)
        self.__parseTimer.setSingleShot(True)
        self.__parseTimer.setInterval(5 * 1000)
        self.__parseTimer.timeout.connect(self.__parseEditor)
        self.__editor.textChanged.connect(self.__resetParseTimer)
        self.__editor.refreshed.connect(self.__resetParseTimer)

        self.__selectedGlobal = ""
        self.__selectedMember = ""
        self.__globalsBoundaries = {}
        self.__membersBoundaries = {}

        QTimer.singleShot(0, self.__parseEditor)

    def shutdownTimer(self):
        """
        Public method to stop and disconnect the timer.
        """
        self.__parseTimer.stop()
        self.__parseTimer.timeout.disconnect(self.__parseEditor)
        self.__editor.textChanged.disconnect(self.__resetParseTimer)
        self.__editor.refreshed.disconnect(self.__resetParseTimer)

    def getEditor(self):
        """
        Public method to get the reference to the editor widget.
        
        @return reference to the editor widget (Editor)
        """
        return self.__editor

    def __globalsActivated(self, index, moveCursor=True):
        """
        Private method to jump to the line of the selected global entry and to
        populate the members combo box.
        
        @param index index of the selected entry (integer)
        @keyparam moveCursor flag indicating to move the editor cursor
            (boolean)
        """
        # step 1: go to the line of the selected entry
        lineno = self.__globalsCombo.itemData(index)
        if lineno is not None:
            if moveCursor:
                txt = self.__editor.text(lineno - 1).rstrip()
                pos = len(txt.replace(txt.strip(), ""))
                self.__editor.gotoLine(lineno, pos if pos == 0 else pos + 1,
                                       True)
                self.__editor.setFocus()

            # step 2: populate the members combo, if the entry is a class
            self.__membersCombo.clear()
            self.__membersBoundaries = {}
            self.__membersCombo.addItem("")
            memberIndex = 0
            entryName = self.__globalsCombo.itemText(index)
            if self.__module:
                if entryName in self.__module.classes:
                    entry = self.__module.classes[entryName]
                elif entryName in self.__module.modules:
                    entry = self.__module.modules[entryName]
                    # step 2.0: add module classes
                    items = {}
                    for cl in entry.classes.values():
                        if cl.isPrivate():
                            icon = UI.PixmapCache.getIcon("class_private.png")
                        elif cl.isProtected():
                            icon = UI.PixmapCache.getIcon(
                                "class_protected.png")
                        else:
                            icon = UI.PixmapCache.getIcon("class.png")
                        items[cl.name] = (icon, cl.lineno, cl.endlineno)
                    for key in sorted(items.keys()):
                        itm = items[key]
                        self.__membersCombo.addItem(itm[0], key, itm[1])
                        memberIndex += 1
                        self.__membersBoundaries[(itm[1], itm[2])] = \
                            memberIndex
                else:
                    return

                # step 2.1: add class methods
                from Utilities.ModuleParser import Function
                items = {}
                for meth in entry.methods.values():
                    if meth.modifier == Function.Static:
                        icon = UI.PixmapCache.getIcon("method_static.png")
                    elif meth.modifier == Function.Class:
                        icon = UI.PixmapCache.getIcon("method_class.png")
                    elif meth.isPrivate():
                        icon = UI.PixmapCache.getIcon("method_private.png")
                    elif meth.isProtected():
                        icon = UI.PixmapCache.getIcon("method_protected.png")
                    else:
                        icon = UI.PixmapCache.getIcon("method.png")
                    items[meth.name] = (icon, meth.lineno, meth.endlineno)
                for key in sorted(items.keys()):
                    itm = items[key]
                    self.__membersCombo.addItem(itm[0], key, itm[1])
                    memberIndex += 1
                    self.__membersBoundaries[(itm[1], itm[2])] = memberIndex

                # step 2.2: add class instance attributes
                items = {}
                for attr in entry.attributes.values():
                    if attr.isPrivate():
                        icon = UI.PixmapCache.getIcon("attribute_private.png")
                    elif attr.isProtected():
                        icon = UI.PixmapCache.getIcon(
                            "attribute_protected.png")
                    else:
                        icon = UI.PixmapCache.getIcon("attribute.png")
                    items[attr.name] = (icon, attr.lineno)
                for key in sorted(items.keys()):
                    itm = items[key]
                    self.__membersCombo.addItem(itm[0], key, itm[1])

                # step 2.3: add class attributes
                items = {}
                icon = UI.PixmapCache.getIcon("attribute_class.png")
                for glob in entry.globals.values():
                    items[glob.name] = (icon, glob.lineno)
                for key in sorted(items.keys()):
                    itm = items[key]
                    self.__membersCombo.addItem(itm[0], key, itm[1])

    def __membersActivated(self, index, moveCursor=True):
        """
        Private method to jump to the line of the selected members entry.
        
        @param index index of the selected entry (integer)
        @keyparam moveCursor flag indicating to move the editor cursor
            (boolean)
        """
        lineno = self.__membersCombo.itemData(index)
        if lineno is not None and moveCursor:
            txt = self.__editor.text(lineno - 1).rstrip()
            pos = len(txt.replace(txt.strip(), ""))
            self.__editor.gotoLine(lineno, pos if pos == 0 else pos + 1, True)
            self.__editor.setFocus()

    def __resetParseTimer(self):
        """
        Private slot to reset the parse timer.
        """
        self.__parseTimer.stop()
        self.__parseTimer.start()

    def __parseEditor(self):
        """
        Private method to parse the editor source and repopulate the globals
        combo.
        """
        from Utilities.ModuleParser import Module, getTypeFromTypeName

        self.__module = None
        sourceType = getTypeFromTypeName(self.__editor.determineFileType())
        if sourceType != -1:
            src = self.__editor.text()
            if src:
                fn = self.__editor.getFileName()
                if fn is None:
                    fn = ""
                self.__module = Module("", fn, sourceType)
                self.__module.scan(src)

                # remember the current selections
                self.__selectedGlobal = self.__globalsCombo.currentText()
                self.__selectedMember = self.__membersCombo.currentText()

                self.__globalsCombo.clear()
                self.__membersCombo.clear()
                self.__globalsBoundaries = {}
                self.__membersBoundaries = {}

                self.__globalsCombo.addItem("")
                index = 0

                # step 1: add modules
                items = {}
                for module in self.__module.modules.values():
                    items[module.name] = (UI.PixmapCache.getIcon("module.png"),
                                          module.lineno, module.endlineno)
                for key in sorted(items.keys()):
                    itm = items[key]
                    self.__globalsCombo.addItem(itm[0], key, itm[1])
                    index += 1
                    self.__globalsBoundaries[(itm[1], itm[2])] = index

                # step 2: add classes
                items = {}
                for cl in self.__module.classes.values():
                    if cl.isPrivate():
                        icon = UI.PixmapCache.getIcon("class_private.png")
                    elif cl.isProtected():
                        icon = UI.PixmapCache.getIcon("class_protected.png")
                    else:
                        icon = UI.PixmapCache.getIcon("class.png")
                    items[cl.name] = (icon, cl.lineno, cl.endlineno)
                for key in sorted(items.keys()):
                    itm = items[key]
                    self.__globalsCombo.addItem(itm[0], key, itm[1])
                    index += 1
                    self.__globalsBoundaries[(itm[1], itm[2])] = index

                # step 3: add functions
                items = {}
                for func in self.__module.functions.values():
                    if func.isPrivate():
                        icon = UI.PixmapCache.getIcon("method_private.png")
                    elif func.isProtected():
                        icon = UI.PixmapCache.getIcon("method_protected.png")
                    else:
                        icon = UI.PixmapCache.getIcon("method.png")
                    items[func.name] = (icon, func.lineno, func.endlineno)
                for key in sorted(items.keys()):
                    itm = items[key]
                    self.__globalsCombo.addItem(itm[0], key, itm[1])
                    index += 1
                    self.__globalsBoundaries[(itm[1], itm[2])] = index

                # step 4: add attributes
                items = {}
                for glob in self.__module.globals.values():
                    if glob.isPrivate():
                        icon = UI.PixmapCache.getIcon("attribute_private.png")
                    elif glob.isProtected():
                        icon = UI.PixmapCache.getIcon(
                            "attribute_protected.png")
                    else:
                        icon = UI.PixmapCache.getIcon("attribute.png")
                    items[glob.name] = (icon, glob.lineno)
                for key in sorted(items.keys()):
                    itm = items[key]
                    self.__globalsCombo.addItem(itm[0], key, itm[1])

                # reset the currently selected entries without moving the
                # text cursor
                index = self.__globalsCombo.findText(self.__selectedGlobal)
                if index != -1:
                    self.__globalsCombo.setCurrentIndex(index)
                    self.__globalsActivated(index, moveCursor=False)
                index = self.__membersCombo.findText(self.__selectedMember)
                if index != -1:
                    self.__membersCombo.setCurrentIndex(index)
                    self.__membersActivated(index, moveCursor=False)

    def __editorCursorLineChanged(self, lineno):
        """
        Private slot handling a line change of the cursor of the editor.
        
        @param lineno line number of the cursor (integer)
        """
        lineno += 1  # cursor position is zero based, code info one based

        # step 1: search in the globals
        for (lower, upper), index in self.__globalsBoundaries.items():
            if upper == -1:
                upper = 1000000  # it is the last line
            if lower <= lineno <= upper:
                break
        else:
            index = 0
        self.__globalsCombo.setCurrentIndex(index)
        self.__globalsActivated(index, moveCursor=False)

        # step 2: search in members
        for (lower, upper), index in self.__membersBoundaries.items():
            if upper == -1:
                upper = 1000000  # it is the last line
            if lower <= lineno <= upper:
                break
        else:
            index = 0
        self.__membersCombo.setCurrentIndex(index)
        self.__membersActivated(index, moveCursor=False)
Exemplo n.º 45
0
class MeetingUi:
    def __init__(self, window: QtWidgets.QMainWindow,
                 meeting_tab: QtWidgets.QWidget):

        self.add_meeting_button: QtWidgets.QPushButton = meeting_tab.findChild(
            QtWidgets.QPushButton, 'addMeeting')
        self.add_meeting_button.clicked.connect(self.add_meeting)

        self.remove_meeting_button: QtWidgets.QPushButton = meeting_tab.findChild(
            QtWidgets.QPushButton, 'removeMeeting')
        self.remove_meeting_button.clicked.connect(self.remove_meeting)

        self.meeting_list: QtWidgets.QListWidget = meeting_tab.findChild(
            QtWidgets.QListWidget, 'meetingList')
        self.meeting_list.itemActivated.connect(self.select_meeting_item)

        self.status_bar: QtWidgets.QStatusBar = window.findChild(
            QtWidgets.QStatusBar, 'statusBar')

        self.model = MeetingList.load_from_file()
        self.update_meeting_list()

        self.auto_join = MeetingAutoJoin()

        self.timer = QTimer()
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.auto_join_meeting)
        self.timer.start()

    def auto_join_meeting(self):
        self.auto_join.process(self.model.get_meetings())

    def update_meeting_list(self):
        self.meeting_list.clear()
        self.meeting_list.addItems(list(map(str, self.model.get_meetings())))

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

    def add_meeting(self):
        dialog = MeetingEditDialog()
        if dialog.exec_():
            try:
                self.model.add_meeting(Meeting(*dialog.get_fields()))
                self.model.save()
                self.update_meeting_list()
            except ValueError as e:
                pass

    def remove_meeting(self):
        index = self.meeting_list.currentRow()
        if index < 0:
            return

        self.model.remove_index(index)
        self.model.save()
        self.update_meeting_list()

    def select_meeting_item(self):
        index = self.meeting_list.currentRow()
        if index < 0:
            return

        meeting = self.model.get_index(index)

        dialog = MeetingEditDialog(meeting)
        if dialog.exec_():
            try:
                new_meeting = Meeting(*dialog.get_fields())
                self.model.replace_index(index, new_meeting)
                self.model.save()
                self.update_meeting_list()
            except ValueError:
                pass
Exemplo n.º 46
0
class MikochikuAlarm(QWidget):
    def __init__(self, parent=None):
        super(MikochikuAlarm, self).__init__(parent)
        self.search_ch_id = settings.CHID
        self.old_video_id_list = []

        # メンバー一覧のjsonを取得し、memberに格納
        with open(".\\channel\\hololive.json", encoding="UTF-8") as file:
            self.member = json.load(file)

        # Checks which os is being used then sets the correct path
        if os.name == "posix": self.lang_path = "lang/"
        elif os.name == "nt": self.lang_path = ".\\lang\\"

        self.initUI()

    def initUI(self):

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.check_live)
        self.timer.setInterval(40000)
        self.timer.start()

        sakura_miko = QLabel(self)
        sakura_miko.setPixmap(QPixmap(resource_path(settings.ICON)))
        sakura_miko.move(65, 70)

        self.alarm_cb = QCheckBox(self.localized_text("alarm"), self)
        self.alarm_cb.toggle()

        # self.loop_cb = QCheckBox('アラームをループ再生する', self)
        # self.loop_cb.move(20, 40)
        # self.loop_cb.toggle()

        self.webbrowser_cb = QCheckBox(self.localized_text("webbrowser"), self)
        self.webbrowser_cb.toggle()

        self.alarm_stop = QPushButton(self.localized_text("waiting"), self)
        # self.alarm_stop.setCheckable(True)
        # self.alarm_stop.setEnabled(False)
        self.alarm_stop.clicked[bool].connect(self.stop_alarm)

        self.config_btn = QPushButton("config", self)
        self.config_btn.clicked.connect(self.cfg_dialog)
        self.dialogs = list()

        # setGeometry
        self.alarm_cb.setGeometry(10, 10, 250, 20)
        self.webbrowser_cb.setGeometry(10, 30, 250, 20)
        self.alarm_stop.setGeometry(80, 80, 80, 25)
        self.config_btn.setGeometry(195, 120, 60, 25)

        self.setGeometry(300, 300, 260, 150)
        self.setWindowTitle(self.localized_text("title"))

        # メンバー名をlistWidgetに格納
        self.listWidget = QListWidget(self)
        for v in self.member:
            self.listWidget.addItem(v['name'])
        self.listWidget.move(30, 200)
        self.listWidget.itemClicked.connect(self.clicked)

        self.show()

    def cfg_dialog(self):
        dialog = config_tab.ConfigTab(self)
        self.dialogs.append(dialog)

    # FIXME: 関数名が抽象的すぎる
    def clicked(self, qmode8ndex):
        # 要素番号使うのでcurrentRow()に変更
        member = self.member[self.listWidget.currentRow()]
        self.search_ch_id = member['channel_id']

    def check_live(self):
        buff_video_id_set = self.get_live_video_id(self.search_ch_id)
        print("buff_video_id_set", buff_video_id_set)
        print("self.old_video_id_list", self.old_video_id_list)
        if buff_video_id_set:
            for getting_video_id in buff_video_id_set:
                if not getting_video_id == "" and not getting_video_id is None:
                    if not getting_video_id in self.old_video_id_list:
                        self.old_video_id_list.append(getting_video_id)
                        if len(self.old_video_id_list) > 30:
                            self.old_video_id_list = self.old_video_id_list[1:]
                        print("")
                        print(self.get_text(self.get_locale_json(), "started"))
                        # self.alarm_stop.setEnabled(False)
                        self.alarm_stop.click()
                        self.alarm_stop.setText(
                            self.get_text(self.get_locale_json(), "stop"))
                        if self.webbrowser_cb.checkState():
                            webbrowser.open(
                                "https://www.youtube.com/watch?v=" +
                                getting_video_id)
                        if self.alarm_cb.checkState():
                            self.alarm_sound()

    def stop_alarm(self):
        pygame.mixer.music.stop()
        self.alarm_stop.setEnabled(True)
        self.alarm_stop.setText(self.localized_text("waiting"))

    def alarm_sound(self):
        # loop = 1
        # if self.loop_cb.checkState():
        loop_count = 5
        pygame.mixer.music.play(loop_count)
        pygame.mixer.music.play(loop_count)

    def get_live_video_id(self, search_ch_id):
        dict_str = ""
        video_id_set = set()
        try:
            session = requests.Session()
            headers = {
                'user-agent':
                'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
            }
            html = session.get("https://www.youtube.com/channel/" +
                               search_ch_id,
                               headers=headers,
                               timeout=10)
            soup = BeautifulSoup(html.text, 'html.parser')
            keyword = 'window["ytInitialData"]'
            for scrp in soup.find_all("script"):
                if keyword in str(scrp):
                    dict_str = str(scrp).split(' = ', 1)[1]
            dict_str = dict_str.replace('false', 'False')
            dict_str = dict_str.replace('true', 'True')

            index = dict_str.find("\n")
            dict_str = dict_str[:index - 1]
            dics = eval(dict_str)
            for section in dics.get("contents", {}).get(
                    "twoColumnBrowseResultsRenderer",
                {}).get("tabs",
                        {})[0].get("tabRenderer",
                                   {}).get("content",
                                           {}).get("sectionListRenderer",
                                                   {}).get("contents", {}):
                for itemsection in section.get("itemSectionRenderer",
                                               {}).get("contents", {}):
                    items = {}
                    if "shelfRenderer" in itemsection:
                        for items in itemsection.get("shelfRenderer",
                                                     {}).get("content",
                                                             {}).values():
                            for item in items.get("items", {}):
                                for videoRenderer in item.values():
                                    for badge in videoRenderer.get(
                                            "badges", {}):
                                        if badge.get(
                                                "metadataBadgeRenderer",
                                            {}).get(
                                                "style", {}
                                            ) == "BADGE_STYLE_TYPE_LIVE_NOW":
                                            video_id_set.add(
                                                videoRenderer.get(
                                                    "videoId", ""))
                    elif "channelFeaturedContentRenderer" in itemsection:
                        for item in itemsection.get(
                                "channelFeaturedContentRenderer",
                            {}).get("items", {}):
                            for badge in item.get("videoRenderer",
                                                  {}).get("badges", {}):
                                if badge.get("metadataBadgeRenderer", {}).get(
                                        "style",
                                        "") == "BADGE_STYLE_TYPE_LIVE_NOW":
                                    video_id_set.add(
                                        item.get("videoRenderer",
                                                 {}).get("videoId", ""))
        except:
            return video_id_set

        return video_id_set

    def load_locale_json(self):  # from json file
        path = self.lang_path + "locale.json"
        with open(path, mode='r') as file:
            dict_json = json.load(file)
            return dict_json["locale"]

    def localized_text(self, content):
        path = self.lang_path + self.load_locale_json() + ".json"
        with open(path, encoding="UTF-8") as file:
            dict_json = json.load(file)
        return dict_json[content]
Exemplo n.º 47
0
class mainwindow(QtWidgets.QWidget, ui_widget):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        ui_widget.__init__(self)
        self.setupUi(self)

        self.toolButtonStart.setIcon(QIcon("images//start.png"))
        self.init_meter_list()

        self.meter_state = False
        self.toolButtonStart.clicked.connect(self.start_meter)

        self.counter = 1
        self.timer = QTimer()
        self.timer.setInterval(100)
        self.timer.start()
        # 信号连接到槽
        self.timer.timeout.connect(self.onDelMsgQueue)

        self.mqt = my_mqtt()

    def init_meter_list(self):
        self.toolBoxDevList.removeItem(0)

        self.read_config()

        self.sm_widgets = {}
        for meter_id in self.meters_dict:
            sm_wdgt = single_meter_widget(meter_id, self.meters_name[meter_id],
                                          self.meters_dict[meter_id])
            self.sm_widgets[meter_id] = sm_wdgt
            self.toolBoxDevList.addItem(sm_wdgt, self.meters_name[meter_id])

    def read_config(self):
        print('read simu dev config start ...')

        config = configparser.ConfigParser()

        try:
            config.read("./config.ini", encoding='utf-8')
        except IOError:
            print("打开config.ini 失败!")
            return

        self.meters_dict = {}
        self.meters_name = {}
        meters = config.sections()
        for meter in meters:
            meases = config.items(meter)
            meas_dict = {}
            id = int(meter)
            for meas in meases:
                if meas[0] == "name":
                    self.meters_name[id] = meas[1]
                else:
                    meas_dict[meas[0]] = meas[1]

            self.meters_dict[id] = meas_dict

    def start_meter(self):
        host = self.lineEditHost.text()
        port = int(self.lineEditPort.text())

        if self.meter_state == True:
            self.meter_state = False
            self.toolButtonStart.setIcon(QIcon("images//start.png"))
            self.append_msg('停止通信')

            self.mqt.stop()
        else:
            ret = self.mqt.start(host, port)
            if ret != 0:
                QMessageBox.critical(self, "Error", "请检查host和port是否正确!")
                return

            self.meter_state = True
            self.toolButtonStart.setIcon(QIcon("images//stop.png"))
            self.append_msg('启动通信')

    def append_msg(self, msg):
        self.textBrowserMsg.append(msg)

    def onDelMsgQueue(self):
        while not MQTT_DOWN_QUEUE.empty():
            msg = MQTT_DOWN_QUEUE.get()
            request = json.loads(msg.payload.decode())

            soc = int(time.time())
            timestruct = time.localtime(soc)
            timestring = time.strftime("%Y-%m-%d %H:%M:%S", timestruct)

            if "id" in request.keys():
                meter_id = request["id"]
                if meter_id in self.sm_widgets.keys():
                    if 'heart' in request.keys():
                        self.append_msg("%s 心跳报文, id = %d" %
                                        (timestring, meter_id))
                        self.sm_widgets[meter_id].show_msg("%s 心跳报文" %
                                                           (timestring))
                    else:
                        self.append_msg("%s 召唤数据, id = %d" %
                                        (timestring, meter_id))
                        self.sm_widgets[meter_id].show_msg("%s 召唤数据" %
                                                           (timestring))
                        self.sm_widgets[meter_id].get_data()
                else:
                    self.append_msg("设备ID不在列表中 id = %d", meter_id)
            else:
                self.append_msg("WWWWWWWWWW")

        while not MQTT_UP_QUEUE.empty():
            msg = MQTT_UP_QUEUE.get()
            self.mqt.publish(msg)
Exemplo n.º 48
0
class SVApplication(QApplication):

    # Signals need to be on a QObject
    create_new_window_signal = pyqtSignal(str, object)
    cosigner_received_signal = pyqtSignal(object, object)
    labels_changed_signal = pyqtSignal(object, object, object)
    window_opened_signal = pyqtSignal(object)
    window_closed_signal = pyqtSignal(object)
    # Async tasks
    async_tasks_done = pyqtSignal()
    # Logging
    new_category = pyqtSignal(str)
    new_log = pyqtSignal(object)
    # Preferences updates
    fiat_ccy_changed = pyqtSignal()
    custom_fee_changed = pyqtSignal()
    op_return_enabled_changed = pyqtSignal()
    num_zeros_changed = pyqtSignal()
    base_unit_changed = pyqtSignal()
    fiat_history_changed = pyqtSignal()
    fiat_balance_changed = pyqtSignal()
    update_check_signal = pyqtSignal(bool, object)
    # Contact events
    contact_added_signal = pyqtSignal(object, object)
    contact_removed_signal = pyqtSignal(object)
    identity_added_signal = pyqtSignal(object, object)
    identity_removed_signal = pyqtSignal(object, object)
    new_notification = pyqtSignal(object, object)

    def __init__(self, argv):
        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-sv.desktop')
        super().__init__(argv)
        self.windows = []
        self.log_handler = SVLogHandler()
        self.log_window = None
        self.net_dialog = None
        self.timer = QTimer()
        self.exception_hook = None
        # A floating point number, e.g. 129.1
        self.dpi = self.primaryScreen().physicalDotsPerInch()

        # init tray
        self.dark_icon = app_state.config.get("dark_icon", False)
        self.tray = QSystemTrayIcon(self._tray_icon(), None)
        self.tray.setToolTip('ElectrumSV')
        self.tray.activated.connect(self._tray_activated)
        self._build_tray_menu()
        self.tray.show()

        # FIXME Fix what.. what needs to be fixed here?
        set_language(app_state.config.get('language', get_default_language()))

        logs.add_handler(self.log_handler)
        self._start()

    def _start(self):
        self.setWindowIcon(read_QIcon("electrum-sv.png"))
        self.installEventFilter(OpenFileEventFilter(self.windows))
        self.create_new_window_signal.connect(self.start_new_window)
        self.async_tasks_done.connect(app_state.async_.run_pending_callbacks)
        self.num_zeros_changed.connect(
            partial(self._signal_all, 'on_num_zeros_changed'))
        self.fiat_ccy_changed.connect(
            partial(self._signal_all, 'on_fiat_ccy_changed'))
        self.base_unit_changed.connect(
            partial(self._signal_all, 'on_base_unit_changed'))
        self.fiat_history_changed.connect(
            partial(self._signal_all, 'on_fiat_history_changed'))
        # Toggling of showing addresses in the fiat preferences.
        self.fiat_balance_changed.connect(
            partial(self._signal_all, 'on_fiat_balance_changed'))
        self.update_check_signal.connect(
            partial(self._signal_all, 'on_update_check'))
        ColorScheme.update_from_widget(QWidget())

    def _signal_all(self, method, *args):
        for window in self.windows:
            getattr(window, method)(*args)

    def _close(self):
        for window in self.windows:
            window.close()

    def close_window(self, window):
        app_state.daemon.stop_wallet_at_path(window._wallet.get_storage_path())
        self.windows.remove(window)
        self.window_closed_signal.emit(window)
        self._build_tray_menu()
        if not self.windows:
            self._last_window_closed()

    def setup_app(self):
        # app_state.daemon is initialised after app. Setup things dependent on daemon here.
        pass

    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()
        for window in self.windows:
            submenu = m.addMenu(window._wallet.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 ElectrumSV"), self._close)
        self.tray.setContextMenu(m)

    def _tray_icon(self):
        if self.dark_icon:
            return read_QIcon('electrumsv_dark_icon.png')
        else:
            return read_QIcon('electrumsv_light_icon.png')

    def _toggle_tray_icon(self):
        self.dark_icon = not self.dark_icon
        app_state.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 new_window(self,
                   path: Optional[str],
                   uri: Optional[str] = None) -> None:
        # Use a signal as can be called from daemon thread
        self.create_new_window_signal.emit(path, uri)

    def show_network_dialog(self, parent) -> None:
        if not app_state.daemon.network:
            parent.show_warning(_(
                'You are using ElectrumSV in offline mode; restart '
                'ElectrumSV if you want to get connected'),
                                title=_('Offline'))
            return
        if self.net_dialog:
            self.net_dialog.on_update()
            self.net_dialog.show()
            self.net_dialog.raise_()
            return
        from . import network_dialog
        # from importlib import reload
        # reload(network_dialog)
        self.net_dialog = network_dialog.NetworkDialog(
            app_state.daemon.network, app_state.config)
        self.net_dialog.show()

    def show_log_viewer(self) -> None:
        if self.log_window is None:
            self.log_window = SVLogWindow(None, self.log_handler)
        self.log_window.show()

    def _last_window_closed(self):
        for dialog in (self.net_dialog, self.log_window):
            if dialog:
                dialog.accept()

    def on_transaction_label_change(self, account: AbstractAccount,
                                    tx_hash: bytes, text: str) -> None:
        self.label_sync.set_transaction_label(account, tx_hash, text)

    def on_keyinstance_label_change(self, account: AbstractAccount,
                                    key_id: int, text: str) -> None:
        self.label_sync.set_keyinstance_label(account, key_id, text)

    def _create_window_for_wallet(self, wallet: Wallet):
        w = ElectrumWindow(wallet)
        self.windows.append(w)
        self._build_tray_menu()
        self._register_wallet_events(wallet)
        self.window_opened_signal.emit(w)
        return w

    def _register_wallet_events(self, wallet: Wallet) -> None:
        wallet.contacts._on_contact_added = self._on_contact_added
        wallet.contacts._on_contact_removed = self._on_contact_removed
        wallet.contacts._on_identity_added = self._on_identity_added
        wallet.contacts._on_identity_removed = self._on_identity_removed

    def _on_identity_added(self, contact: ContactEntry,
                           identity: ContactIdentity) -> None:
        self.identity_added_signal.emit(contact, identity)

    def _on_identity_removed(self, contact: ContactEntry,
                             identity: ContactIdentity) -> None:
        self.identity_removed_signal.emit(contact, identity)

    def _on_contact_added(self, contact: ContactEntry,
                          identity: ContactIdentity) -> None:
        self.contact_added_signal.emit(contact, identity)

    def _on_contact_removed(self, contact: ContactEntry) -> None:
        self.contact_removed_signal.emit(contact)

    def on_new_wallet_event(self, wallet_path: str,
                            row: WalletEventRow) -> None:
        self.new_notification.emit(wallet_path, row)

    def get_wallet_window(self, path: str) -> Optional[ElectrumWindow]:
        for w in self.windows:
            if w._wallet.get_storage_path() == path:
                return w

    def get_wallet_window_by_id(self,
                                account_id: int) -> Optional[ElectrumWindow]:
        for w in self.windows:
            for account in w._wallet.get_accounts():
                if account.get_id() == account_id:
                    return w

    def start_new_window(self,
                         wallet_path: Optional[str],
                         uri: Optional[str] = None,
                         is_startup: bool = False) -> Optional[ElectrumWindow]:
        '''Raises the window for the wallet if it is open.  Otherwise
        opens the wallet and creates a new window for it.'''
        for w in self.windows:
            if w._wallet.get_storage_path() == wallet_path:
                w.bring_to_top()
                break
        else:
            wizard_window: Optional[WalletWizard] = None
            if wallet_path is not None:
                is_valid, was_aborted, wizard_window = WalletWizard.attempt_open(
                    wallet_path)
                if was_aborted:
                    return None
                if not is_valid:
                    wallet_filename = os.path.basename(wallet_path)
                    MessageBox.show_error(
                        _("Unable to load file '{}'.").format(wallet_filename))
                    return None
            else:
                wizard_window = WalletWizard(is_startup=is_startup)
            if wizard_window is not None:
                result = wizard_window.run()
                if result != QDialog.Accepted:
                    return None
                wallet_path = wizard_window.get_wallet_path()
                # We cannot rely on accept alone indicating success.
                if wallet_path is None:
                    return None
            wallet = app_state.daemon.load_wallet(wallet_path)
            assert wallet is not None
            w = self._create_window_for_wallet(wallet)
        if uri:
            w.pay_to_URI(uri)
        w.bring_to_top()
        w.setWindowState(w.windowState() & ~QtCore.Qt.WindowMinimized
                         | QtCore.Qt.WindowActive)

        # this will activate the window
        w.activateWindow()
        return w

    def update_check(self) -> None:
        if (not app_state.config.get('check_updates', True)
                or app_state.config.get("offline", False)):
            return

        def f():
            import requests
            try:
                response = requests.request(
                    'GET',
                    "https://electrumsv.io/release.json",
                    headers={'User-Agent': 'ElectrumSV'},
                    timeout=10)
                result = response.json()
                self._on_update_check(True, result)
            except Exception:
                self._on_update_check(False, sys.exc_info())

        t = threading.Thread(target=f)
        t.setDaemon(True)
        t.start()

    def _on_update_check(self, success: bool, result: dict) -> None:
        if success:
            when_checked = datetime.datetime.now().astimezone().isoformat()
            app_state.config.set_key('last_update_check', result)
            app_state.config.set_key('last_update_check_time', when_checked,
                                     True)
        self.update_check_signal.emit(success, result)

    def initial_dialogs(self) -> None:
        '''Suppressible dialogs that are shown when first opening the app.'''
        dialogs.show_named('welcome-ESV-1.3.0')

    def event_loop_started(self) -> None:
        self.cosigner_pool = CosignerPool()
        self.label_sync = LabelSync()
        if app_state.config.get("show_crash_reporter", default=True):
            self.exception_hook = Exception_Hook(self)
        self.timer.start()
        signal.signal(signal.SIGINT, lambda *args: self.quit())
        self.initial_dialogs()
        path = app_state.config.get_cmdline_wallet_filepath()
        if not self.start_new_window(
                path, app_state.config.get('url'), is_startup=True):
            self.quit()

    def run_app(self) -> None:
        when_started = datetime.datetime.now().astimezone().isoformat()
        app_state.config.set_key('previous_start_time',
                                 app_state.config.get("start_time"))
        app_state.config.set_key('start_time', when_started, True)
        self.update_check()

        threading.current_thread().setName('GUI')
        self.timer.setSingleShot(False)
        self.timer.setInterval(500)  # msec
        self.timer.timeout.connect(app_state.device_manager.timeout_clients)

        QTimer.singleShot(0, self.event_loop_started)
        self.exec_()

        logs.remove_handler(self.log_handler)
        # 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.sendEvent(self.clipboard(), event)
        self.tray.hide()

    def run_coro(self, coro, *args, on_done=None):
        '''Run a coroutine.  on_done, if given, is passed the future containing the reuslt or
        exception, and is guaranteed to be called in the context of the GUI thread.
        '''
        def task_done(future):
            self.async_tasks_done.emit()

        future = app_state.async_.spawn(coro, *args, on_done=on_done)
        future.add_done_callback(task_done)
        return future

    def run_in_thread(self, func, *args, on_done=None):
        '''Run func(*args) in a thread.  on_done, if given, is passed the future containing the
        reuslt or exception, and is guaranteed to be called in the context of the GUI
        thread.
        '''
        return self.run_coro(run_in_thread, func, *args, on_done=on_done)
Exemplo n.º 49
0
class SynchronizationDaemon(QObject):
    signal_synchronization_synchronize = pyqtSignal()
    signal_synchronization_started = pyqtSignal()
    signal_synchronization_status_update = pyqtSignal(str)
    signal_synchronization_note_updated = pyqtSignal(str, str, str, int,
                                                     DriveExtendedAction)
    signal_synchronization_finished = pyqtSignal()

    def __init__(self):
        super(SynchronizationDaemon, self).__init__()

        self.signal_synchronization_synchronize.connect(self.synchronize)

        self._synchronize_notes_thread_timer = QTimer()
        self._synchronize_notes_thread = QThread()
        self._synchronize_notes_thread_worker = SynchronizeNotesThreadWorker()

    def enable(self):
        if not self.enabled():
            self._synchronize_notes_thread_timer = QTimer()
            self._synchronize_notes_thread_timer.setTimerType(
                Qt.VeryCoarseTimer)
            self._synchronize_notes_thread_timer.setInterval(60000)
            self._synchronize_notes_thread_timer.timeout.connect(
                self.synchronize)
            self._synchronize_notes_thread_timer.start()

    def disable(self):
        if self.enabled():
            self._synchronize_notes_thread_timer.stop()
            if self.synchronizing(
            ) and self._synchronize_notes_thread_worker.receivers(
                    self._synchronize_notes_thread_worker.signal_restart_timer
            ) > 0:
                self._synchronize_notes_thread_worker.signal_restart_timer.disconnect(
                )

    def enabled(self) -> bool:
        return self._synchronize_notes_thread_timer.isActive()

    def synchronize(self):
        if not self.synchronizing():
            self._synchronize_notes_thread = QThread()
            self._synchronize_notes_thread_worker = SynchronizeNotesThreadWorker(
            )

            self._synchronize_notes_thread.started.connect(
                self.signal_synchronization_started)
            self._synchronize_notes_thread.started.connect(
                self._synchronize_notes_thread_worker.run)
            self._synchronize_notes_thread_worker.signal_synchronization_finished.connect(
                self.signal_synchronization_finished)
            self._synchronize_notes_thread_worker.signal_synchronization_status_update.connect(
                self.signal_synchronization_status_update)
            self._synchronize_notes_thread_worker.signal_synchronization_note_updated.connect(
                self.signal_synchronization_note_updated)
            if self.enabled():
                self._synchronize_notes_thread_worker.signal_restart_timer.connect(
                    self._synchronize_notes_thread_timer.start)
            self._synchronize_notes_thread_worker.signal_synchronization_finished.connect(
                self._synchronize_notes_thread.quit)

            self._synchronize_notes_thread_worker.moveToThread(
                self._synchronize_notes_thread)
            self._synchronize_notes_thread.start()

    def synchronizing(self) -> bool:
        return self._synchronize_notes_thread.isRunning()
Exemplo n.º 50
0
class CuraEngineBackend(QObject, Backend):
    ##  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, parent=None):
        super().__init__(parent=parent)
        # 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
        if os.path.exists(
                os.path.join(Application.getInstallPrefix(), "bin",
                             executable_name)):
            default_engine_location = os.path.join(
                Application.getInstallPrefix(), "bin", executable_name)
        if hasattr(sys, "frozen"):
            default_engine_location = os.path.join(
                os.path.dirname(os.path.abspath(sys.executable)),
                executable_name)
        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 os.getenv("PATH").split(os.pathsep):
                execpath = os.path.join(pathdir, executable_name)
                if os.path.exists(execpath):
                    default_engine_location = execpath
                    break

        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)
        Preferences.getInstance().addPreference("backend/location",
                                                default_engine_location)

        # Workaround to disable layer view processing if layer view is not active.
        self._layer_view_active = False
        Application.getInstance().getController().activeViewChanged.connect(
            self._onActiveViewChanged)
        self._onActiveViewChanged()
        self._stored_layer_data = []
        self._stored_optimized_layer_data = []

        self._scene = Application.getInstance().getController().getScene()
        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, it will set the "_is_error_check_scheduled" flag, 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
        Application.getInstance().globalContainerStackChanged.connect(
            self._onGlobalStackChanged)
        self._onGlobalStackChanged()

        Application.getInstance().stacksValidationFinished.connect(
            self._onStackErrorCheckFinished)

        # A flag indicating if an error check was scheduled
        # If so, we will stop the auto-slice timer and start upon the error check
        self._is_error_check_scheduled = False

        # 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
        self._slicing = False  # Are we currently slicing?
        self._restart = False  # Back-end is currently restarting?
        self._tool_active = False  # If a tool is active, some tasks do not have to do anything
        self._always_restart = True  # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
        self._process_layers_job = None  # The currently active job to process layers, or None if it is not processing layers.
        self._need_slicing = False
        self._engine_is_fresh = True  # Is the newly started engine used before or not?

        self._backend_log_max_lines = 20000  # Maximum number of lines to buffer
        self._error_message = None  # Pop-up message that shows errors.
        self._last_num_objects = 0  # Count number of objects to see if there is something changed
        self._postponed_scene_change_sources = [
        ]  # scene change is postponed (by a tool)

        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.
        Application.getInstance().getController().toolOperationStarted.connect(
            self._onToolOperationStarted)
        Application.getInstance().getController().toolOperationStopped.connect(
            self._onToolOperationStopped)

        self._slice_start_time = None

        Preferences.getInstance().addPreference("general/auto_slice", True)

        self._use_timer = False
        # 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()
        self._change_timer.setSingleShot(True)
        self._change_timer.setInterval(500)
        self.determineAutoSlicing()
        Preferences.getInstance().preferenceChanged.connect(
            self._onPreferencesChanged)

    ##  Terminate the engine process.
    #
    #   This function should terminate the engine process.
    #   Called when closing the application.
    def close(self):
        # 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):
        json_path = Resources.getPath(Resources.DefinitionContainers,
                                      "fdmprinter.def.json")
        return [
            Preferences.getInstance().getValue("backend/location"), "connect",
            "127.0.0.1:{0}".format(self._port), "-j", json_path, ""
        ]

    ##  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):
        self.backendStateChange.emit(BackendState.NotStarted)
        if self._slicing:  # We were already slicing. Stop the old job.
            self._terminate()
            self._createSocket()

        if self._process_layers_job:  # We were processing layers. Stop that, the layers are going to change soon.
            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):
        if self._use_timer:
            self._change_timer.start()
        else:
            self.slice()

    ##  Perform a slice of the scene.
    def slice(self):
        self._slice_start_time = time()
        if not self._need_slicing:
            self.processingProgress.emit(1.0)
            self.backendStateChange.emit(BackendState.Done)
            Logger.log(
                "w",
                "Slice unnecessary, nothing has changed that needs reslicing.")
            return

        self.printDurationMessage.emit(
            {
                "none": 0,
                "inset_0": 0,
                "inset_x": 0,
                "skin": 0,
                "support": 0,
                "skirt": 0,
                "infill": 0,
                "support_infill": 0,
                "travel": 0,
                "retract": 0,
                "support_interface": 0
            }, [0])

        self._stored_layer_data = []
        self._stored_optimized_layer_data = []

        if self._process is None:
            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_list = []
        self._slicing = True
        self.slicingStarted.emit()

        slice_message = self._socket.createMessage("cura.proto.Slice")
        self._start_slice_job = StartSliceJob.StartSliceJob(slice_message)
        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):
        self._slicing = False
        self._stored_layer_data = []
        self._stored_optimized_layer_data = []
        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 Application.getInstance().getCommandLineOption(
                "external-backend", False):
            return

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

            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):
        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(
        ) == StartSliceJob.StartJobResult.Error:
            return

        if job.getResult(
        ) == StartSliceJob.StartJobResult.MaterialIncompatible:
            if Application.getInstance().platformActivity:
                self._error_message = Message(
                    catalog.i18nc(
                        "@info:status",
                        "The selected material is incompatible with the selected machine or configuration."
                    ))
                self._error_message.show()
                self.backendStateChange.emit(BackendState.Error)
            else:
                self.backendStateChange.emit(BackendState.NotStarted)
            return

        if job.getResult() == StartSliceJob.StartJobResult.SettingError:
            if Application.getInstance().platformActivity:
                extruders = list(
                    ExtruderManager.getInstance().getMachineExtruders(
                        self._global_container_stack.getId()))
                error_keys = []
                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 = 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)

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

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

        if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice:
            if Application.getInstance().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."
                    ))
                self._error_message.show()
                self.backendStateChange.emit(BackendState.Error)
            else:
                self.backendStateChange.emit(BackendState.NotStarted)
            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.backendStateChange.emit(BackendState.Processing)

        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):
        enable_timer = True

        if not Preferences.getInstance().getValue("general/auto_slice"):
            enable_timer = False
        for node in DepthFirstIterator(self._scene.getRoot()):
            if node.callDecoration("isBlockSlicing"):
                enable_timer = False
                self.backendStateChange.emit(BackendState.Disabled)
            gcode_list = node.callDecoration("getGCodeList")
            if gcode_list is not None:
                self._scene.gcode_list = gcode_list

        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

    ##  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):
        if type(source) is not SceneNode:
            return

        root_scene_nodes_changed = False
        if source == self._scene.getRoot():
            num_objects = 0
            for node in DepthFirstIterator(self._scene.getRoot()):
                # Only count sliceable objects
                if node.callDecoration("isSliceable"):
                    num_objects += 1
            if num_objects != self._last_num_objects:
                self._last_num_objects = num_objects
                root_scene_nodes_changed = True
            else:
                return

        if not source.callDecoration(
                "isGroup") and not root_scene_nodes_changed:
            if source.getMeshData() is None:
                return
            if source.getMeshData().getVertices() is None:
                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.needsSlicing()
        self.stopSlicing()
        self._onChanged()

    ##  Called when an error occurs in the socket connection towards the engine.
    #
    #   \param error The exception that occurred.
    def _onSocketError(self, error):
        if Application.getInstance().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")

    ##  Remove old layer data (if any)
    def _clearLayerData(self):
        for node in DepthFirstIterator(self._scene.getRoot()):
            if node.callDecoration("getLayerData"):
                node.getParent().removeChild(node)
                break

    ##  Convenient function: set need_slicing, emit state and clear layer data
    def needsSlicing(self):
        self._need_slicing = True
        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()

    ##  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, property):
        if property == "value":  # Only reslice if the value has changed.
            self.needsSlicing()
            self._onChanged()

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

    def _onStackErrorCheckFinished(self):
        self._is_error_check_scheduled = False
        if self._need_slicing:
            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):
        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):
        self._stored_optimized_layer_data.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):
        self.processingProgress.emit(message.amount)
        self.backendStateChange.emit(BackendState.Processing)

    ##  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):
        self.backendStateChange.emit(BackendState.Done)
        self.processingProgress.emit(1.0)

        for line in self._scene.gcode_list:
            replaced = line.replace(
                "{print_time}",
                str(Application.getInstance().getPrintInformation().
                    currentPrintTime.getDisplayString(
                        DurationFormat.Format.ISO8601)))
            replaced = replaced.replace(
                "{filament_amount}",
                str(Application.getInstance().getPrintInformation().
                    materialLengths))
            replaced = replaced.replace(
                "{filament_weight}",
                str(Application.getInstance().getPrintInformation().
                    materialWeights))
            replaced = replaced.replace(
                "{filament_cost}",
                str(Application.getInstance().getPrintInformation().
                    materialCosts))
            replaced = replaced.replace(
                "{jobname}",
                str(Application.getInstance().getPrintInformation().jobName))

            self._scene.gcode_list[self._scene.gcode_list.index(
                line)] = replaced

        self._slicing = False
        self._need_slicing = False
        Logger.log("d", "Slicing took %s seconds",
                   time() - self._slice_start_time)
        if self._layer_view_active and (
                self._process_layers_job is None
                or not self._process_layers_job.isRunning()):
            self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(
                self._stored_optimized_layer_data)
            self._process_layers_job.finished.connect(
                self._onProcessLayersFinished)
            self._process_layers_job.start()
            self._stored_optimized_layer_data = []

    ##  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):
        self._scene.gcode_list.append(message.data.decode("utf-8", "replace"))

    ##  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):
        self._scene.gcode_list.insert(0,
                                      message.data.decode("utf-8", "replace"))

    ##  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):
        material_amounts = []
        for index in range(message.repeatedMessageCount("materialEstimates")):
            material_amounts.append(
                message.getRepeatedMessage("materialEstimates",
                                           index).material_amount)
        feature_times = {
            "none": message.time_none,
            "inset_0": message.time_inset_0,
            "inset_x": message.time_inset_x,
            "skin": message.time_skin,
            "support": message.time_support,
            "skirt": message.time_skirt,
            "infill": message.time_infill,
            "support_infill": message.time_support_infill,
            "travel": message.time_travel,
            "retract": message.time_retract,
            "support_interface": message.time_support_interface
        }
        self.printDurationMessage.emit(feature_times, material_amounts)

    ##  Creates a new socket connection.
    def _createSocket(self):
        super()._createSocket(
            os.path.abspath(
                os.path.join(
                    PluginRegistry.getInstance().getPluginPath(
                        self.getPluginId()), "Cura.proto")))
        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, **kwargs):
        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._is_error_check_scheduled:
                self._change_timer.stop()
            else:
                self._change_timer.start()

    ##  Called when the back-end connects to the front-end.
    def _onBackendConnected(self):
        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):
        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):
        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)

    ##  Called when the user changes the active view mode.
    def _onActiveViewChanged(self):
        if Application.getInstance().getController().getActiveView():
            view = Application.getInstance().getController().getActiveView()
            if view.getPluginId(
            ) == "LayerView":  # 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.
                if self._stored_optimized_layer_data and not self._slicing:
                    self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(
                        self._stored_optimized_layer_data)
                    self._process_layers_job.finished.connect(
                        self._onProcessLayersFinished)
                    self._process_layers_job.start()
                    self._stored_optimized_layer_data = []
            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):
        if not self._restart:
            if self._process:
                Logger.log(
                    "d",
                    "Backend quit with return code %s. Resetting process and socket.",
                    self._process.wait())
                self._process = None

    ##  Called when the global container stack changes
    def _onGlobalStackChanged(self):
        if self._global_container_stack:
            self._global_container_stack.propertyChanged.disconnect(
                self._onSettingChanged)
            self._global_container_stack.containersChanged.disconnect(
                self._onChanged)
            extruders = list(ExtruderManager.getInstance().getMachineExtruders(
                self._global_container_stack.getId()))

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

        self._global_container_stack = Application.getInstance(
        ).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(ExtruderManager.getInstance().getMachineExtruders(
                self._global_container_stack.getId()))
            for extruder in extruders:
                extruder.propertyChanged.connect(self._onSettingChanged)
                extruder.containersChanged.connect(self._onChanged)
            self._onChanged()

    def _onProcessLayersFinished(self, job):
        self._process_layers_job = None

    ##  Connect slice function to timer.
    def enableTimer(self):
        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):
        if self._use_timer:
            self._use_timer = False
            self._change_timer.timeout.disconnect(self.slice)

    def _onPreferencesChanged(self, preference):
        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):
        if self._use_timer:
            self._change_timer.start()
Exemplo n.º 51
0
class RespirationRateWidget(PyQtGraphWidget):
    def __init__(self, take_ft_at_seconds=60):
        super().__init__()
        self.take_ft_at_seconds = take_ft_at_seconds
        self.respiration_plot = self.canvas.addPlot(row=0, col=0)
        self.respiration_plot.setTitle("Respiration")

        self.setWindowTitle("Respiration Monitor")

        self.respiration_line = self.respiration_plot.plot(pen="r")

        self.fft_plot = self.canvas.addPlot(row=1, col=0)
        self.fft_plot.setTitle("FFT")
        #self.fft_plot.setYRange(0, 2000, padding=0)
        self.fft_line = self.fft_plot.plot(pen='r')

        self._data = np.zeros((MAX_STORE, ))

        self._time = INVALID_TIME * np.ones((MAX_STORE, ))

        self.ft_times = [self.take_ft_at_seconds]
        self.ft_maxes = []

        self.current_index = 0
        self.update_frequency = 20
        self._current_time = 0
        self.plot_timer = QTimer()
        self.plot_timer.timeout.connect(self.plot)
        self.plot_timer.setInterval(1000.0 / self.update_frequency)
        self.last_time = time.perf_counter()

        self.ft_frequency = np.arange(0, HALF_MAX_STORE,
                                      1) / self.take_ft_at_seconds

    def append(self, t, value):

        in_seconds = t / 1000.0

        self._time[self.current_index] = in_seconds
        self._data[self.current_index] = value
        self._current_time = in_seconds
        self.current_index = (self.current_index + 1) % MAX_STORE

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

    # def update_line(self):
    #     return self._data

    #  todo - FFT does not work when Arduino has a delay. The x and y shapes do not much up. Put on a plaster on it
    #         that causes it to not work after the first time window :(
    def plot(self):

        if self._current_time == 0:
            return

        try:
            self._plot()
        except Exception as e:
            self.dump_data(reason=e.__str__())

    def reset(self):
        self._data = np.zeros_like(self._data)
        self._time = np.zeros_like(self._time)
        self._current_time = 0
        self.current_index = 0

        self.plot()

    def showEvent(self, event):
        self.plot_timer.start()

    def dump_data(self, reason=''):
        time_stamp = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
        filename = "respiration_rate_widget_data_{}.txt".format(time_stamp)

        data = np.vstack((self._time, self._data)).T
        np.savetxt(filename, data, header=reason)

    def _plot(self):

        time_window_start = self._current_time - self.take_ft_at_seconds
        x_min = max(0.0, time_window_start)
        x_max = max(self.take_ft_at_seconds, self._current_time)

        self.respiration_plot.setRange(xRange=[x_min, x_max])

        # copying to avoid potential race conditions from c++ QT timer. See if okay for a while.
        debug_time = self._time.copy()
        debug_data = self._data.copy()

        indices = np.where(
            np.logical_and(debug_time >= x_min, debug_time <= x_max))
        time = np.take(debug_time, indices[0])
        data = np.take(debug_data, indices[0])
        self.respiration_line.setData(time, data)

        averaged_data = data - np.average(data)

        # interpolate onto uniform t for FFT.
        min_delta_time = np.diff(time).min()
        uniform_time = np.arange(time.min(), time.max(), min_delta_time)
        uniform_data = np.interp(uniform_time, time, averaged_data)

        ft = fft(uniform_data)
        num_samples = ft.shape[0]
        half_num_samples = int(num_samples / 2)
        self.ft_frequency = np.linspace(0.0, 1.0 / (2.0 * min_delta_time),
                                        half_num_samples)

        ft_result = (2.0 / num_samples) * np.abs(ft[:half_num_samples])

        self.fft_line.setData(self.ft_frequency, ft_result)

        self.fft_plot.setRange(yRange=[ft_result.min(), ft_result.max()])
Exemplo n.º 52
0
class ElectrumGui(Logger):
    @profiler
    def __init__(self, config, daemon, 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.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.png"))
        # timer
        self.timer = QTimer(self.app)
        self.timer.setSingleShot(False)
        self.timer.setInterval(500)  # msec

        self.nd = 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('Noir 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()
        for window in self.windows:
            submenu = m.addMenu(window.wallet.basename())
            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()

    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_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.nd:
            self.nd.on_update()
            self.nd.show()
            self.nd.raise_()
            return
        self.nd = NetworkDialog(self.daemon.network, self.config,
                                self.network_updated_signal_obj)
        self.nd.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' +
                               str(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' +
                                   str(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' + str(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)
        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()
        self.config.open_last_wallet()
        path = self.config.get_wallet_path()
        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
            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()
Exemplo n.º 53
0
class DataUpdateUi(QWidget):
    task_finish_signal = pyqtSignal([UpdateTask])
    refresh_finish_signal = pyqtSignal()

    INDEX_CHECK = 0
    INDEX_ITEM = 1
    INDEX_STATUS = 8
    TABLE_HEADER = ['', 'Item', 'Local Data Since', 'Local Data Until', 'Latest Update',
                    'Update Estimation', 'Sub Update', 'Update', 'Status']

    # TODO: Auto detect
    INCLUDES_SECURITIES_SUB_UPDATE_LIST = [
        'Finance.Audit', 'Finance.BalanceSheet', 'Finance.IncomeStatement', 'Finance.CashFlowStatement',
        'Stockholder.PledgeStatus', 'Stockholder.PledgeHistory']

    def __init__(self, data_hub_entry: DataHubEntry, update_table: UpdateTableEx):
        super(DataUpdateUi, self).__init__()

        # Access entry
        self.__data_hub = data_hub_entry
        self.__data_center = self.__data_hub.get_data_center()
        self.__update_table = update_table

        # Table content
        self.__display_uri = []
        self.__display_identities = None
        self.__display_table_lines = []

        # Page related
        self.__page = 0
        self.__item_per_page = 20

        # For processing updating
        self.__processing_update_tasks = []
        # Fot task counting
        self.__processing_update_tasks_count = []

        self.task_finish_signal.connect(self.__on_task_done)
        self.refresh_finish_signal.connect(self.update_table_display)

        # Timer for update status
        self.__timer = QTimer()
        self.__timer.setInterval(1000)
        self.__timer.timeout.connect(self.on_timer)
        self.__timer.start()

        # UI related
        self.__info_panel = QLabel(DEFAULT_INFO)

        self.__table_main = EasyQTableWidget()
        self.__button_head_page = QPushButton('<<')
        self.__button_prev_page = QPushButton('<')
        self.__button_next_page = QPushButton('>')
        self.__button_tail_page = QPushButton('>>')
        self.__button_upper_level = QPushButton('↑')

        self.__button_refresh = QPushButton('Refresh')
        self.__button_batch_auto_update = QPushButton('Auto Update Select')
        self.__button_batch_force_update = QPushButton('Force Update Select')

        self.init_ui()

        # Post update and cache stock list after posting RefreshTask
        data_utility = self.__data_hub.get_data_utility()
        StockAnalysisSystem().get_task_queue().append_task(UpdateStockListTask(data_utility))

    # ---------------------------------------------------- UI Init -----------------------------------------------------

    def init_ui(self):
        self.__layout_control()
        self.__config_control()
        self.__to_top_level()

    def __layout_control(self):
        main_layout = QVBoxLayout()
        self.setLayout(main_layout)
        self.setMinimumSize(600, 400)
        main_layout.addWidget(self.__table_main)

        bottom_control_area = QHBoxLayout()
        main_layout.addLayout(bottom_control_area)

        bottom_right_area = QVBoxLayout()
        bottom_control_area.addWidget(self.__info_panel, 99)
        bottom_control_area.addLayout(bottom_right_area, 0)

        line = horizon_layout([self.__button_head_page, self.__button_prev_page,
                               self.__button_next_page, self.__button_tail_page,
                               self.__button_upper_level, self.__button_refresh])
        bottom_right_area.addLayout(line)

        line = horizon_layout([self.__button_batch_auto_update, self.__button_batch_force_update])
        bottom_right_area.addLayout(line)

    def __config_control(self):
        for _ in DataUpdateUi.TABLE_HEADER:
            self.__table_main.insertColumn(0)
        self.__table_main.setHorizontalHeaderLabels(DataUpdateUi.TABLE_HEADER)
        self.__table_main.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)

        self.__button_head_page.clicked.connect(partial(self.on_page_control, '<<'))
        self.__button_prev_page.clicked.connect(partial(self.on_page_control, '<'))
        self.__button_next_page.clicked.connect(partial(self.on_page_control, '>'))
        self.__button_tail_page.clicked.connect(partial(self.on_page_control, '>>'))
        self.__button_upper_level.clicked.connect(partial(self.on_page_control, '^'))
        self.__button_refresh.clicked.connect(partial(self.on_page_control, 'r'))

        self.__button_batch_auto_update.clicked.connect(partial(self.on_batch_update, False))
        self.__button_batch_force_update.clicked.connect(partial(self.on_batch_update, True))

    def on_detail_button(self, uri: str):
        print('Detail of ' + uri)
        self.__page = 0
        self.__to_detail_level(uri)

    def on_auto_update_button(self, uri: str, identity: str):
        print('Auto update ' + uri + ':' + str(identity))
        self.__build_post_update_task(uri, None, False)

    def on_force_update_button(self, uri: str, identity: str):
        print('Force update ' + uri + ':' + str(identity))
        self.__build_post_update_task(uri, None, True)

    def on_batch_update(self, force: bool):
        for i in range(0, self.__table_main.rowCount()):
            item_check = self.__table_main.item(i, DataUpdateUi.INDEX_CHECK)
            if item_check.checkState() == Qt.Checked:
                item_id = self.__table_main.item(i, DataUpdateUi.INDEX_ITEM).text()
                # A little ugly...To distinguish it's uri or securities ideneity
                if self.__display_identities is None:
                    self.__build_post_update_task(item_id, None, force)
                else:
                    self.__build_post_update_task(self.__display_uri[0], item_id, force)

    def on_page_control(self, control: str):
        # data_utility = self.__data_hub.get_data_utility()
        # stock_list = data_utility.get_stock_list()
        # max_page = len(stock_list) // self.__item_per_page

        if self.__display_identities is None:
            max_item_count = len(self.__display_uri)
        else:
            max_item_count = len(self.__display_identities)
        max_page = max_item_count // self.__item_per_page

        if control == '<<':
            self.__page = 0
        elif control == '<':
            self.__page = max(self.__page - 1, 0)
        elif control == '>':
            self.__page = min(self.__page + 1, max_page)
        elif control == '>>':
            self.__page = max_page
        elif control == '^':
            self.__to_top_level()

        if control in ['<<', '<', '>', '>>', '^', 'r']:
            self.update_table()

    def on_timer(self):
        for i in range(0, self.__table_main.rowCount()):
            item_id = self.__table_main.item(i, DataUpdateUi.INDEX_ITEM).text()
            # A little ugly...To distinguish it's uri or securities identity
            if self.__display_identities is None:
                uri = item_id
                prog_id = uri
            else:
                uri = self.__display_uri[0]
                prog_id = [uri, item_id]
            for task in self.__processing_update_tasks:
                if not task.in_work_package(uri):
                    continue
                if task.progress.has_progress(prog_id):
                    rate = task.progress.get_progress_rate(prog_id)
                    status = '%ss | %.2f%%' % (task.clock.elapsed_s(), rate * 100)
                    self.__table_main.item(i, DataUpdateUi.INDEX_STATUS).setText(status)
                else:
                    self.__table_main.item(i, DataUpdateUi.INDEX_STATUS).setText('等待中...')
                break

    # def closeEvent(self, event):
    #     if self.__task_thread is not None:
    #         QMessageBox.information(self,
    #                                 QtCore.QCoreApplication.translate('', '无法关闭窗口'),
    #                                 QtCore.QCoreApplication.translate('', '更新过程中无法关闭此窗口'),
    #                                 QMessageBox.Close, QMessageBox.Close)
    #         event.ignore()
    #     else:
    #         event.accept()

    # ---------------------------------------- Table Update ----------------------------------------

    def update_table(self):
        self.__table_main.clear()
        self.__table_main.setRowCount(0)
        self.__table_main.setHorizontalHeaderLabels(DataUpdateUi.TABLE_HEADER)
        self.__table_main.AppendRow(['', '刷新中...', '', '', '', '', '', '', ''])
        task = RefreshTask(self)
        StockAnalysisSystem().get_task_queue().append_task(task)

    def update_table_display(self):
        self.__table_main.clear()
        self.__table_main.setRowCount(0)
        self.__table_main.setHorizontalHeaderLabels(DataUpdateUi.TABLE_HEADER)

        for line in self.__display_table_lines:
            self.__table_main.AppendRow(line)
            index = self.__table_main.rowCount() - 1

            # Add check box
            check_item = QTableWidgetItem()
            check_item.setCheckState(QtCore.Qt.Unchecked)
            self.__table_main.setItem(index, 0, check_item)

            # Add detail button
            if line[1] in DataUpdateUi.INCLUDES_SECURITIES_SUB_UPDATE_LIST:
                button = QPushButton('Enter')
                button.clicked.connect(partial(self.on_detail_button, line[1]))
                self.__table_main.AddWidgetToCell(index, 6, button)

            # Add update button
            button_auto = QPushButton('Auto')
            button_force = QPushButton('Force')
            button_auto.clicked.connect(partial(self.on_auto_update_button, line[1], None))
            button_force.clicked.connect(partial(self.on_force_update_button, line[1], None))
            self.__table_main.AddWidgetToCell(index, 7, [button_auto, button_force])

    def update_table_content(self):
        contents = []
        count = self.__item_per_page
        offset = self.__page * self.__item_per_page

        for uri in self.__display_uri:
            update_details = self.__display_identities if \
                self.__display_identities is not None else [None]
            for index in range(offset, offset + count):
                if index >= len(update_details):
                    break
                line = self.generate_line_content(uri, update_details[index])
                if line is not None:
                    contents.append(line)
        self.__display_table_lines = contents

    def generate_line_content(self, uri: str, identity: str or None) -> [list] or None:
        line = []

        data_table, _ = self.__data_center.get_data_table(uri)
        if data_table is None:
            return None

        since, until = data_table.range(uri, None)
        update_since, update_until = self.__data_center.calc_update_range(uri)

        update_tags = uri.split('.')
        latest_update = self.__update_table.get_last_update_time(update_tags)

        line.append('')     # Place holder for check box
        line.append(identity if str_available(identity) else uri)
        line.append(date2text(since) if since is not None else ' - ')
        line.append(date2text(until) if until is not None else ' - ')
        line.append(date2text(latest_update) if latest_update is not None else ' - ')

        if update_since is not None and update_until is not None:
            line.append(date2text(update_since) + ' - ' + date2text(update_until))
        else:
            line.append(' - ')
        line.append('-')    # Place holder for detail button
        line.append('')     # Place holder for update button
        line.append('')     # Place holder for status

        return line

    # def update_table(self):
    #     if self.__current_uri == '':
    #         self.update_uri_level()
    #     else:
    #         self.update_identity_level(self.__current_uri, self.__page * self.__item_per_page, self.__item_per_page)
    #
    # def update_uri_level(self):
    #     self.__table_main.clear()
    #     self.__table_main.setRowCount(0)
    #     self.__table_main.setHorizontalHeaderLabels(DataUpdateUi.TABLE_HEADER_URI)
    #
    #     for declare in DATA_FORMAT_DECLARE:
    #         line = []
    #         uri = declare[0]
    #         data_table, _ = self.__data_center.get_data_table(uri)
    #
    #         # TODO: Fetching finance data's date range spends a lost of time because the data is huge.
    #         since, until = data_table.range(uri, None)
    #         update_since, update_until = self.__data_center.calc_update_range(uri)
    #
    #         update_tags = uri.split('.')
    #         latest_update = self.__update_table.get_last_update_time(update_tags)
    #
    #         line.append('')     # Place holder for check box
    #         line.append(uri)
    #         line.append(date2text(since) if since is not None else ' - ')
    #         line.append(date2text(until) if until is not None else ' - ')
    #         line.append(date2text(latest_update) if latest_update is not None else ' - ')
    #
    #         if update_since is not None and update_until is not None:
    #             line.append(date2text(update_since) + ' - ' + date2text(update_until))
    #         else:
    #             line.append(' - ')
    #         line.append('-')    # Place holder for detail button
    #         line.append('')     # Place holder for update button
    #         line.append('')     # Place holder for status
    #
    #         self.__table_main.AppendRow(line)
    #         index = self.__table_main.rowCount() - 1
    #
    #         # Add check box
    #         check_item = QTableWidgetItem()
    #         check_item.setCheckState(QtCore.Qt.Unchecked)
    #         self.__table_main.setItem(index, 0, check_item)
    #
    #         # Add detail button
    #         if uri in DataUpdateUi.INCLUDES_SECURITIES_SUB_UPDATE_LIST:
    #             button = QPushButton('Enter')
    #             button.clicked.connect(partial(self.on_detail_button, uri))
    #             self.__table_main.AddWidgetToCell(index, 6, button)
    #
    #         # Add update button
    #         button_auto = QPushButton('Auto')
    #         button_force = QPushButton('Force')
    #         button_auto.clicked.connect(partial(self.on_auto_update_button, uri, None))
    #         button_force.clicked.connect(partial(self.on_force_update_button, uri, None))
    #         self.__table_main.AddWidgetToCell(index, 7, [button_auto, button_force])
    #
    # def update_identity_level(self, uri: str, offset: int, count: int):
    #     if uri == '':
    #         self.update_uri_level()
    #         return
    #
    #     self.__table_main.clear()
    #     self.__table_main.setRowCount(0)
    #     self.__table_main.setHorizontalHeaderLabels(DataUpdateUi.TABLE_HEADER_IDENTITY)
    #
    #     data_utility = self.__data_hub.get_data_utility()
    #     stock_list = data_utility.get_stock_list()
    #
    #     for index in range(offset, offset + count):
    #         if index >= len(stock_list):
    #             break
    #
    #         stock_identity, name = stock_list[index]
    #         data_table, _ = self.__data_center.get_data_table(uri)
    #
    #         since, until = data_table.range(uri, stock_identity)
    #         update_since, update_until = self.__data_center.calc_update_range(uri, stock_identity)
    #
    #         update_tags = uri.split('.')
    #         update_tags.append(stock_identity.replace('.', '_'))
    #         latest_update = self.__update_table.get_last_update_time(update_tags)
    #
    #         line = []
    #         line.append('')     # Place holder for check box
    #         line.append(stock_identity)
    #         line.append(date2text(since) if since is not None else ' - ')
    #         line.append(date2text(until) if until is not None else ' - ')
    #         line.append(date2text(latest_update) if latest_update is not None else ' - ')
    #
    #         if update_since is not None and update_until is not None:
    #             line.append(date2text(update_since) + ' - ' + date2text(update_until))
    #         else:
    #             line.append(' - ')
    #         line.append('')     # Place holder for update button
    #         line.append('')     # Place holder for status
    #
    #         self.__table_main.AppendRow(line)
    #         index = self.__table_main.rowCount() - 1
    #
    #         # Add check box
    #         check_item = QTableWidgetItem()
    #         check_item.setCheckState(QtCore.Qt.Unchecked)
    #         self.__table_main.setItem(index, 0, check_item)
    #
    #         # Add update button
    #         button_auto = QPushButton('Auto')
    #         button_force = QPushButton('Force')
    #         button_auto.clicked.connect(partial(self.on_auto_update_button, uri, stock_identity))
    #         button_force.clicked.connect(partial(self.on_force_update_button, uri, stock_identity))
    #         self.__table_main.AddWidgetToCell(index, 6, [button_auto, button_force])

    # --------------------------------------------------------------------------

    def __to_top_level(self):
        self.__display_uri = [declare[0] for declare in DATA_FORMAT_DECLARE]
        self.__display_identities = None
        self.__page = 0
        self.update_table()

    def __to_detail_level(self, uri: str):
        self.__display_uri = [uri]
        if uri in ['Market.TradeCalender']:
            self.__display_identities = ['SSE']
        elif uri in DataUpdateUi.INCLUDES_SECURITIES_SUB_UPDATE_LIST:
            data_utility = self.__data_hub.get_data_utility()
            self.__display_identities = data_utility.get_stock_identities()
        self.__page = 0
        self.update_table()

    def __build_post_update_task(self, uri: str, identities: list or None, force: bool) -> bool:
        task = UpdateTask(self, self.__data_hub, self.__data_center, force)
        if identities is None:
            if uri == 'Market.TradeCalender':
                identities = 'SSE'
            elif uri in DataUpdateUi.INCLUDES_SECURITIES_SUB_UPDATE_LIST:
                data_utility = self.__data_hub.get_data_utility()
                identities = data_utility.get_stock_identities()
        task.set_work_package(uri, identities)
        self.__processing_update_tasks.append(task)
        self.__processing_update_tasks_count.append(task)
        ret = StockAnalysisSystem().get_task_queue().append_task(task)
        # After updating market info, also update stock list cache
        if ret and uri == 'Market.SecuritiesInfo':
            data_utility = self.__data_hub.get_data_utility()
            StockAnalysisSystem().get_task_queue().append_task(UpdateStockListTask(data_utility))
        return ret

    # def __work_around_for_update_pack(self):
    #     for i in range(0, len(self.__update_pack)):
    #         if self.__update_pack[i][0] == 'Market.TradeCalender':
    #             self.__update_pack[i][1] = ['SSE']
    #         elif self.__update_pack[i][0] in DataUpdateUi.INCLUDES_SECURITIES_SUB_UPDATE_LIST:
    #             if self.__update_pack[i][1] is None:
    #                 data_utility = self.__data_hub.get_data_utility()
    #                 stock_list = data_utility.get_stock_identities()
    #                 self.__update_pack[i][1] = stock_list

    # --------------------------------- Thread ---------------------------------

    # ------------------------- Refresh Task -------------------------

    # def execute_refresh_task(self):
    #     if self.__refresh_thread is None:
    #         self.__refresh_thread = threading.Thread(target=self.refresh_task)
    #         self.__refresh_thread.start()
    #
    # def refresh_task(self):
    #     print('Refresh task start.')
    #     self.update_table_content()
    #     self.__refresh_thread = None
    #     self.refresh_finish_signal.emit()
    #     print('Refresh task finished.')

    # ----------------------- Data Update Task ----------------------

    # def execute_update_task(self):
    #     if self.__refresh_thread is not None:
    #         QMessageBox.information(self,
    #                                 QtCore.QCoreApplication.translate('', '无法执行'),
    #                                 QtCore.QCoreApplication.translate('', '列表刷新中,无法执行数据更新'),
    #                                 QMessageBox.Close, QMessageBox.Close)
    #         return
    #
    #     self.__work_around_for_update_pack()
    #     if self.__task_thread is None:
    #         self.__task_thread = threading.Thread(target=self.update_task)
    #         StockAnalysisSystem().lock_sys_quit()
    #         self.__task_thread.start()
    #     else:
    #         print('Task already running...')
    #         QMessageBox.information(self,
    #                                 QtCore.QCoreApplication.translate('', '无法执行'),
    #                                 QtCore.QCoreApplication.translate('', '已经有更新在运行中,无法同时运行多个更新'),
    #                                 QMessageBox.Close, QMessageBox.Close)
    #
    # def update_task(self):
    #     print('Update task start.')
    #
    #     self.__lock.acquire()
    #     task = copy.deepcopy(self.__update_pack)
    #     force = self.__update_force
    #     self.__lock.release()
    #
    #     self.__timing_clock.reset()
    #     self.__progress_rate.reset()
    #     for uri, identities in task:
    #         if identities is not None:
    #             self.__progress_rate.set_progress(uri, 0, len(identities))
    #             for identity in identities:
    #                 self.__progress_rate.set_progress([uri, identity], 0, 1)
    #         else:
    #             self.__progress_rate.set_progress(uri, 0, 1)
    #
    #     for uri, identities in task:
    #         if identities is not None:
    #             for identity in identities:
    #                 # Optimise: Update not earlier than listing date.
    #                 listing_date = self.__data_hub.get_data_utility().get_stock_listing_date(identity, default_since())
    #
    #                 if force:
    #                     since, until = listing_date, now()
    #                 else:
    #                     since, until = self.__data_center.calc_update_range(uri, identity)
    #                     since = max(listing_date, since)
    #
    #                 self.__data_center.update_local_data(uri, identity, (since, until))
    #                 self.__progress_rate.increase_progress([uri, identity])
    #                 self.__progress_rate.increase_progress(uri)
    #         else:
    #             self.__data_center.update_local_data(uri, force=force)
    #             self.__progress_rate.increase_progress(uri)
    #
    #     self.task_finish_signal.emit()
    #     print('Update task finished.')

    # ---------------------------------------------------------------------------------

    def __on_task_done(self, task: UpdateTask):
        if task in self.__processing_update_tasks_count:
            self.__processing_update_tasks_count.remove(task)
            if len(self.__processing_update_tasks_count) == 0:
                QMessageBox.information(self,
                                        QtCore.QCoreApplication.translate('main', '更新完成'),
                                        QtCore.QCoreApplication.translate('main', '数据更新完成'),
                                        QMessageBox.Ok, QMessageBox.Ok)
                self.__processing_update_tasks.clear()
                self.update_table()
        else:
            print('Impossible: Cannot find finished task in task list.')
Exemplo n.º 54
0
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.setEnabled(True)
        MainWindow.setGeometry(450, 50, 480, 700)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.mainlayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.mainlayout.setObjectName("mainlayout")
        self.topgroupBox = QtWidgets.QGroupBox(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Comic Sans MS")
        font.setPointSize(12)
        self.topgroupBox.setFont(font)
        self.topgroupBox.setStyleSheet("background-color:#fcc324\n" "")
        self.topgroupBox.setObjectName("topgroupBox")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.topgroupBox)
        self.verticalLayout.setObjectName("verticalLayout")
        self.topLayout = QtWidgets.QHBoxLayout()
        self.topLayout.setObjectName("topLayout")
        self.progressBar = QtWidgets.QProgressBar(self.topgroupBox)
        self.progressBar.setProperty("value", 0)
        self.progressBar.setTextVisible(False)
        self.progressBar.setStyleSheet(style.progressbarStyle())
        self.progressBar.setObjectName("progressBar")
        self.topLayout.addWidget(self.progressBar)
        self.verticalLayout.addLayout(self.topLayout)
        self.middleLayout = QtWidgets.QHBoxLayout()
        self.middleLayout.setContentsMargins(0, -1, -1, -1)
        self.middleLayout.setObjectName("middleLayout")
        self.middleLayout.addStretch()
        self.addButton = QtWidgets.QToolButton(self.topgroupBox)
        self.addButton.setStyleSheet("border:0\n" "\n" "")
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("assests/add.png"), QtGui.QIcon.Normal,
                       QtGui.QIcon.Off)
        self.addButton.setIcon(icon)
        self.addButton.setIconSize(QtCore.QSize(48, 48))
        self.addButton.setObjectName("addButton")
        self.middleLayout.addWidget(self.addButton)
        self.shuffleButton = QtWidgets.QToolButton(self.topgroupBox)
        self.shuffleButton.setStyleSheet("border:0")
        icon1 = QtGui.QIcon()
        icon1.addPixmap(QtGui.QPixmap("assests/shuffle.png"),
                        QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.shuffleButton.setIcon(icon1)
        self.shuffleButton.setIconSize(QtCore.QSize(48, 48))
        self.shuffleButton.setObjectName("shuffleButton")
        self.middleLayout.addWidget(self.shuffleButton)
        self.playButton = QtWidgets.QToolButton(self.topgroupBox)
        self.playButton.setStyleSheet("border:0")
        icon2 = QtGui.QIcon()
        icon2.addPixmap(QtGui.QPixmap("assests/play.png"), QtGui.QIcon.Normal,
                        QtGui.QIcon.Off)
        self.playButton.setIcon(icon2)
        self.playButton.setIconSize(QtCore.QSize(64, 64))
        self.playButton.setObjectName("playButton")
        self.middleLayout.addWidget(self.playButton)
        self.previousButton = QtWidgets.QToolButton(self.topgroupBox)
        self.previousButton.setStyleSheet("border:0\n" "")
        icon3 = QtGui.QIcon()
        icon3.addPixmap(QtGui.QPixmap("assests/previous.png"),
                        QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.previousButton.setIcon(icon3)
        self.previousButton.setIconSize(QtCore.QSize(48, 48))
        self.previousButton.setObjectName("previousButton")
        self.middleLayout.addWidget(self.previousButton)
        self.nextButton = QtWidgets.QToolButton(self.topgroupBox)
        self.nextButton.setStyleSheet("border:0")
        icon4 = QtGui.QIcon()
        icon4.addPixmap(QtGui.QPixmap("assests/next.png"), QtGui.QIcon.Normal,
                        QtGui.QIcon.Off)
        self.nextButton.setIcon(icon4)
        self.nextButton.setIconSize(QtCore.QSize(48, 48))
        self.nextButton.setObjectName("nextButton")
        self.middleLayout.addWidget(self.nextButton)
        self.volumeSlider = QtWidgets.QSlider(self.topgroupBox)
        self.volumeSlider.setOrientation(QtCore.Qt.Horizontal)
        self.volumeSlider.setObjectName("volumeSlider")
        self.middleLayout.addWidget(self.volumeSlider)
        self.muteButton = QtWidgets.QToolButton(self.topgroupBox)
        self.muteButton.setStyleSheet("border:0")
        icon5 = QtGui.QIcon()
        icon5.addPixmap(QtGui.QPixmap("assests/mute.png"), QtGui.QIcon.Normal,
                        QtGui.QIcon.Off)
        self.muteButton.setIcon(icon5)
        self.muteButton.setIconSize(QtCore.QSize(24, 24))
        self.muteButton.setObjectName("muteButton")
        self.middleLayout.addStretch()

        self.middleLayout.addWidget(self.muteButton)
        self.verticalLayout.addLayout(self.middleLayout)
        self.mainlayout.addWidget(self.topgroupBox, 25)
        self.bottomLayout = QtWidgets.QVBoxLayout()
        self.bottomLayout.setSizeConstraint(
            QtWidgets.QLayout.SetDefaultConstraint)
        self.bottomLayout.setObjectName("bottomLayout")
        self.playlist = QtWidgets.QListWidget(self.centralwidget)
        self.playlist.setObjectName("playlist")
        self.playlist.doubleClicked.connect(self.playsong_1)
        self.bottomLayout.addWidget(self.playlist)
        self.mainlayout.addLayout(self.bottomLayout, 75)
        MainWindow.setCentralWidget(self.centralwidget)
        self.songTimerLabel = QtWidgets.QLabel("0:00")
        self.songLengthLabel = QtWidgets.QLabel("/ 0:00")
        self.topLayout.addWidget(self.songTimerLabel)
        self.topLayout.addWidget(self.songLengthLabel)

        self.addButton.clicked.connect(self.addSound)
        self.shuffleButton.clicked.connect(self.shuffleplaylist)
        self.playButton.clicked.connect(self.playsong)
        self.volumeSlider.setMinimum(0)
        self.volumeSlider.setMaximum(100)
        self.volumeSlider.setValue(70)
        mixer.music.set_volume(0.7)
        self.volumeSlider.valueChanged.connect(self.setvolume)
        self.muteButton.clicked.connect(self.mutesong)
        self.previousButton.clicked.connect(self.previoussong)
        self.nextButton.clicked.connect(self.nextsong)
        self.topgroupBox.setStyleSheet(style.groupboxStyle())
        self.playlist.setStyleSheet(style.playliststyle())

        self.timer = QTimer()
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.updateprogressbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Music Player"))
        self.topgroupBox.setTitle(_translate("MainWindow", "Music Player"))
        self.addButton.setToolTip(_translate("MainWindow", "Add a song"))
        self.addButton.setText(_translate("MainWindow", "..."))
        self.shuffleButton.setToolTip(
            _translate("MainWindow", "Shuffle the list"))
        self.shuffleButton.setText(_translate("MainWindow", "..."))
        self.playButton.setToolTip(_translate("MainWindow", "Play"))
        self.playButton.setText(_translate("MainWindow", "..."))
        self.previousButton.setToolTip(
            _translate("MainWindow", "Play previous"))
        self.previousButton.setText(_translate("MainWindow", "..."))
        self.nextButton.setToolTip(_translate("MainWindow", "Play next"))
        self.nextButton.setText(_translate("MainWindow", "..."))
        self.volumeSlider.setToolTip(_translate("MainWindow", "Volume"))
        self.muteButton.setToolTip(_translate("MainWindow", "Mute"))
        self.muteButton.setText(_translate("MainWindow", "..."))

    def addSound(self):
        directory = QFileDialog.getOpenFileName(
            MainWindow, "Add Sound", "", "Sound Filed(*.mp3 *.ogg *.wav)")
        filename = os.path.basename(directory[0])
        # print(filename)
        musiclist.append(directory[0])
        self.playlist.addItem(filename)

    def shuffleplaylist(self):
        random.shuffle(musiclist)
        # print(musiclist)
        self.playlist.clear()
        for song in musiclist:
            filename = os.path.basename(song)
            self.playlist.addItem(filename)

    def playsong_1(self):
        global play, current_song, songLength, count, index
        count = 0
        index = self.playlist.currentRow()
        try:
            mixer.music.load(str(musiclist[index]))
            mixer.music.play()
            icon7 = QtGui.QIcon()
            icon7.addPixmap(QtGui.QPixmap("assests/stop.png"),
                            QtGui.QIcon.Normal, QtGui.QIcon.Off)
            self.playButton.setIcon(icon7)
            play = True
            current_song = index
            self.timer.start()
            sound = MP3(str(musiclist[index]))
            songLength = sound.info.length
            songLength = round(songLength)
            # print(songLength)
            min, sec = divmod(songLength, 60)
            self.songLengthLabel.setText("/ " + str(min) + ":" + str(sec))
            self.progressBar.setMaximum(songLength)

        except:
            pass

    def playsong(self):
        global play, current_song, songLength, count, index
        index = self.playlist.currentRow()
        if play == False:
            if current_song == index:
                play = True
                icon2 = QtGui.QIcon()
                icon2.addPixmap(QtGui.QPixmap("assests/stop.png"),
                                QtGui.QIcon.Normal, QtGui.QIcon.Off)
                self.playButton.setIcon(icon2)
                self.timer.start()
                mixer.music.unpause()

            else:
                try:
                    count = 0
                    mixer.music.load(musiclist[index])
                    mixer.music.play()
                    icon7 = QtGui.QIcon()
                    icon7.addPixmap(QtGui.QPixmap("assests/stop.png"),
                                    QtGui.QIcon.Normal, QtGui.QIcon.Off)
                    self.playButton.setIcon(icon7)
                    current_song = index
                    self.timer.start()
                    sound = MP3(str(musiclist[index]))
                    songLength = sound.info.length
                    songLength = round(songLength)
                    # print(songLength)
                    min, sec = divmod(songLength, 60)
                    self.songLengthLabel.setText("/ " + str(min) + ":" +
                                                 str(sec))
                    self.progressBar.setMaximum(songLength)

                except:
                    pass
            play = True
        elif play == True:
            play = False
            icon2 = QtGui.QIcon()
            icon2.addPixmap(QtGui.QPixmap("assests/play.png"),
                            QtGui.QIcon.Normal, QtGui.QIcon.Off)
            self.playButton.setIcon(icon2)
            self.timer.stop()
            mixer.music.pause()

    def setvolume(self):
        global mute
        self.volume = self.volumeSlider.value()
        if self.volume > 0 and mute == True:
            self.mutesong()
        mixer.music.set_volume(self.volume / 100)

    def mutesong(self):
        global mute
        _translate = QtCore.QCoreApplication.translate
        if mute == False:
            mixer.music.set_volume(0.0)
            icon6 = QtGui.QIcon()
            icon6.addPixmap(QtGui.QPixmap("assests/unmuted.png"),
                            QtGui.QIcon.Normal, QtGui.QIcon.Off)
            self.muteButton.setIcon(icon6)
            self.muteButton.setToolTip(_translate("MainWindow", "Unmute"))
            self.volumeSlider.setValue(0)
            mute = True
        else:
            icon6 = QtGui.QIcon()
            icon6.addPixmap(QtGui.QPixmap("assests/mute.png"),
                            QtGui.QIcon.Normal, QtGui.QIcon.Off)
            self.muteButton.setIcon(icon6)
            self.muteButton.setToolTip(_translate("MainWindow", "Mute"))
            self.volumeSlider.setValue(70)
            mute = False

    def previoussong(self):
        global play, current_song, songLength, count, index
        if index == 0:
            index = len(musiclist) - 1
        else:
            index -= 1
        try:
            count = 0
            mixer.music.load(musiclist[index])
            mixer.music.play()
            icon7 = QtGui.QIcon()
            icon7.addPixmap(QtGui.QPixmap("assests/stop.png"),
                            QtGui.QIcon.Normal, QtGui.QIcon.Off)
            self.playButton.setIcon(icon7)
            current_song = index
            self.timer.start()
            sound = MP3(str(musiclist[index]))
            songLength = sound.info.length
            songLength = round(songLength)
            # print(songLength)
            min, sec = divmod(songLength, 60)
            self.songLengthLabel.setText("/ " + str(min) + ":" + str(sec))
            self.progressBar.setMaximum(songLength)

        except:
            pass

    def nextsong(self):
        global play, current_song, songLength, count, index
        if index == len(musiclist) - 1:
            index = 0
        else:
            index += 1
        try:
            count = 0
            mixer.music.load(musiclist[index])
            mixer.music.play()
            icon7 = QtGui.QIcon()
            icon7.addPixmap(QtGui.QPixmap("assests/stop.png"),
                            QtGui.QIcon.Normal, QtGui.QIcon.Off)
            self.playButton.setIcon(icon7)
            current_song = index
            self.timer.start()
            sound = MP3(str(musiclist[index]))
            songLength = sound.info.length
            songLength = round(songLength)
            # print(songLength)
            min, sec = divmod(songLength, 60)
            self.songLengthLabel.setText("/ " + str(min) + ":" + str(sec))
            self.progressBar.setMaximum(songLength)

        except:
            pass

    def updateprogressbar(self):
        global count, songLength
        count += 1
        self.progressBar.setValue(count)
        self.songTimerLabel.setText(time.strftime("%M:%S", time.gmtime(count)))
        if count == songLength:
            self.timer.stop()
            self.nextsong()
Exemplo n.º 55
0
class mainwindow(QMainWindow):
    def __init__(self, parent = None):
        QMainWindow.__init__(self)
        loadUi("pyzcto.ui", self)
        self.settings = {}
        self.load_settings()
        fd = open(os.path.expanduser('~/.koto/koto.conf'))
        fdl = fd.readlines()
        fd.close()
        userlines = [l for l in fdl if 'rpcuser' in l]
        passlines = [l for l in fdl if 'rpcpassword' in l]
        if not userlines or not passlines:
            raise Error('setup rpcuser and rpcpassword in koto.conf')
        username = userlines[-1].replace(' ', '').split('=')[1]
        password = passlines[-1].replace(' ', '').split('=')[1]
        self.line_user.setText(username.replace('\n',''))
        self.line_password.setText(password.replace('\n',''))
        self.torproc = QProcess()
        self.tableWidget_ownaddresses.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.tableWidget_otheraddresses.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.tableWidget_traddr.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.tableWidget_shaddr.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.transtable_input.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.transtable_output.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.pushButton_importmultisig.setEnabled(False)
        self.pushButton_importmultisig.clicked.connect(self.importmultisig)
        self.torconnectbutton.clicked.connect(self.torconnect)
        self.pushButton_newtr.clicked.connect(self.newtraddr)
        self.pushButton_newsh.clicked.connect(self.newshaddr)
        self.sendButton.clicked.connect(self.send)
        self.listaddresses_receive.currentItemChanged.connect(self.geneartereceiveqr)
        self.line_receiveamount.textChanged.connect(self.geneartereceiveqr)
        self.line_receivedesc.textChanged.connect(self.geneartereceiveqr)
        self.plainTextEdit_sendmultiple.textChanged.connect(self.check_is_send_correct)
        self.comboBox_sendaccounts.currentIndexChanged.connect(self.check_is_send_correct)
        self.line_sendaccount1.currentTextChanged.connect(self.check_is_send_correct)
        self.line_fee.textChanged.connect(self.check_is_send_correct)
        self.line_sendamount1.textChanged.connect(self.check_is_send_correct)
        self.line_sendmemo1.textChanged.connect(self.check_is_send_correct)
        self.transtable_input.clicked.connect(self.show_transaction_details)
        self.transtable_output.clicked.connect(self.show_transaction_details)
        self.pushButton_addotheraddress.clicked.connect(self.tableWidget_otheraddresses.insertRow,0)
        self.pushButton_deleteotheraddress.clicked.connect(self.removerowfromaccounts)
        self.tableWidget_otheraddresses.cellChanged.connect(self.updateotheraccounts)
        self.tableWidget_otheraddresses.cellChanged.connect(self.updatelinesendaccount)
        self.plainTextEdit_multisigkeys.textChanged.connect(self.generatemultisig)
        self.spinBox_multisign.valueChanged.connect(self.generatemultisig)
        self.plainTextEdit_spendscript.textChanged.connect(self.verifymultisig)
        self.plainTextEdit_to_address_ms.textChanged.connect(self.createmultisigtx)
        self.comboBox_from_addr_ms.currentTextChanged.connect(self.createmultisigtx)
        self.plainTextEdit_raw_ms_tx.textChanged.connect(self.parserawhex)
        self.pushButton_ms_sign.clicked.connect(self.signrawtransaction)
        self.pushButton_ms_broadcast.clicked.connect(self.broadcastrawtransaction)
        self.tableWidget_ownaddresses.horizontalHeader().sectionClicked.connect(self.tableWidget_ownaddresses.sortByColumn)
        self.pushButton_add_multisig_addr.clicked.connect(self.addmultisigaddrtolist)
        self.tabWidget.setCurrentIndex(0)
        self.utxos = []
        self.shreceived = []
        self.balances = {}
        self.addressesalias = {}
        self.otheralias = {}
        self.transactions = []
        self.receiveaddresses = []
        self.sendadrresses = []
        self.pubkeys = {}
        self.readaliasesfromfile()
        self.timer = QTimer()
        self.timer.timeout.connect(self.update)
        self.timer.start(2000)
        self.tableWidget_ownaddresses.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tableWidget_ownaddresses.customContextMenuRequested.connect(self.showpkmenu)
        self.pushButton_newpubkey.clicked.connect(self.newpubkey)
        self.pushButton_showorbotqr.clicked.connect(self.showorbotqr)
        self.pushButton_showzcpqr.clicked.connect(self.showzcpqr)
        self.pushButton_hideqr.clicked.connect(self.hideqr)
        try:
            with open('./hidden_service/hostname') as fd:
                line = fd.readline()
                if len(line.split()) > 1:
                    self.checkBox_stealth.setChecked(True)
        except:
            pass
        self.update()
        self.show()

    def newpubkey(self):
        try:
            nlines = len(self.plainTextEdit_multisigkeys.toPlainText().splitlines())
            if nlines>15:
                return
            newaddress = self.callzcash('getnewaddress')
            pubkey = self.callzcash('validateaddress',[newaddress])['pubkey']
            self.pubkeys[newaddress]=pubkey
            plaintext = self.plainTextEdit_multisigkeys.toPlainText()
            while len(plaintext) >= 2 and plaintext[-2] == '\n':
                plaintext = plaintext[:-1]
            if len(plaintext) >= 1 and plaintext[-1] != '\n':
                plaintext = plaintext + '\n'
            plaintext = plaintext + pubkey
            self.plainTextEdit_multisigkeys.setPlainText(plaintext)
        except:
            pass



    def showpkmenu(self, position):
        item = self.tableWidget_ownaddresses.currentItem()
        address = str(item.text())
        if address[:2] in ['tm', 't1']:
            if not address in self.pubkeys:
                return
            key = self.pubkeys[address]
            menu = QMenu()
            action = menu.addAction("show public key")
            messagebox = QMessageBox()
            messagebox.setText(key)
            messagebox.setWindowTitle("")
            action.triggered.connect(messagebox.exec_)
            menu.exec_(self.tableWidget_ownaddresses.viewport().mapToGlobal(position))

    def createmultisigtx(self):
        try:
            fromaddress = self.comboBox_from_addr_ms.currentText().split()[-1]
            fee = Decimal(self.lineEdit_ms_fee.text())
            destinations = {}
            totalout = fee
            for l in self.plainTextEdit_to_address_ms.toPlainText().splitlines():
                address = l.split(',')[0]
                amount = Decimal(l.split(',')[1])
                totalout += amount
                destinations[address]=amount
            txouts = self.callzcash('listunspent', [1, 999999999, [fromaddress]])
            totalin = Decimal('0')
            txins = []
            while totalin < totalout:
                txout = txouts.pop()
                totalin += txout['amount']
                txid = txout['txid']
                vout = txout['vout']
                txins.append({'txid':txid, 'vout':vout})
            change = totalin-totalout
            if change > 0:
                destinations[fromaddress] = change
            rawtrans = self.callzcash('createrawtransaction',[txins, destinations])
            try:
                self.plainTextEdit_raw_ms_tx.textChanged.disconnect(self.parserawhex)
            except:
                pass
            self.plainTextEdit_raw_ms_tx.clear()
            self.plainTextEdit_raw_ms_tx.appendPlainText(rawtrans)
            self.plainTextEdit_raw_ms_tx.textChanged.connect(self.parserawhex)
        except:
            try:
                self.plainTextEdit_raw_ms_tx.textChanged.disconnect(self.parserawhex)
            except:
                pass
            self.plainTextEdit_raw_ms_tx.clear()
            self.plainTextEdit_raw_ms_tx.textChanged.connect(self.parserawhex)

    def signrawtransaction(self):
        rawtrans = self.plainTextEdit_raw_ms_tx.toPlainText()
        signed = self.callzcash('signrawtransaction', [rawtrans])
        try:
            self.plainTextEdit_raw_ms_tx.textChanged.disconnect(self.parserawhex)
        except:
            pass
        self.plainTextEdit_raw_ms_tx.clear()
        self.plainTextEdit_raw_ms_tx.appendPlainText(signed['hex'])
        self.plainTextEdit_raw_ms_tx.textChanged.connect(self.parserawhex)
        if signed['complete']:
            self.pushButton_ms_broadcast.setEnabled(True)
        else:
            self.pushButton_ms_broadcast.setEnabled(False)



    def importmultisig(self):
        n = self.spinBox_multisign.value()
        addresses = str(self.plainTextEdit_multisigkeys.toPlainText()).splitlines()
        for i in range(len(addresses)):
            if addresses[i] in self.pubkeys:
                addresses[i] = self.pubkeys[addresses[i]]
        self.pushButton_importmultisig.setEnabled(False)
        res = self.callzcash('addmultisigaddress', [n,addresses])
        multisigaddress = str(self.lineEdit_multisigaddress.text())
        messagebox = QMessageBox()
        messagebox.setText("Importing address.\n\nDo you want to rescan the blockchain? \n\n Rescanning the blockchain allows to use the funds sent to the multisig address before this moment but will take several minutes. The program will be irresponsive during that time.\n\n If you don't rescan the blockchain, you will still be able to create payment orders for the funds that will be received in the future, or to sign orders created by other signers.")
        messagebox.setWindowTitle("")
        messagebox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        rescan = messagebox.exec_() == QMessageBox.Yes
        self.callzcash('importaddress', [multisigaddress, "", rescan])

        self.update()


    def generatemultisig(self):
        try:
            n = self.spinBox_multisign.value()
            addresses = str(self.plainTextEdit_multisigkeys.toPlainText()).splitlines()
            totkeys = len(addresses)
            selfkeys = len([a for a in addresses if a in self.pubkeys.values()])
            try:
                self.spinBox_multisign.valueChanged.disconnect(self.generatemultisig)
            except:
                pass
            self.spinBox_multisign.setMaximum(len(addresses))
            self.spinBox_multisign.valueChanged.connect(self.generatemultisig)
            try:
                self.plainTextEdit_spendscript.textChanged.disconnect(self.verifymultisig)
            except:
                pass
            res = self.callzcash('createmultisig',[n, addresses])
            self.lineEdit_multisigaddress.setText(res['address'])
            availableaddresses = self.callzcash('getaddressesbyaccount', [""])
            if res['address'] in availableaddresses:
                self.pushButton_importmultisig.setEnabled(False)
            else:
                self.pushButton_importmultisig.setEnabled(True)
            self.plainTextEdit_spendscript.clear()
            self.plainTextEdit_spendscript.appendPlainText(res['redeemScript'])
            self.plainTextEdit_spendscript.textChanged.connect(self.verifymultisig)
            self.label_nsignatures.setText('Own signatures: {} of {}'.format(selfkeys, totkeys))
        except:
            try:
                self.plainTextEdit_spendscript.textChanged.disconnect(self.verifymultisig)
            except:
                pass
            self.lineEdit_multisigaddress.clear()
            self.pushButton_importmultisig.setEnabled(False)
            self.plainTextEdit_spendscript.clear()
            self.label_nsignatures.setText('Invalid keys')
            self.plainTextEdit_spendscript.textChanged.connect(self.verifymultisig)

    def parserawhex(self):
        self.pushButton_ms_broadcast.setEnabled(False)
        rawhex = self.plainTextEdit_raw_ms_tx.toPlainText()
        res = self.callzcash('decoderawtransaction', [rawhex])
        try:
            self.plainTextEdit_to_address_ms.textChanged.disconnect(self.createmultisigtx)
            self.comboBox_from_addr_ms.currentTextChanged.disconnect(self.createmultisigtx)
        except:
            pass
        self.plainTextEdit_to_address_ms.clear()
        self.updatesendlist()
        self.plainTextEdit_to_address_ms.textChanged.connect(self.createmultisigtx)
        self.comboBox_from_addr_ms.currentTextChanged.connect(self.createmultisigtx)
        try:
            inputaddreses = []
            for vin in res['vin']:
                inputaddreses += self.callzcash('gettxout', [vin['txid'], vin['vout']])['scriptPubKey']['addresses']
            if len(set(inputaddreses)) == 1:
                address = inputaddreses[0]
                tex = ''
                if address in self.balances:
                    tex += self.balances[address]+'\t'
                alias = self.aliasofaddress(address)
                if alias != address:
                    tex += alias +'\t'
                tex += address
                try:
                    self.comboBox_from_addr_ms.currentTextChanged.disconnect(self.createmultisigtx)
                except:
                    pass
                self.comboBox_from_addr_ms.setCurrentText(tex)
                self.comboBox_from_addr_ms.currentTextChanged.connect(self.createmultisigtx)
            outputs = res['vout']
            for output in outputs:
                adr = output['scriptPubKey']['addresses']
                if len(adr)>1:
                    try:
                        self.plainTextEdit_to_address_ms.textChanged.disconnect(self.createmultisigtx)
                        self.comboBox_from_addr_ms.currentTextChanged.disconnect(self.createmultisigtx)
                    except:
                        pass
                    self.plainTextEdit_to_address_ms.clear()
                    self.updatesendlist()
                    self.plainTextEdit_to_address_ms.textChanged.connect(self.createmultisigtx)
                    self.comboBox_from_addr_ms.currentTextChanged.connect(self.createmultisigtx)
                    return
                value = output['value']
                if adr[0] !=address:
                    try:
                        self.plainTextEdit_to_address_ms.textChanged.disconnect(self.createmultisigtx)
                        self.comboBox_from_addr_ms.currentTextChanged.disconnect(self.createmultisigtx)
                    except:
                        pass
                    self.plainTextEdit_to_address_ms.appendPlainText(adr[0]+','+str(value))
                    self.plainTextEdit_to_address_ms.textChanged.connect(self.createmultisigtx)
                    self.comboBox_from_addr_ms.currentTextChanged.connect(self.createmultisigtx)
        except:
            pass

    def verifymultisig(self):
        try:
            script = str(self.plainTextEdit_spendscript.toPlainText())
            res = self.callzcash('decodescript', [script])
            try:
                self.plainTextEdit_multisigkeys.textChanged.disconnect(self.generatemultisig)
            except:
                pass
            self.plainTextEdit_multisigkeys.clear()
            asm = res['asm'].split()
            if not asm[-1] == "OP_CHECKMULTISIG" or not res['type']=='multisig':
                raise ValueError("Not a multisig script")
            for l in asm[1:-2]:
                self.plainTextEdit_multisigkeys.appendPlainText(l)
            self.plainTextEdit_multisigkeys.textChanged.connect(self.generatemultisig)
            self.spinBox_multisign.valueChanged.disconnect(self.generatemultisig)
            self.spinBox_multisign.setMaximum(len(res['addresses']))
            self.spinBox_multisign.setValue(res['reqSigs'])
            self.spinBox_multisign.valueChanged.connect(self.generatemultisig)
            self.lineEdit_multisigaddress.setText(res['p2sh'])
            availableaddresses = self.callzcash('getaddressesbyaccount', [""])
            if res['p2sh'] in availableaddresses and any(ad in availableaddresses for ad in res['addresses']):
                self.pushButton_importmultisig.setEnabled(False)
            else:
                self.pushButton_importmultisig.setEnabled(True)
        except:
            self.lineEdit_multisigaddress.clear()
            self.pushButton_importmultisig.setEnabled(False)
            try:
                self.plainTextEdit_multisigkeys.textChanged.disconnect(self.generatemultisig)
            except:
                pass
            self.plainTextEdit_multisigkeys.clear()
            self.plainTextEdit_multisigkeys.textChanged.connect(self.generatemultisig)

    def addmultisigaddrtolist(self):
        addr = self.comboBox__send_ms_addr.currentText().split()[-1]
        amount = self.lineEdit_send_ms_amount.text()
        self.plainTextEdit_to_address_ms.appendPlainText(addr+','+amount)

    def broadcastrawtransaction(self):
        hex = self.plainTextEdit_raw_ms_tx.toPlainText()
        res = self.callzcash('sendrawtransaction', [hex])
        self.pushButton_ms_broadcast.setEnabled(False)


    def updateotheraccounts(self):
        self.otheralias = {}
        r = self.tableWidget_otheraddresses.rowCount()
        for i in range(r):
            try:
                ad = self.tableWidget_otheraddresses.item(i,2).text()
            except:
                ad = '..'
            if len(ad)<3:
                ad = '..'
            typ = ''
            validate = 'validateaddress'
            if ad[0]=='z':
                typ = 'Shielded'
                validate = 'z_validateaddress'
            elif ad[1] in '1m':
                typ = 'Transparent'
            elif ad[1] in '23':
                typ = 'Multisig'
            try:
                valid = self.callzcash(validate, [ad])['isvalid']
                if not valid:
                    typ = 'Invalid'
            except:
                typ = ''
            if not  self.tableWidget_otheraddresses.item(i,0) or self.tableWidget_otheraddresses.item(i,0).text() != typ:
                item = QTableWidgetItem(typ)
                item.setTextAlignment(Qt.AlignRight)
                item.setFlags(Qt.ItemFlags(97))
                self.tableWidget_otheraddresses.setItem(i,0,item)
            alias = self.tableWidget_otheraddresses.item(i,1)
            if alias:
                self.otheralias[ad]=alias.text()
        with open('addresses.ext','w') as fd:
            for ad in self.otheralias:
                fd.write(ad+' '+self.otheralias[ad]+'\n')

    def removerowfromaccounts(self):
        self.tableWidget_otheraddresses.removeRow(self.tableWidget_otheraddresses.currentRow())
        self.updateotheraccounts()
        self.updatelinesendaccount()

    def show_transaction_details(self):
        table = self.sender()
        row = table.currentRow()
        txid = str(table.item(row, 3).text())
        data = self.callzcash('gettransaction', [txid])
        self.transtext.clear()
        self.transtext.appendPlainText(simplejson.dumps(data, indent=4))

    def get_balances(self):
        zaddresses = self.callzcash('z_listaddresses')
        unspent = self.callzcash('listunspent')
        traddresses = list(self.callzcash('getaddressesbyaccount', ['']))
        addresses = zaddresses + traddresses
        bals = {}
        for ad in addresses:
            bal = self.callzcash('z_getbalance', [ad])
            bal = str(bal)
            if bal == '0E-8':
                bal = '0.00000000'
            #bal += (14-len(str(bal)))*' '
            bals[ad]=bal
        return bals

    def get_utxos(self):
        unspent = reversed(sorted([(u['confirmations'],u['address'], u['amount']) for u in self.callzcash('listunspent')]))
        unspent = [(u[1], u[2], colorfromconfs(u[0])) for u in unspent]
        return unspent

    def get_shreceieved(self):
        shaddreses = self.callzcash('z_listaddresses')
        shtxs = []
        for shad in shaddreses:
            txs = self.callzcash('z_listreceivedbyaddress', [shad])
            for tx in txs:
                txdata = self.callzcash('gettransaction', [tx['txid']])
                memofield = bytearray.fromhex(tx['memo'])
                if memofield[0] == 246:
                    memofield = ''
                else:
                    memofield = memofield.decode().split('\x00')[0]
                shtxs.append((txdata['confirmations'], shad, tx['amount'], memofield))
        shtxs = [(t[1], t[2], t[3], colorfromconfs(t[0])) for t in reversed(sorted(shtxs))]
        return shtxs

    def get_aliases(self):
        aliases = {}
        rows = self.tableWidget_ownaddresses.rowCount()
        for r in range(rows):
            ad = str(self.tableWidget_ownaddresses.item(r,3).text())
            al = self.tableWidget_ownaddresses.item(r,2).text()
            if al:
                aliases[ad]=al
        return aliases

    def readaliasesfromfile(self):
        with open('addresses') as fd:
            lines = fd.readlines()
            for line in lines:
                address = line.split()[0]
                alias = line[len(address)+1:].replace('\n','')
                self.addressesalias[address]=alias
            self.updatelinesendaccount()
        otheralias = {}
        with open('addresses.ext') as fd:
            lines = fd.readlines()
            for line in lines:
                address = line.split()[0]
                alias = line[len(address)+1:].replace('\n','')
                otheralias[address]=alias
        self.tableWidget_otheraddresses.setRowCount(0)
        for ad in otheralias:
            self.tableWidget_otheraddresses.insertRow(0)
            alias = otheralias[ad]
            if alias == ad:
                alias = ''
            item = QTableWidgetItem(alias)
            item.setTextAlignment(Qt.AlignRight)
            self.tableWidget_otheraddresses.setItem(0,1,item)
            item = QTableWidgetItem(ad)
            self.tableWidget_otheraddresses.setItem(0,2,item)

    def aliasofaddress(self, address):
        if address in self.addressesalias:
            return self.addressesalias[address]
        elif address in self.otheralias:
            return self.otheralias[address]
        else:
            return address

    def savealiases(self):
        with open('addresses','w') as fd:
            for ad in self.addressesalias:
                fd.write(ad+' '+self.addressesalias[ad]+'\n')

    def load_settings(self):
        with open('pyzcto.conf','r') as fd:
            options = [l.split('#')[0].split() for l in fd.readlines()]
            for o in options:
                self.settings[o[0]] = o[1]
        self.line_host.setText(self.settings['zcashd_host'])
        self.line_port.setText(self.settings['zcashd_port'])

    def check_is_send_correct(self):
        if self.get_send_data():
            self.sendButton.setEnabled(True)
        else:
            self.sendButton.setEnabled(False)

    def updatelinesendaccount(self):
        self.line_sendaccount1.clear()
        self.comboBox__send_ms_addr.clear()
        self.line_sendaccount1.addItem('')
        self.comboBox__send_ms_addr.addItem('')
        for al in self.addressesalias:
            self.line_sendaccount1.addItem(self.aliasofaddress(al)+'\t'+al)
            self.comboBox__send_ms_addr.addItem(self.aliasofaddress(al)+'\t'+al)
        for al in self.otheralias:
            self.line_sendaccount1.addItem(self.otheralias[al]+'\t'+al)
            self.comboBox__send_ms_addr.addItem(self.otheralias[al]+'\t'+al)

    def get_send_data(self):
        send_data = []
        if self.tabWidget_send.currentIndex() == 0:
            try:
                sendaddr = str(self.line_sendaccount1.currentText()).split()[-1]
                availablefunds =  Decimal(str(self.comboBox_sendaccounts.currentText()).split()[0])
                sendamount = Decimal(str(self.line_sendamount1.text()))
                fee = Decimal(str(self.line_fee.text()))
                memo = str(self.line_sendmemo1.text())
                is_zaddr = sendaddr[0] == 'z'
                if memo:
                    encmemo = ''.join('{:x}'.format(ord(c)) for c in str(memo))
            except:
                return False
            if availablefunds < sendamount + fee:
                return False
            if (not is_zaddr) and memo:
                return False
            if is_zaddr:
                isvalid = self.callzcash('z_validateaddress', [sendaddr])
                if memo:
                    send_data.append({'address':sendaddr, 'amount': sendamount, 'memo': encmemo})
                else:
                    send_data.append({'address':sendaddr, 'amount': sendamount})
            else:
                if memo:
                    return False
                isvalid = self.callzcash('validateaddress', [sendaddr])
                send_data.append({'address':sendaddr, 'amount': sendamount})
            if not isvalid['isvalid']:
                return False
        elif self.tabWidget_send.currentIndex() == 1:
            lines = str(self.plainTextEdit_sendmultiple.toPlainText())
            if not lines:
                return False
            try:
                availablefunds =  Decimal(str(self.comboBox_sendaccounts.currentText()).split()[0])
                fee = Decimal(str(self.line_fee.text()))
                for line in lines.split('\n'):
                    if ',' in line:
                        parsedline = line.split(',')
                        address = parsedline[0].replace(' ','')
                        value = Decimal(parsedline[1].replace(' ',''))
                        if len(parsedline)>2:
                            memo = parsedline[2]
                            if memo:
                                encmemo = ''.join('{:x}'.format(ord(c)) for c in str(memo))
                        else:
                            memo = False
                    else:
                        prot = line.split(':')
                        if len(prot)>2 or prot[0] != 'zcash':
                            return False
                        prot = prot[1].split('?')
                        address = prot[0]
                        values = {k.split('=')[0]:k.split('=')[1] for k in prot[1].split('&')}
                        value = Decimal(values['amount'])
                        memo = 'message' in values and values['message']
                        if memo:
                            memo = values['message']
                            encmemo = ''.join('{:x}'.format(ord(c)) for c in str(memo))
                    if address[0] == 'z':
                        isvalid = self.callzcash('z_validateaddress', [address])
                    else:
                        isvalid = self.callzcash('validateaddress', [address])
                        if memo:
                            return False
                    if not isvalid['isvalid']:
                        return False
                    fee += value
                    if memo:
                        send_data.append({'address':address, 'amount':value, 'memo':encmemo})
                    else:
                        send_data.append({'address':address, 'amount':value})
            except:
                return False
            if fee > availablefunds:
                return False
        return send_data

    def update(self):

        try:
            tocall = set([])
            transactions = self.gettransactions()
            if transactions != self.transactions:
                self.transactions = transactions
                tocall.add(self.updatehistorial)
            aliases = self.get_aliases()
            if self.tableWidget_ownaddresses.rowCount()>0 and self.addressesalias != aliases:
                self.addressesalias = aliases
                tocall.add(self.updatereceive)
                tocall.add(self.updatesendlist)
                tocall.add(self.updatetrs)
                tocall.add(self.updatehistorial)
                tocall.add(self.savealiases)
                tocall.add(self.updatelinesendaccount)
            utxos = self.get_utxos()
            if utxos != self.utxos:
                tocall.add(self.updatetrs)
                self.utxos = utxos
            shreceived = self.get_shreceieved()
            if shreceived != self.shreceived:
                self.shreceived = shreceived
                tocall.add(self.updatetrs)
            balances = self.get_balances()
            if balances != self.balances:
                self.balances = balances
                tocall.add(self.updatesendlist)
                tocall.add(self.updatereceive)
                tocall.add(self.updatealiases)
            for c in tocall:
                c.__call__()
            self.updatestatus()
            self.statusBar.showMessage('Conected to {}:{}'.format(self.line_host.text(), self.line_port.text()))
        except:
            self.statusBar.showMessage('Not connected to daemon. Please check settings')

    def updatesendlist(self):
        self.sendadrresses = []
        self.comboBox_sendaccounts.clear()
        self.comboBox__send_ms_addr.clear()
        self.comboBox_from_addr_ms.clear()
        self.comboBox_from_addr_ms.addItem('')
        self.comboBox__send_ms_addr.addItem('')
        for bal in self.balances:
            self.comboBox_sendaccounts.addItem(self.balances[bal]+ '\t' +self.aliasofaddress(bal))
            if self.aliasofaddress(bal) != bal:
                self.comboBox__send_ms_addr.addItem(self.balances[bal]+ '\t' +self.aliasofaddress(bal) + '\t' + bal)
            else:
                self.comboBox__send_ms_addr.addItem(self.balances[bal]+'\t\t' + bal)
            self.sendadrresses.append(bal)
            if not bal[0] == 'z' and not bal[1] in '1m':
                alias = self.aliasofaddress(bal)
                if alias == bal:
                    self.comboBox_from_addr_ms.addItem(self.balances[bal]+ '\t' + bal)
                else:
                    self.comboBox_from_addr_ms.addItem(self.balances[bal]+ '\t' +alias+ '\t' + bal)



    def send(self):
        params = self.get_send_data()
        if not params:
            return
        fromaddress = self.sendadrresses[self.comboBox_sendaccounts.currentIndex()]
        try:
            fee = Decimal(str(self.line_fee.text()))
        except:
            return
        op = self.callzcash('z_sendmany', [fromaddress, params, 1, fee])
        self.donetext.appendPlainText(op)
        self.sendButton.setEnabled(False)

    def updatestatus(self):
        opresults = self.callzcash('z_getoperationresult')
        if opresults:
            self.donetext.appendPlainText(simplejson.dumps(opresults,indent=4))
        opstatus = self.callzcash('z_getoperationstatus')
        if opstatus:
            opstr = simplejson.dumps(opstatus,indent=4)
            if str(self.statustext.toPlainText()) != opstr:
                self.statustext.clear()
                self.statustext.appendPlainText(opstr)
        else:
            self.statustext.clear()

    def newtraddr(self):
        self.callzcash('getnewaddress')
        self.listaddresses_receive.clear()
        self.updatereceive()
        self.update()

    def newshaddr(self):
        self.callzcash('z_getnewaddress')
        self.listaddresses_receive.clear()
        self.updatereceive()
        self.update()

    def torconnect(self):
        if self.torconnectbutton.text() == '&Connect':
            with open('torrc', 'w') as fd:
                fd.write('SOCKSPort 10050\n')
                if self.proxybutton.isChecked():
                    proxyurl = self.proxyaddress.text()
                    proxyport = self.proxyport.text()
                    proxyuser = self.proxyuser.text()
                    proxypass = self.proxypasswd.text()
                    if self.proxytype.currentText()=='http':
                        if not proxyport:
                            proxyport = 80
                        fd.write('HTTPProxy {}:{}\n'.format(proxyurl,proxyport))
                        if proxyuser or proxypass:
                            fd.write('HTTPProxyAuthenticator {}:{}\n'.format(proxyuser,proxypass))
                    elif self.proxytype.currentText() == 'https':
                        if not proxyport:
                            proxyport = 443
                        fd.write('HTTPSProxy {}:{}\n'.format(proxyurl,proxyport))
                        if proxyuser or proxypass:
                            fd.write('HTTPSProxyAuthenticator {}:{}\n'.format(proxyuser,proxypass))
                    elif self.proxytype.currentText() == 'socks4':
                        if not proxyport:
                            proxyport = 1080
                        fd.write('Socks4Proxy {}:{}\n'.format(proxyurl,proxyport))
                    elif self.proxytype.currentText() == 'socks5':
                        if not proxyport:
                            proxyport = 1080
                        fd.write('Socks5Proxy {}:{}\n'.format(proxyurl,proxyport))
                        if proxyuser or proxypass:
                            fd.write('Socks5ProxyUsername {}\n'.format(proxyuser))
                            fd.write('Socks5ProxyPassword {}\n'.format(proxypass))
                fd.write('HiddenServiceDir ./hidden_service/ \n')
                fd.write('HiddenServicePort 80 127.0.0.1:{}\n'.format(self.line_port.text()))
                if self.checkBox_stealth.isChecked():
                    fd.write('HiddenServiceAuthorizeClient stealth zcashpannel\n')
            self.torproc.setProcessChannelMode(QProcess.MergedChannels)
            self.torproc.start('tor', ['-f', 'torrc'])
            self.torproc.readyReadStandardOutput.connect(self.updatetor)
            self.torproc.waitForStarted()
            self.torconnectbutton.setText('&Disconnect')
            while not os.path.isfile('./hidden_service/hostname'):
                time.sleep(0.5)
            if self.checkBox_stealth.isChecked():
                self.pushButton_showorbotqr.setEnabled(True)
            self.pushButton_showzcpqr.setEnabled(True)
            self.pushButton_hideqr.setEnabled(True)
            self.checkBox_stealth.setEnabled(False)

            self.timer.setInterval(60000) # reduce frequecy of updating to reduce interference with requests through tor
        else:
            self.torproc.terminate()
            self.torproc.waitForFinished()
            self.torconnectbutton.setText('&Connect')
            self.pushButton_showorbotqr.setEnabled(False)
            self.pushButton_showzcpqr.setEnabled(False)
            self.pushButton_hideqr.setEnabled(False)
            self.checkBox_stealth.setEnabled(True)
            self.timer.setInterval(2000) # restore update frequency when there is no possible interference with requests through tor

    def showzcpqr(self):
        fd = open('./hidden_service/hostname')
        oniondom = fd.readline().split()[0]
        fd.close()
        username = str(self.line_user.text())
        password = str(self.line_password.text())
        img = qrcode.make(username+':'+password+'@'+oniondom)
        img.save('qrcode.png', 'PNG')
        qrc = QPixmap('qrcode.png')
        os.remove('qrcode.png')
        self.onionlabelname.setText(username+':'+password+'@'+oniondom)
        self.onionlabelname.show()
        self.onionlabel.setPixmap(qrc.scaled(self.onionlabel.size(), Qt.KeepAspectRatio))
        self.onionlabel.show()

    def showorbotqr(self):
        fd = open('./hidden_service/hostname')
        line = fd.readline()
        oniondom =line.split()[0]
        cookie = line.split()[1]
        fd.close()
        jsonstring = '{'+'"auth_cookie_value": "{}", "domain":"{}"'.format(cookie, oniondom)+'}'
        img = qrcode.make(jsonstring)
        img.save('qrcode.png', 'PNG')
        qrc = QPixmap('qrcode.png')
        os.remove('qrcode.png')
        self.onionlabelname.setText(jsonstring)
        self.onionlabelname.show()
        self.onionlabel.setPixmap(qrc.scaled(self.onionlabel.size(), Qt.KeepAspectRatio))
        self.onionlabel.show()

    def hideqr(self):
        self.onionlabel.hide()
        self.onionlabelname.hide()


    def updatetor(self):
        self.torconsole.appendPlainText(str(self.torproc.readAllStandardOutput()))

    def gettransactions(self):
        trans = []
        transactions = self.callzcash('listtransactions', ['', 1000])
        for tx in transactions:
            if 'address' in tx:
                address = tx['address']
            else:
                address = False
            tx2 = [tx['category'], tx['txid'], tx['time'], address, tx['amount'], colorfromconfs(tx['confirmations'])]
            trans.append(tx2)
        return trans

    def updatehistorial(self):
        rw = 0
        self.transtable_input.setRowCount(0)
        self.transtable_output.setRowCount(0)
        for tx in self.transactions:
            if tx[0]=='receive':
                table = self.transtable_input
            elif tx[0] == 'send':
                table = self.transtable_output
            else:
                continue
            table.insertRow(rw)
            item = QTableWidgetItem(tx[1])
            item.setFlags(Qt.ItemFlags(97))
            item.setBackground(QBrush(QColor(tx[-1][0],tx[-1][1],tx[-1][2])))
            table.setItem(rw, 3, item)
            timet = time.strftime('%b %d %Y, %H:%M', time.localtime(tx[2]))
            item = QTableWidgetItem(timet)
            item.setFlags(Qt.ItemFlags(97))
            item.setBackground(QBrush(QColor(tx[-1][0],tx[-1][1],tx[-1][2])))
            table.setItem(rw, 0, item)
            if tx[2]:
                item = QTableWidgetItem(self.aliasofaddress(tx[3]))
                item.setFlags(Qt.ItemFlags(97))
                item.setBackground(QBrush(QColor(tx[-1][0],tx[-1][1],tx[-1][2])))
                table.setItem(rw, 1, item)
            item = QTableWidgetItem(str(tx[4]))
            item.setFlags(Qt.ItemFlags(97))
            item.setBackground(QBrush(QColor(tx[-1][0],tx[-1][1],tx[-1][2])))
            table.setItem(rw, 2, item)
        #self.transtable_input.resizeColumnsToContents()
        #self.transtable_output.resizeColumnsToContents()

    def updatetrs(self):
        self.tableWidget_traddr.setRowCount(0)
        trbalance = Decimal('0.0')
        shbalance = Decimal(self.callzcash('z_gettotalbalance')['private'])
        for us in self.utxos:
            self.tableWidget_traddr.insertRow(0)
            trbalance += us[1]
            item = QTableWidgetItem(self.aliasofaddress(us[0]))
            item.setFlags(Qt.ItemFlags(97))
            item.setBackground(QBrush(QColor(us[-1][0],us[-1][1],us[-1][2])))
            self.tableWidget_traddr.setItem(0, 0, item)
            item = QTableWidgetItem(str(us[1]))
            item.setFlags(Qt.ItemFlags(97))
            item.setTextAlignment(Qt.AlignRight)
            item.setBackground(QBrush(QColor(us[-1][0],us[-1][1],us[-1][2])))
            self.tableWidget_traddr.setItem(0, 1, item)
        self.label_transparent_balance.setText('Transparent balance: {}'.format(trbalance))
        #self.tableWidget_traddr.resizeColumnsToContents()
        self.tableWidget_shaddr.setRowCount(0)
        for tr in self.shreceived:
            self.tableWidget_shaddr.insertRow(0)
            item = QTableWidgetItem(self.aliasofaddress(tr[0]))
            item.setFlags(Qt.ItemFlags(97))
            if len(tr[2])>1:
                item.setToolTip(tr[2])
            item.setBackground(QBrush(QColor(tr[-1][0],tr[-1][1],tr[-1][2])))
            self.tableWidget_shaddr.setItem(0, 0, item)
            item = QTableWidgetItem(str(tr[1]))
            item.setFlags(Qt.ItemFlags(97))
            item.setTextAlignment(Qt.AlignRight)
            if len(tr[2])>1:
                item.setToolTip(tr[2])
            item.setBackground(QBrush(QColor(tr[-1][0],tr[-1][1],tr[-1][2])))
            self.tableWidget_shaddr.setItem(0, 1, item)
        #self.tableWidget_shaddr.resizeColumnsToContents()
        self.label_shielded_balance.setText('Shielded balance: {}'.format(shbalance))
        self.label_total_balance.setText('Total balance: {}'.format(shbalance+trbalance))

    def updatealiases(self):
        self.tableWidget_ownaddresses.setRowCount(0)
        for ad in self.balances:
            bal = self.balances[ad]
            self.tableWidget_ownaddresses.insertRow(0)
            item = QTableWidgetItem(bal)
            item.setTextAlignment(Qt.AlignRight)
            item.setFlags(Qt.ItemFlags(97))
            self.tableWidget_ownaddresses.setItem(0,0,item)
            typ = ''
            if ad[0]=='z':
                typ = 'Shielded'
            elif ad[1] in '1m':
                typ = 'Transparent'
                vali =  self.callzcash('validateaddress', [ad])
                if vali['ismine']:
                    pubkey =vali['pubkey']
                    self.pubkeys[ad] = pubkey
            elif ad[1] in '23':
                typ = 'Multisig'
            item = QTableWidgetItem(typ)
            item.setTextAlignment(Qt.AlignRight)
            item.setFlags(Qt.ItemFlags(97))
            self.tableWidget_ownaddresses.setItem(0,1,item)
            alias = self.aliasofaddress(ad)
            if alias == ad:
                alias = ''
            item = QTableWidgetItem(alias)
            #item.setTextAlignment(Qt.AlignRight)
            self.tableWidget_ownaddresses.setItem(0,2,item)
            item = QTableWidgetItem(ad)
            item.setFlags(Qt.ItemFlags(97))
            self.tableWidget_ownaddresses.setItem(0,3,item)

    def updatereceive(self):
        self.listaddresses_receive.clear()
        self.receiveaddresses = []
        for ad in self.balances:
            bal = self.balances[ad]
            self.receiveaddresses= [ad]+self.receiveaddresses
            self.listaddresses_receive.insertItem(0, bal+'\t'+self.aliasofaddress(ad))

    def geneartereceiveqr(self):
        try:
            address = self.receiveaddresses[self.listaddresses_receive.currentIndex().row()]
        except:
            address = ''
        amount = self.line_receiveamount.text()
        comment = self.line_receivedesc.text()
        if address and amount:
            if comment:
                try:
                    string = 'zcash:{}?amount={}&message={}'.format(address,amount,comment)
                except:
                    self.label_qrreceive.hide()
                    self.label_textreceive.hide()
                    return
            else:
                string = 'zcash:{}?amount={}'.format(address,amount)
            img = qrcode.make(string)
            img.save('qrcode.png', 'PNG')
            qrc = QPixmap('qrcode.png')
            os.remove('qrcode.png')
            self.label_textreceive.setText(string)
            self.label_textreceive.show()
            self.label_qrreceive.setPixmap(qrc.scaled(self.onionlabel.size(), Qt.KeepAspectRatio))
            self.label_qrreceive.show()
        else:
            self.label_qrreceive.hide()
            self.label_textreceive.hide()

    def callzcash(self, method, params = []):
        url='http://'+str(self.line_host.text()) + ':' + str(self.line_port.text())
        user = str(self.line_user.text())
        passwd = str(self.line_password.text())
        user = user.encode('utf8')
        passwd = passwd.encode('utf8')
        timeout = 600
        jsondata = simplejson.dumps({'version':'2', 'method': method, 'params': params, 'id': 0})
        r = requests.post(url, auth=(user,passwd), data=jsondata, timeout=timeout)
        res = simplejson.loads(r.text, use_decimal=True)['result']
        return res
Exemplo n.º 56
0
class MainFrame(QtWidgets.QWidget,_MainFrameLoadData.MainFrameLoadData,
        _MainFrameDataSlots.MainFrameDataSlots,
        _MainFrameToolBarSlots.MainFrameToolBarSlots,
        _MainFrameLibTreeSlots.MainFrameLibTreeSlots,
        _MainFrameFilterListSlots.MainFrameFilterListSlots,
        _MainFrameDocTableSlots.MainFrameDocTableSlots,
        _MainFrameMetaTabSlots.MainFrameMetaTabSlots,
        _MainFrameOtherSlots.MainFrameOtherSlots,
        _MainFrameProperties.MainFrameProperties
        ):
    def __init__(self,settings, parent):
        super(MainFrame,self).__init__()

        self.settings=settings
        self.parent=parent
        self.logger=logging.getLogger(__name__)
        self.initUI()
        self.auto_save_timer=QTimer(self)
        tinter=self.settings.value('saving/auto_save_min', 1, int)*60*1000 # in msc
        self.auto_save_timer.setInterval(tinter)
        self.auto_save_timer.timeout.connect(self.saveToDatabase)


    def initUI(self):

        v_layout0=QtWidgets.QVBoxLayout(self)
        v_layout0.setContentsMargins(2,5,2,1)

        #-------------------Tool bar row-------------------
        h_layout0=QtWidgets.QHBoxLayout()
        v_layout0.addLayout(h_layout0)

        # Add button
        self.add_button=self.createAddMoreButton()
        self.add_folder_button=self.createAddFolderButton()
        self.duplicate_check_button=self.createDuplicateCheckButton()

        self.add_button.setEnabled(False)
        self.add_folder_button.setEnabled(False)
        self.duplicate_check_button.setEnabled(False)

        h_layout0.addWidget(self.add_button)
        h_layout0.addWidget(self.add_folder_button)
        h_layout0.addWidget(self.duplicate_check_button)

        h_layout0.addStretch()

        # seach bar
        self.search_bar=QtWidgets.QLineEdit(self)
        self.search_bar.setPlaceholderText('Type to search')
        self.search_bar.setFixedWidth(300)
        self.search_bar.setSizePolicy(getMinSizePolicy())
        self.search_bar.returnPressed.connect(self.searchBarClicked)

        # search button
        self.search_button=self.createSearchButton()
        self.search_button.setEnabled(False)

        h_layout0.addWidget(self.search_bar)
        h_layout0.addWidget(self.search_button)

        #-------------Add hline below tool bar-------------
        v_layout0.addWidget(getHLine(self))

        #---------------------H split---------------------
        h_split=QtWidgets.QSplitter(Qt.Horizontal)
        h_split.setSizePolicy(getXExpandYExpandSizePolicy())
        v_layout0.addWidget(h_split)

        #---------------------V split---------------------
        v_split=QtWidgets.QSplitter(Qt.Vertical)
        v_la1=QtWidgets.QVBoxLayout()
        v_la1.setContentsMargins(0,0,0,0)
        v_la1.setSpacing(0)
        fr=QtWidgets.QFrame()

        #-------------------Add lib tree-------------------
        self.libtree=self.createLibTree()
        v_la1.addWidget(self.libtree)

        #-----------Add fold filter list button-----------
        self.fold_filter_button=self.createFoldFilterButton()
        v_la1.addWidget(self.fold_filter_button)

        fr.setLayout(v_la1)
        v_split.addWidget(fr)

        #----------------Add filter list----------------
        self.filter_list=self.createFilterList()

        v_split.addWidget(self.filter_list)
        h_split.addWidget(v_split)

        v_split.setSizes([3,1])
        h=v_split.size().height()
        v_split.setSizes([h*0.65,h*0.35])

        #------------Add clear filtering frame------------
        self.clear_filter_frame=self.createClearFilterFrame()

        frame=QtWidgets.QFrame()
        v_la=QtWidgets.QVBoxLayout()
        v_la.addWidget(self.clear_filter_frame)

        #---------Add duplicate check result frame---------
        self.duplicate_result_frame=self.createDuplicateResultFrame()
        v_la.addWidget(self.duplicate_result_frame)

        #-------------Add search result frame-------------
        self.search_res_frame=self.createSearchResultFrame()
        v_la.addWidget(self.search_res_frame)

        #------------------Add doc table------------------
        self.doc_table=self.createDocTable()
        v_la.addWidget(self.doc_table)

        h_layout=QtWidgets.QHBoxLayout()
        h_layout.setContentsMargins(0,0,0,0)
        h_layout.setSpacing(0)
        h_layout.addLayout(v_la)

        #--------------Add fold/unfold button--------------
        self.fold_tab_button=self.createFoldTabButton()
        h_layout.addWidget(self.fold_tab_button)
        frame.setLayout(h_layout)
        frame.setSizePolicy(getXExpandYExpandSizePolicy())
        h_split.addWidget(frame)

        #------------------Add right pane------------------
        self.tab_pane=QtWidgets.QFrame()
        v_la2=QtWidgets.QVBoxLayout()
        self.tab_pane.setLayout(v_la2)

        #-------------Add confirm review frame-------------
        self.confirm_review_frame=self.createConfirmReviewFrame()
        v_la2.addWidget(self.confirm_review_frame)

        #---------------------Add tabs---------------------
        self.tabs=self.createTabs()
        v_la2.addWidget(self.tabs)

        h_split.addWidget(self.tab_pane)

        #------------------Add status bar------------------
        self.status_bar=QtWidgets.QStatusBar()
        #self.status_bar.setFixedHeight(12)
        self.status_bar.setFont(self.settings.value('display/fonts/statusbar',QFont))
        v_layout0.addWidget(self.status_bar)
        self.status_bar.showMessage('Welcome')

        self.progressbar=QtWidgets.QProgressBar(self)
        self.progressbar.setSizePolicy(getXExpandYMinSizePolicy())
        self.progressbar.setMaximum(1)
        self.progressbar.setVisible(False)
        self.status_bar.addPermanentWidget(self.progressbar)

        # search_res_frame created before status bar
        self.search_res_frame.search_done_sig.connect(self.status_bar.clearMessage)

        show_widgets=self.settings.value('view/show_widgets',[],str)
        if isinstance(show_widgets,str) and show_widgets=='':
            show_widgets=[]

        if 'Toggle Status bar' not in show_widgets:
            self.status_bar.setVisible(False)

        h_split.setHandleWidth(4)
        w=h_split.size().width()
        h_split.setCollapsible(2,True)
        h_split.setSizes([w*0.15,w*0.6,w*0.25])

        self.logger.info('Main frame UI inited.')

        self.show()


    #######################################################################
    #                           Create widgets                            #
    #######################################################################


    def createAddMoreButton(self):

        button=QtWidgets.QToolButton(self)
        button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        @pyqtSlot(QtWidgets.QAction)
        def changeDefaultAction(action):
            '''Change the default action'''

            action_data=action.data()
            for aii, (addii, cdii) in add_buttons.items():
                if aii==action_data:
                    addii.setShortcut('Ctrl+n')
                    cdii.setChecked(True)
                    button.setDefaultAction(addii)
                    button.setText('Add')
                    button.setIcon(QIcon.fromTheme('document-new',
                        self.style().standardIcon(QStyle.SP_FileDialogDetailedView)))
                    self.settings.setValue('import/default_add_action',aii)
                else:
                    addii.setShortcut('')
                    cdii.setChecked(False)

        #-------Add the default action to the button-------
        add_buttons={}
        menu=QtWidgets.QMenu()
        choose_default_menu=QtWidgets.QMenu('Choose Default Action',menu)
        default_act=self.settings.value('import/default_add_action',type=str)

        for aii in ['Add PDF File', 'Add Bibtex File', 'Add RIS File',
                'Add Entry Manually']:

            add_actionii=menu.addAction(aii)
            cd_actionii=choose_default_menu.addAction(aii)
            cd_actionii.setData(aii)
            cd_actionii.setCheckable(True)

            if aii==default_act:
                add_actionii.setShortcut('Ctrl+n')
                cd_actionii.setChecked(True)
            else:
                add_actionii.setShortcut('')
                cd_actionii.setChecked(False)
            add_buttons[aii]=[add_actionii, cd_actionii]

        choose_default_menu.triggered.connect(changeDefaultAction)
        menu.addMenu(choose_default_menu)

        button.setDefaultAction(add_buttons[default_act][0])

        # these has to happen after setDefaultAction()
        button.setText('Add')
        button.setIcon(QIcon.fromTheme('document-new',
            self.style().standardIcon(QStyle.SP_FileDialogDetailedView)))
        button.setMenu(menu)
        button.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup)

        menu.triggered.connect(self.addActionTriggered)

        return button


    def createAddFolderButton(self):

        button=QtWidgets.QToolButton(self)
        button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        menu=QtWidgets.QMenu()
        self.create_folder_action=menu.addAction('Create Folder')
        self.create_subfolder_action=menu.addAction('Create Sub Folder')

        button.setDefaultAction(self.create_folder_action)

        button.setText('Create Folder')
        button.setIcon(QIcon.fromTheme('folder-new',
            self.style().standardIcon(QStyle.SP_FileDialogNewFolder)))
        button.setMenu(menu)
        button.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup)

        menu.triggered.connect(self.addFolderButtonClicked)

        return button


    def createDuplicateCheckButton(self):

        button=QtWidgets.QToolButton(self)
        button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        button.setText('Check Duplicates')
        button.setIcon(QIcon.fromTheme('scanner',
            self.style().standardIcon(QStyle.SP_FileDialogContentsView)))
        button.clicked.connect(self.checkDuplicateClicked)

        return button


    def createSearchButton(self):

        button=QtWidgets.QToolButton(self)
        menu=QtWidgets.QMenu()

        search_fields=self.settings.value('search/search_fields',[],str)
        if isinstance(search_fields,str) and search_fields=='':
            search_fields=[]

        # add search fields menu
        lib_xapian_folder=os.path.join(self.settings.value(
            'saving/current_lib_folder', type=str), '_xapian_db')
        if hasXapian() and os.path.exists(lib_xapian_folder):
            has_pdf=True
        else:
            has_pdf=False

        for fieldii in ['Authors', 'Title', 'Abstract', 'Keywords', 'Tags',
                'Notes', 'Publication', 'PDF', 'Citationkey']:
            cbii=QtWidgets.QCheckBox(fieldii, menu)
            aii=QtWidgets.QWidgetAction(menu)
            if fieldii in search_fields:
                cbii.setChecked(True)
            if fieldii=='PDF':
                # keep a reference
                self.pdf_search_action=aii
                self.pdf_search_checkbox=cbii
                if not has_pdf:
                    cbii.setChecked(False)
                    aii.setEnabled(False)
            cbii.stateChanged.connect(aii.trigger)
            aii.setDefaultWidget(cbii)
            aii.setText(fieldii)
            menu.addAction(aii)

        menu.addSeparator()

        # add desend into subfolders option
        cbii=QtWidgets.QCheckBox('Include sub-folders', menu)
        if self.settings.value('search/desend_folder',bool):
            cbii.setChecked(True)
        aii=QtWidgets.QWidgetAction(menu)
        cbii.stateChanged.connect(aii.trigger)
        aii.setDefaultWidget(cbii)
        aii.setText('Include sub-folders')
        menu.addAction(aii)

        button.setIcon(QIcon.fromTheme('edit-find',
            self.style().standardIcon(QStyle.SP_FileDialogContentsView)))
        button.setMenu(menu)
        button.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup)
        button.clicked.connect(self.searchBarClicked)

        return button


    def createLibTree(self):

        libtree=MyTreeWidget(self)

        libtree.customContextMenuRequested.connect(self.libTreeMenu)
        libtree.folder_move_signal.connect(self.changeFolderParent)
        libtree.folder_del_signal.connect(self.trashFolder)
        libtree.itemDoubleClicked.connect(self.renameFolder)
        libtree.add_doc_to_folder_signal.connect(self.addDocToFolder)
        libtree.selectionModel().selectionChanged.connect(self.selFolder)

        return libtree


    def createFoldFilterButton(self):

        button=QtWidgets.QToolButton(self)
        button.setArrowType(Qt.DownArrow)
        button.clicked.connect(self.foldFilterButtonClicked)
        button.setSizePolicy(getXExpandYMinSizePolicy())
        button.setFixedHeight(10)
        #button.setStyleSheet(
                #''''border-radius: 0; border-width: 1px;
                #border-style: solid; border-color:grey''')

        return button


    def createFilterList(self):

        frame=QtWidgets.QFrame()
        scroll=QtWidgets.QScrollArea(self)
        scroll.setWidgetResizable(True)
        v_layout=QtWidgets.QVBoxLayout()
        v_layout.setContentsMargins(0,0,0,0)

        self.filter_type_combbox=QtWidgets.QComboBox(self)
        self.filter_type_combbox.addItem('Filter by authors')
        self.filter_type_combbox.addItem('Filter by keywords')
        self.filter_type_combbox.addItem('Filter by publications')
        self.filter_type_combbox.addItem('Filter by tags')
        self.filter_type_combbox.currentIndexChanged.connect(
                self.filterTypeCombboxChange)
        self.filter_type_combbox.setSizeAdjustPolicy(
                QtWidgets.QComboBox.AdjustToMinimumContentsLength)

        self.filter_item_list=QtWidgets.QListWidget(self)
        self.filter_item_list.itemClicked.connect(self.filterItemClicked)

        v_layout.addWidget(self.filter_type_combbox)
        v_layout.addWidget(self.filter_item_list)

        frame.setLayout(v_layout)
        scroll.setWidget(frame)

        show_widgets=self.settings.value('view/show_widgets',[],str)
        if isinstance(show_widgets,str) and show_widgets=='':
            show_widgets=[]

        if 'Toggle Filter List' not in show_widgets:
            scroll.setVisible(False)

        return scroll


    def createDuplicateResultFrame(self):

        frame=CheckDuplicateFrame(self.settings,self)
        frame.tree.currentItemChanged.connect(self.duplicateResultCurrentChange)
        frame.del_doc_from_folder_signal.connect(self.delFromFolder)
        frame.del_doc_from_lib_signal.connect(self.delDoc)
        frame.clear_duplicate_button.clicked.connect(
                self.clearDuplicateButtonClicked)
        frame.merge_frame.add_new_doc_sig.connect(self.addDocFromDuplicateMerge)
        frame.merge_frame.del_doc_sig.connect(self.delDoc)
        frame.setVisible(False)

        return frame


    def createSearchResultFrame(self):

        frame=SearchResFrame(self.settings,self)
        frame.tree.currentItemChanged.connect(self.searchResultCurrentChange)
        frame.clear_searchres_button.clicked.connect(self.clearSearchResButtonClicked)
        frame.create_folder_sig.connect(self.createFolderFromSearch)
        #frame.hide_doc_sig.connect(self.hideDocTable)
        frame.setVisible(False)

        return frame


    def createClearFilterFrame(self):

        frame=QtWidgets.QFrame()
        frame.setStyleSheet('background: rgb(235,225,190)')
        h_la=QtWidgets.QHBoxLayout()

        # clear fitlering button
        self.clear_filter_button=QtWidgets.QToolButton(self)
        self.clear_filter_button.setText('Clear')
        self.clear_filter_button.clicked.connect(self.clearFilterButtonClicked)

        self.clear_filter_label=QtWidgets.QLabel('  Clear current filtering')
        h_la.addWidget(self.clear_filter_label)
        h_la.addWidget(self.clear_filter_button)

        frame.setLayout(h_la)
        frame.setVisible(False)

        return frame


    def createConfirmReviewFrame(self):

        frame=QtWidgets.QFrame()
        frame.setStyleSheet('background: rgb(235,225,190)')
        h_la=QtWidgets.QHBoxLayout()

        # confirm button
        self.confirm_review_button=QtWidgets.QToolButton(self)
        self.confirm_review_button.setText('Confirm')
        self.confirm_review_button.clicked.connect(self.confirmReviewButtonClicked)

        label=QtWidgets.QLabel('Meta data is correct?')
        h_la.addWidget(label)
        h_la.addWidget(self.confirm_review_button)

        frame.setLayout(h_la)
        frame.setVisible(False)

        return frame


    def createDocTable(self):

        tv=QtWidgets.QTableView(self)

        hh=MyHeaderView(self)

        tv.setHorizontalHeader(hh)
        tv.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
        tv.setShowGrid(True)
        tv.setSortingEnabled(True)

        tv.setDragEnabled(True)
        #tv.setSectionsMovable(True)
        tv.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)

        header=['docid','favourite','read','has_file','author','title',
                'journal','year','added','confirmed']
        tablemodel=TableModel(self,[],header,self.settings)
        tablemodel.dataChanged.connect(self.modelDataChanged)
        tv.setModel(tablemodel)
        hh.setModel(tablemodel)
        hh.initresizeSections()
        tv.setColumnHidden(0,True) # doc id column, hide
        tv.setColumnHidden(9,True) # needs review column, shown as bold/normal

        tv.selectionModel().currentChanged.connect(self.selDoc)
        tv.clicked.connect(self.docTableClicked)
        #tablemodel.rowsInserted.connect(self.model_insert_row)
        tv.setContextMenuPolicy(Qt.CustomContextMenu)
        tv.customContextMenuRequested.connect(self.docTableMenu)

        tv.doubleClicked.connect(self.docDoubleClicked)
        tv.setAlternatingRowColors(True)

        # NOTE: this seems to be change somewhere between PyQt5.6.0 and
        # PyQt5.12.1 that the latter default to setWordWrap(True)
        tv.setWordWrap(False)

        tv.setStyleSheet('''alternate-background-color: rgb(230,230,249);
                background-color: none''')

        # add a short cut for pdf preview
        QtWidgets.QShortcut(Qt.Key_Space, tv, activated=self.openPDFViewer)

        return tv


    def createFoldTabButton(self):

        button=QtWidgets.QToolButton(self)
        button.setArrowType(Qt.RightArrow)
        button.clicked.connect(self.foldTabButtonClicked)
        button.setFixedWidth(10)
        button.setFixedHeight(200)
        #button.setStyleSheet(
                #''''border-radius: 0; border-width: 1px;
                #border-style: solid; border-color:grey''')

        return button


    def createTabs(self):

        tabs=QtWidgets.QTabWidget()

        self.t_notes=self.createNoteTab()
        self.t_bib=self.createBiBTab()
        self.t_scratchpad=self.createScratchTab()
        self.t_pdf=self.createPDFTab()
        self.t_meta=MetaTabScroll(self.settings,self)
        self.t_meta.meta_edited.connect(lambda field_list: self.updateTableData(\
            self._current_doc,self.t_meta._meta_dict,field_list))
        self.t_meta.update_by_doi_signal.connect(self.updateByDOI)

        # tab_dict is used for visibility control
        self.tab_dict={'Toggle Meta Tab': [self.t_meta, 'Meta Data'],
                'Toggle Notes Tab': [self.t_notes, 'Notes'],
                'Toggle BibTex Tab': [self.t_bib, 'BibTex'],
                'Toggle Scratch Pad Tab': [self.t_scratchpad, 'Scratch Pad'],
                'Toggle PDF Tab': [self.t_pdf, 'PDF preview']
                }

        show_widgets=self.settings.value('view/show_widgets',[],str)
        if isinstance(show_widgets,str) and show_widgets=='':
            show_widgets=[]

        # add tabs that are toggled on in view menu
        for tii in show_widgets:
            if tii in self.tab_dict:
                tabs.addTab(*self.tab_dict[tii])

        for tii in list(set(self.tab_dict.keys()).difference(show_widgets)):
            self.tab_dict[tii][0].setVisible(False)

        tabs.currentChanged.connect(self.currentTabChange)

        return tabs


    def createNoteTab(self):

        scroll=QtWidgets.QScrollArea(self)
        scroll.setWidgetResizable(True)
        frame=QtWidgets.QFrame()
        v_layout=QtWidgets.QVBoxLayout()

        button=QtWidgets.QToolButton(self)
        button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        button.setText('Open Editor')

        menu=QtWidgets.QMenu()
        self.open_editor_action=menu.addAction('Open Editor')
        self.choose_editor_action=menu.addAction('Choose Editor')
        button.setDefaultAction(self.open_editor_action)

        button.setIcon(QIcon.fromTheme('insert-text',
            self.style().standardIcon(QStyle.SP_FileDialogNewFolder)))
        button.setMenu(menu)
        button.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup)

        self.zim_tip_label=QtWidgets.QLabel()
        tip_icon=QIcon.fromTheme('help-about',
            self.style().standardIcon(QStyle.SP_MessageBoxInformation)).pixmap(
                    QSize(16,16))
        self.zim_tip_label.setPixmap(tip_icon)
        self.zim_tip_label.setToolTip('''Currently use zim as default note source. Change this in "Tools->Create Zim Notebook->Update from Zim Notes".''')

        if self.settings.value('saving/use_zim_default', type=bool):
            self.zim_tip_label.setVisible(True)
        else:
            self.zim_tip_label.setVisible(False)

        menu.triggered.connect(self.openEditorTriggered)
        ha=QtWidgets.QHBoxLayout()
        ha.addWidget(button, 1, Qt.AlignLeft)
        ha.addWidget(self.zim_tip_label)
        v_layout.addLayout(ha)

        self.note_textedit=NoteTextEdit(self.settings)
        self.note_textedit.note_edited_signal.connect(lambda x:\
                self.updateNotes(self._current_doc,
                    self.note_textedit.toPlainText(), x))

        v_layout.addWidget(self.note_textedit)
        frame.setLayout(v_layout)
        scroll.setWidget(frame)

        return scroll


    def createScratchTab(self):

        scroll=QtWidgets.QScrollArea(self)
        scroll.setWidgetResizable(True)
        frame=QtWidgets.QFrame()
        v_layout=QtWidgets.QVBoxLayout()

        self.scratchpad_textedit=QtWidgets.QTextEdit(self)
        self.scratchpad_textedit.setFont(self.settings.value(
            '/display/fonts/scratch_pad',QFont))
        self.scratchpad_textedit.setSizePolicy(getXExpandYExpandSizePolicy())

        v_layout.addWidget(self.scratchpad_textedit)
        frame.setLayout(v_layout)
        scroll.setWidget(frame)

        return scroll


    def createPDFTab(self):

        scroll=QtWidgets.QScrollArea(self)
        scroll.setWidgetResizable(True)
        frame=QtWidgets.QFrame()
        v_layout=QtWidgets.QVBoxLayout()
        self.pdf_viewer=PDFPreviewer(self)
        v_layout.addWidget(self.pdf_viewer)
        frame.setLayout(v_layout)
        scroll.setWidget(frame)

        return scroll



    def createBiBTab(self):

        frame=QtWidgets.QWidget()
        scroll=QtWidgets.QScrollArea(self)
        scroll.setWidgetResizable(True)
        scroll.setWidget(frame)
        v_layout=QtWidgets.QVBoxLayout()

        self.copy_bib_button=QtWidgets.QToolButton(self)
        self.copy_bib_button.setText('Copy')
        self.copy_bib_button.setIcon(QIcon.fromTheme('edit-copy',
                self.style().standardIcon(QStyle.SP_FileDialogDetailedView)))
        self.copy_bib_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        h_layout=QtWidgets.QHBoxLayout()
        h_layout.addWidget(self.copy_bib_button)
        h_layout.addStretch()

        self.bib_textedit=QtWidgets.QTextEdit(self)
        self.bib_textedit.setReadOnly(True)
        self.bib_textedit.setFont(self.settings.value('/display/fonts/bibtex',QFont))
        v_layout.addLayout(h_layout)
        v_layout.addWidget(self.bib_textedit)
        frame.setLayout(v_layout)
        self.copy_bib_button.clicked.connect(self.copyBibButtonClicked)

        return scroll
Exemplo n.º 57
0
class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        ## Initialization of variables
        # Time in simulation
        self.dt_fix = 0.01  # This is fixed timestep value
        self.dt = 0.0  # It is NOT the fixed timestep value. It is just a value for the first timestep

        # Stop threads if False
        self.run_thread_calculations = False
        self.run_thread_labels = True

        self.counter = 0
        self.real_time = 1
        self.save_history = True
        self.saved = 0

        # Create Cart object

        self.MyCart = Cart()

        ## Create GUI Layout
        layout = QVBoxLayout()
        # Change the window geometry
        self.setGeometry(300, 300, 2500, 1000)

        # Create layout for Matplotlib figures only
        # Draw Figure
        self.fig = Figure(
            figsize=(25, 10)
        )  # Regulates the size of Figure in inches, before scaling to window size.
        self.canvas = FigureCanvas(self.fig)
        self.fig.AxCart = self.canvas.figure.add_subplot(211)
        self.fig.AxSlider = self.canvas.figure.add_subplot(212)

        self.MyCart.draw_constant_elements(self.fig, self.fig.AxCart,
                                           self.fig.AxSlider)

        # Attach figure to the layout
        lf = QVBoxLayout()
        lf.addWidget(self.canvas)

        # Radiobuttons to toggle the mode of operation
        lr = QVBoxLayout()
        self.rb_manual = QRadioButton('Manual Stabilization')
        self.rb_PID = QRadioButton('PID-control with adjustable position')
        self.rb_manual.toggled.connect(self.RadioButtons)
        self.rb_PID.toggled.connect(self.RadioButtons)
        lr.addStretch(1)
        lr.addWidget(self.rb_manual)
        lr.addWidget(self.rb_PID)
        lr.addStretch(1)
        self.rb_manual.setChecked(True)

        # Create main part of the layout for Figures and radiobuttons
        # And add it to the whole layout
        lm = QHBoxLayout()
        lm.addLayout(lf)
        lm.addLayout(lr)
        layout.addLayout(lm)

        # Displays of current relevant values:
        # Time(user time, not necesarily time of the Cart (i.e. used in sinmulation)),
        # Speed, Angle and slider value
        ld = QHBoxLayout()
        self.labt = QLabel("Time (s): ")
        self.timer = QTimer()
        self.timer.setInterval(100)
        self.timer.timeout.connect(self.recurring_timer)
        self.timer.start()
        self.labs = QLabel('Speed (m/s):')
        self.laba = QLabel('Angle (deg):')
        self.labsl = QLabel('Slider:')
        ld.addWidget(self.labt)
        ld.addWidget(self.labs)
        ld.addWidget(self.laba)
        ld.addWidget(self.labsl)
        layout.addLayout(ld)

        #Buttons "START/STOP", "RESET", "QUIT"
        bss = QPushButton("START!/STOP!")
        bss.pressed.connect(self.play)
        br = QPushButton("RESET")
        br.pressed.connect(self.reset_button)
        bq = QPushButton("QUIT")
        bq.pressed.connect(self.quit_application)
        lb = QVBoxLayout()  #Layout for buttons
        lb.addWidget(bss)
        lb.addWidget(br)
        lb.addWidget(bq)
        layout.addLayout(lb)

        # Checkboxs:
        # to swich between real time and constant dt simulation
        # TODO to decide if to save the simulation history
        # TODO to decide if to plot simulation history
        cb = QCheckBox('Real time simulation', self)
        cb.toggle()
        cb.stateChanged.connect(self.real_time_simulation_f)
        layout.addWidget(cb)

        # Create an instance of a GUI window
        w = QWidget()
        w.setLayout(layout)
        self.setCentralWidget(w)
        self.show()
        self.setWindowTitle('CartPole')

        # This line introduces multithreading:
        # to ensure smooth functioning of the app,
        # the calculations and redrawing of the figures have to be done
        # in a different thread thqn the one cupturing the mouse position
        self.threadpool = QThreadPool()

        # This line links function cupturing the mouse position to the canvas of the Figure
        self.canvas.mpl_connect("motion_notify_event", self.on_mouse_movement)

        # Starts a thread constantly redrawing labels of the GUI
        # It runs till the QUIT button is pressed
        worker_labels = Worker(self.thread_labels)
        self.threadpool.start(worker_labels)

        # Defines a variable holding animation object
        self.anim = None
        # Start redrawing the changing elements of the Figure
        # It runs till the QUIT button is pressed
        self.run_animation()

    # Function evoked at a mouese movement
    # If the mouse coursor is over the lower chart it reads the coresponding value
    # and updates the slider
    def on_mouse_movement(self, event):
        if event.xdata == None or event.ydata == None:
            pass
        else:
            if event.inaxes == self.fig.AxSlider:
                self.MyCart.update_slider(mouse_position=event.xdata)

    # Method reseting variables
    def reset_variables(self):
        self.MyCart.reset_state()
        self.MyCart.reset_dict_history()
        self.counter = 0
        # "Try" because this function is called for the first time during initialisation of the Window
        # when the timer label instance is not yer there.
        try:
            self.labt.setText("Time (s): " + str(float(self.counter) / 10.0))
        except:
            pass
        self.saved = 0

    # This method initiate calculation of simulation and iterative updates of Cart state
    # It also measures time intervals for real time simulation
    # implements a termination condition if Pole went out of control
    # and initiate saving to a .csv file
    def thread_calculations(self):

        self.reset_variables()
        start = timeit.default_timer()

        while (self.run_thread_calculations):

            # Measuring real-time timestep
            stop = timeit.default_timer()

            if self.real_time == 1:
                self.dt = stop - start
            else:
                self.dt = self.dt_fix

            start = timeit.default_timer()

            # Calculations of the Cart state in the next timestep
            self.MyCart.update_state(dt=self.dt)

            # Finish simulation if angle bigger than 90 deg.
            if abs(self.MyCart.angle) > pi / 2:
                self.run_thread_calculations = 0
                break

            # Ensure that the animation drawing function can access MyCart at this moment
            QApplication.processEvents()

        # Save simulation history if user chose to do so at the end of the simulation
        if self.save_history:
            self.MyCart.save_history_csv()
            self.saved = 1

    # A thread redrawing labels (except for timer, which has its own function) of GUI every 0.1 s
    def thread_labels(self):
        while (self.run_thread_labels):
            self.labs.setText("Speed (m/s): " +
                              str(around(self.MyCart.CartPositionD, 2)))
            self.laba.setText("Angle (deg): " +
                              str(around(self.MyCart.angle * 360 /
                                         (2 * pi), 2)))
            if self.MyCart.mode == 0:
                self.labsl.setText("Motor power: " +
                                   str(around(self.MyCart.slider_value, 2)))
            elif self.MyCart.mode == 1:
                self.labsl.setText("Target position (m): " +
                                   str(around(self.MyCart.slider_value, 2)))
            sleep(0.1)

    # Actions to be taken when start/stop button is clicked
    def play(self):
        if self.run_thread_calculations == 1:
            self.run_thread_calculations = 0
        elif self.run_thread_calculations == 0:
            self.run_thread_calculations = 1
            # Pass the function to execute
            worker_calculations = Worker(self.thread_calculations)
            # Execute
            self.threadpool.start(worker_calculations)

    # Action to be taken when the reset button is clicked:
    # Stopping simulation, reseting variables, redrawing the Figure completly.
    def reset_button(self):
        # Stop calculating the next Cart state
        if self.run_thread_calculations == 1:
            self.run_thread_calculations = 0

            # If user is saving data wait till data is saved
            if self.save_history:
                while (self.saved == 0):
                    sleep(0.001)

        # Reset variables and redraw the figures
        self.reset_variables()
        # Draw figures
        self.MyCart.draw_constant_elements(self.fig, self.fig.AxCart,
                                           self.fig.AxSlider)
        self.canvas.draw()

    # The acctions which has to be taken to properly terminate the application
    # The method is evoked after QUIT button is pressed
    # TODO: Can we connect it somehow also the the default cross closing the application?
    def quit_application(self):
        # Stops animation (updating changing elements of the Figure)
        self.anim._stop()
        # Stops the two threads updating the GUI labels and updating the state of Cart instance
        self.run_thread_labels = False
        self.run_thread_calculations = False
        # Closes the GUI window
        self.close()
        # The standard command
        # It seems however not to be working by its own
        # I don't know how it works
        QApplication.quit()

    # Function to measure the time of simulation as experienced by user
    # It corresponds to the time of simulation according to equations only if real time mode is on
    def recurring_timer(self):
        # "If": Increment time counter only if simulation is running
        if self.run_thread_calculations == 1:
            self.counter += 1
            # The updates are done smoother if the label is updated here
            # and not in the separate thread
            self.labt.setText("Time (s): " + str(float(self.counter) / 10.0))

    # Action to be taken while a radio button is clicked
    # Toggle a mode of simulation: manual or PID
    def RadioButtons(self):
        # Change the mode variable depending on the Radiobutton state
        if self.rb_manual.isChecked():
            self.MyCart.mode = 0
        elif self.rb_PID.isChecked():
            self.MyCart.mode = 1

        # Reset the state of GUI and of the Cart instance after the mode has changed
        self.reset_variables()
        self.MyCart.draw_constant_elements(self.fig, self.fig.AxCart,
                                           self.fig.AxSlider)
        self.canvas.draw()

    # Action toggling between real time and fix time step mode
    def real_time_simulation_f(self, state):
        if state == Qt.Checked:
            self.real_time = 1
        else:
            self.real_time = 0

    # A function redrawing the changing elements of the Figure
    # This animation runs always when the GUI is open
    # The buttons of GUI only decide if new parameters are calculated or not
    def run_animation(self):
        def init():
            # Adding variable elements to the Figure
            self.fig.AxCart.add_patch(self.MyCart.Mast)
            self.fig.AxCart.add_patch(self.MyCart.Chassis)
            self.fig.AxCart.add_patch(self.MyCart.WheelLeft)
            self.fig.AxCart.add_patch(self.MyCart.WheelRight)
            self.fig.AxSlider.add_patch(self.MyCart.Slider)
            return self.MyCart.Mast, self.MyCart.Chassis, self.MyCart.WheelLeft, self.MyCart.WheelRight, self.MyCart.Slider

        def animationManage(i, MyCart):
            # Updating variable elements
            self.MyCart.update_drawing()
            # Special care has to be taken of the mast rotation
            self.MyCart.t2 = self.MyCart.t2 + self.fig.AxCart.transData
            self.MyCart.Mast.set_transform(self.MyCart.t2)
            return self.MyCart.Mast, self.MyCart.Chassis, self.MyCart.WheelLeft, self.MyCart.WheelRight, self.MyCart.Slider

        # Initialize animation object
        self.anim = animation.FuncAnimation(self.fig,
                                            animationManage,
                                            init_func=init,
                                            frames=300,
                                            fargs=(self.MyCart, ),
                                            interval=10,
                                            blit=True,
                                            repeat=True)
Exemplo n.º 58
0
class AIGameWindow(QMainWindow):
    """Application window for the baby AI game"""

    def __init__(self, env):
        super().__init__()
        self.initUI()

        # By default, manual stepping only
        self.fpsLimit = 0

        self.env = env
        self.lastObs = None

        self.resetEnv()

        self.stepTimer = QTimer()
        self.stepTimer.setInterval(0)
        self.stepTimer.setSingleShot(False)
        self.stepTimer.timeout.connect(self.stepClicked)
        self.stepIdx=1

        # Pointing and naming data
        self.pointingData = []

    def initUI(self):
        """Create and connect the UI elements"""

        self.resize(512, 512)
        self.setWindowTitle('Baby AI Game')

        # Full render view (large view)
        self.imgLabel = ImgWidget(self)
        self.imgLabel.setFrameStyle(QFrame.Panel | QFrame.Sunken)
        leftBox = QVBoxLayout()
        leftBox.addStretch(1)
        leftBox.addWidget(self.imgLabel)
        leftBox.addStretch(1)

        # Area on the right of the large view
        rightBox = self.createRightArea()

        # Arrange widgets horizontally
        hbox = QHBoxLayout()
        hbox.addLayout(leftBox)
        hbox.addLayout(rightBox)

        # Create a main widget for the window
        mainWidget = QWidget(self)
        self.setCentralWidget(mainWidget)
        mainWidget.setLayout(hbox)

        # Show the application window
        self.show()
        self.setFocus()

    def createRightArea(self):
        # Agent render view (partially observable)
        self.obsImgLabel = QLabel()
        self.obsImgLabel.setFrameStyle(QFrame.Panel | QFrame.Sunken)
        miniViewBox = QHBoxLayout()
        miniViewBox.addStretch(1)
        miniViewBox.addWidget(self.obsImgLabel)
        miniViewBox.addStretch(1)

        self.missionBox = QTextEdit()
        self.missionBox.setMinimumSize(500, 100)
        self.missionBox.textChanged.connect(self.missionEdit)

        buttonBox = self.createButtons()

        self.stepsLabel = QLabel()
        self.stepsLabel.setFrameStyle(QFrame.Panel | QFrame.Sunken)
        self.stepsLabel.setAlignment(Qt.AlignCenter)
        self.stepsLabel.setMinimumSize(60, 10)
        resetBtn = QPushButton("Reset")
        resetBtn.clicked.connect(self.resetEnv)
        stepsBox = QHBoxLayout()
        stepsBox.addStretch(1)
        stepsBox.addWidget(QLabel("Steps remaining"))
        stepsBox.addWidget(self.stepsLabel)
        stepsBox.addWidget(resetBtn)
        stepsBox.addStretch(1)

        hline2 = QFrame()
        hline2.setFrameShape(QFrame.HLine)
        hline2.setFrameShadow(QFrame.Sunken)

        # Stack everything up in a vetical layout
        vbox = QVBoxLayout()
        vbox.addLayout(miniViewBox)
        vbox.addLayout(stepsBox)
        vbox.addWidget(hline2)
        vbox.addWidget(QLabel("Mission"))
        vbox.addWidget(self.missionBox)
        vbox.addLayout(buttonBox)

        return vbox

    def createButtons(self):
        """Create the row of UI buttons"""

        stepButton = QPushButton("Step")
        stepButton.clicked.connect(self.stepClicked)

        minusButton = QPushButton("- Reward")
        minusButton.clicked.connect(self.minusReward)

        plusButton = QPushButton("+ Reward")
        plusButton.clicked.connect(self.plusReward)

        slider = QSlider(Qt.Horizontal, self)
        slider.setFocusPolicy(Qt.NoFocus)
        slider.setMinimum(0)
        slider.setMaximum(100)
        slider.setValue(0)
        slider.valueChanged.connect(self.setFrameRate)

        self.fpsLabel = QLabel("Manual")
        self.fpsLabel.setFrameStyle(QFrame.Panel | QFrame.Sunken)
        self.fpsLabel.setAlignment(Qt.AlignCenter)
        self.fpsLabel.setMinimumSize(80, 10)

        # Assemble the buttons into a horizontal layout
        hbox = QHBoxLayout()
        hbox.addStretch(1)
        hbox.addWidget(stepButton)
        hbox.addWidget(slider)
        hbox.addWidget(self.fpsLabel)
        hbox.addStretch(1)
        hbox.addWidget(minusButton)
        hbox.addWidget(plusButton)
        hbox.addStretch(1)

        return hbox

    def keyPressEvent(self, e):
        # Manual agent control
        actions = self.env.unwrapped.actions

        if e.key() == Qt.Key_Left:
            self.stepEnv(actions.left)
        elif e.key() == Qt.Key_Right:
            self.stepEnv(actions.right)
        elif e.key() == Qt.Key_Up:
            self.stepEnv(actions.forward)

        elif e.key() == Qt.Key_PageUp:
            self.stepEnv(actions.pickup)
        elif e.key() == Qt.Key_PageDown:
            self.stepEnv(actions.drop)
        elif e.key() == Qt.Key_Space:
            self.stepEnv(actions.toggle)

        elif e.key() == Qt.Key_Backspace:
            self.resetEnv()
        elif e.key() == Qt.Key_Escape:
            self.close()

    def mousePressEvent(self, event):
        """
        Clear the focus of the text boxes and buttons if somewhere
        else on the window is clicked
        """

        # Set the focus on the full render image
        self.imgLabel.setFocus()

        QMainWindow.mousePressEvent(self, event)

    def imageClick(self, x, y):
        """
        Pointing and naming logic
        """

        # Set the focus on the full render image
        self.imgLabel.setFocus()

        env = self.env.unwrapped
        imgW = self.imgLabel.size().width()
        imgH = self.imgLabel.size().height()

        i = (env.grid.width * x) // imgW
        j = (env.grid.height * y) // imgH
        assert i < env.grid.width
        assert j < env.grid.height

        print('grid clicked: i=%d, j=%d' % (i, j))

        desc, ok = QInputDialog.getText(self, 'Pointing & Naming', 'Enter Description:')
        desc = str(desc)

        if not ok or len(desc) == 0:
            return

        pointObj = env.grid.get(i, j)

        if pointObj is None:
            return

        print('description: "%s"' % desc)
        print('object: %s %s' % (pointObj.color, pointObj.type))

        viewSz = minigrid.AGENT_VIEW_SIZE

        NUM_TARGET = 50
        numItrs = 0
        numPos = 0
        numNeg = 0

        while (numPos < NUM_TARGET or numNeg < NUM_TARGET) and numItrs < 300:
            env2 = copy.deepcopy(env)

            # Randomly place the agent around the selected point
            x, y = i, j
            x += random.randint(-viewSz, viewSz)
            y += random.randint(-viewSz, viewSz)
            x = max(0, min(x, env2.grid.width - 1))
            y = max(0, min(y, env2.grid.height - 1))
            env2.agent_pos = (x, y)
            env2.agent_dir = random.randint(0, 3)

            # Don't want to place the agent on top of something
            if env2.grid.get(*env2.agent_pos) != None:
                continue

            agent_sees = env2.agent_sees(i, j)

            obs = env2.gen_obs()
            img = obs['image'] if isinstance(obs, dict) else obs
            obsGrid = minigrid.Grid.decode(img)

            datum = {
                'desc': desc,
                'img': img,
                'pos': (i, j),
                'present': agent_sees
            }

            if agent_sees and numPos < NUM_TARGET:
                self.pointingData.append(datum)
                numPos += 1

            if not agent_sees and numNeg < NUM_TARGET:
                # Don't want identical object in mismatch examples
                if (pointObj.color, pointObj.type) not in obsGrid:
                    self.pointingData.append(datum)
                    numNeg += 1

            numItrs += 1

        print('positive examples: %d' % numPos)
        print('negative examples: %d' % numNeg)
        print('total examples: %d' % len(self.pointingData))

    def missionEdit(self):
        # The agent will get the mission as an observation
        # before performing the next action
        text = self.missionBox.toPlainText()
        self.lastObs['mission'] = text

    def plusReward(self):
        print('+reward')
        self.env.setReward(1)

    def minusReward(self):
        print('-reward')
        self.env.setReward(-1)

    def stepClicked(self):
        self.stepEnv(action=None)

    def setFrameRate(self, value):
        """Set the frame rate limit. Zero for manual stepping."""

        print('Set frame rate: %s' % value)

        self.fpsLimit = int(value)

        if value == 0:
            self.fpsLabel.setText("Manual")
            self.stepTimer.stop()

        elif value == 100:
            self.fpsLabel.setText("Fastest")
            self.stepTimer.setInterval(0)
            self.stepTimer.start()

        else:
            self.fpsLabel.setText("%s FPS" % value)
            self.stepTimer.setInterval(int(1000 / self.fpsLimit))
            self.stepTimer.start()

    def resetEnv(self):
        obs = self.env.reset()
        self.lastObs = obs
        self.showEnv(obs)

    def showEnv(self, obs):
        unwrapped = self.env.unwrapped

        # Render and display the environment
        pixmap = self.env.render(mode='pixmap')
        self.imgLabel.setPixmap(pixmap)

        # Render and display the agent's view
        image = obs['image']
        obsPixmap = unwrapped.get_obs_render(image)
        self.obsImgLabel.setPixmap(obsPixmap)

        # Update the mission text
        mission = obs['mission']
        self.missionBox.setPlainText(mission)

        # Set the steps remaining
        stepsRem = unwrapped.steps_remaining
        self.stepsLabel.setText(str(stepsRem))

    def stepEnv(self, action=None):
Exemplo n.º 59
0
class BCPClient(object):
    def __init__(self,
                 mc,
                 receiving_queue,
                 sending_queue,
                 interface='localhost',
                 port=5051):

        self.mc = mc
        self.log = logging.getLogger('BCP Client')
        self.interface = interface
        self.port = port
        self.receive_queue = receiving_queue
        self.sending_queue = sending_queue
        self.connected = False
        self.socket = None
        self.sending_thread = None
        self.receive_thread = None
        self.done = False

        self.mc.log.info('Looking for MPF at %s:%s', self.interface, self.port)

        self.reconnect_timer = QTimer(self.mc)
        self.reconnect_timer.setInterval(1000)
        self.reconnect_timer.timeout.connect(self.connect_to_mpf)
        self.reconnect_timer.start()

    def connect_to_mpf(self, *args):
        del args

        if self.connected:
            return

        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        try:
            self.socket.connect((self.interface, self.port))
            self.connected = True
            # self.mc.reset_connection()
            self.log.info("Connected to MPF")

        except socket.error:
            self.socket = None

        if self.create_socket_threads():
            self.start_monitoring()

    def start_monitoring(self):
        self.sending_queue.put('monitor_start?category=devices')
        self.sending_queue.put('monitor_start?category=events')
        self.sending_queue.put('monitor_start?category=modes')

    def create_socket_threads(self):
        """Creates and starts the sending and receiving threads for the BCP
        socket.
        Returns:
            True if the socket exists and the threads were started. False if
            not.
        """

        if self.socket:

            self.receive_thread = threading.Thread(target=self.receive_loop)
            # self.receive_thread.daemon = True
            self.receive_thread.start()

            self.sending_thread = threading.Thread(target=self.sending_loop)
            # self.sending_thread.daemon = True
            self.sending_thread.start()

            return True

        else:
            return False

    def receive_loop(self):
        """The socket thread's run loop."""
        socket_chars = b''
        while self.connected and not self.mc.thread_stopper.is_set():
            try:
                ready = select.select([self.socket], [], [], 1)
                if ready[0]:
                    data_read = self.socket.recv(8192)
                    if data_read:
                        socket_chars += data_read
                        commands = socket_chars.split(b"\n")

                        # keep last incomplete command
                        socket_chars = commands.pop()

                        # process all complete commands
                        for cmd in commands:
                            if cmd:
                                self.process_received_message(cmd.decode())
                    else:
                        # no bytes -> socket closed
                        break

            except socket.timeout:
                pass

            except OSError:
                break

        self.connected = False

    def disconnect(self):
        if not self.connected:
            self.log.info("Disconnecting from BCP")
            self.sending_queue.put('goodbye', None)

    def close(self):
        try:
            self.socket.shutdown(socket.SHUT_RDWR)
            self.socket.close()

        except (OSError, AttributeError):
            pass

        self.socket = None
        self.connected = False

        with self.receive_queue.mutex:
            self.receive_queue.queue.clear()

        with self.sending_queue.mutex:
            self.sending_queue.queue.clear()

    def sending_loop(self):
        while self.connected and not self.mc.thread_stopper.is_set():
            try:
                msg = self.sending_queue.get(block=True, timeout=1)
            except queue.Empty:
                if self.mc.thread_stopper.is_set():
                    return

                else:
                    continue

            self.socket.sendall(('{}\n'.format(msg)).encode('utf-8'))

        self.connected = False

    def process_received_message(self, message):
        """Puts a received BCP message into the receiving queue.

        Args:
            message: The incoming BCP message

        """
        self.log.debug('Received "%s"', message)

        try:
            cmd, kwargs = bcp.decode_command_string(message)
            self.receive_queue.put((cmd, kwargs))
        except ValueError:
            self.log.error("DECODE BCP ERROR. Message: %s", message)
            raise

    def send(self, bcp_command, **kwargs):
        self.sending_queue.put(bcp.encode_command_string(
            bcp_command, **kwargs))
Exemplo n.º 60
0
class InstanceContainersModel(ListModel):
    NameRole = Qt.UserRole + 1  # Human readable name (string)
    IdRole = Qt.UserRole + 2  # Unique ID of the InstanceContainer
    MetaDataRole = Qt.UserRole + 3
    ReadOnlyRole = Qt.UserRole + 4
    SectionRole = Qt.UserRole + 5

    def __init__(self, parent=None):
        super().__init__(parent)
        self.addRoleName(self.NameRole, "name")
        self.addRoleName(self.IdRole, "id")
        self.addRoleName(self.MetaDataRole, "metadata")
        self.addRoleName(self.ReadOnlyRole, "readOnly")
        self.addRoleName(self.SectionRole, "section")

        #We keep track of two sets: One for normal containers that are already fully loaded, and one for containers of which only metadata is known.
        #Both of these are indexed by their container ID.
        self._instance_containers = {}  #type: Dict[str, InstanceContainer]
        self._instance_containers_metadata = {
        }  # type: Dict[str, Dict[str, Any]]

        self._section_property = ""

        # Listen to changes
        ContainerRegistry.getInstance().containerAdded.connect(
            self._onContainerChanged)
        ContainerRegistry.getInstance().containerRemoved.connect(
            self._onContainerChanged)
        ContainerRegistry.getInstance().containerLoadComplete.connect(
            self._onContainerLoadComplete)

        self._container_change_timer = QTimer()
        self._container_change_timer.setInterval(150)
        self._container_change_timer.setSingleShot(True)
        self._container_change_timer.timeout.connect(self._update)

        # List of filters for queries. The result is the union of the each list of results.
        self._filter_dicts = []  # type: List[Dict[str,str]]
        self._container_change_timer.start()

    ##  Handler for container added / removed events from registry
    def _onContainerChanged(self, container):
        # We only need to update when the changed container is a instanceContainer
        if isinstance(container, InstanceContainer):
            self._container_change_timer.start()

    ##  Private convenience function to reset & repopulate the model.
    def _update(self):
        #You can only connect on the instance containers, not on the metadata.
        #However the metadata can't be edited, so it's not needed.
        for container in self._instance_containers.values():
            container.metaDataChanged.disconnect(self._updateMetaData)

        self._instance_containers, self._instance_containers_metadata = self._fetchInstanceContainers(
        )

        for container in self._instance_containers.values():
            container.metaDataChanged.connect(self._updateMetaData)

        new_items = list(self._recomputeItems())
        if new_items != self._items:
            self.setItems(new_items)

    ##  Computes the items that need to be in this list model.
    #
    #   This does not set the items in the list itself. It is intended to be
    #   overwritten by subclasses that add their own roles to the model.
    def _recomputeItems(self):
        registry = ContainerRegistry.getInstance()
        result = []
        for container in self._instance_containers.values():
            result.append({
                "name":
                container.getName(),
                "id":
                container.getId(),
                "metadata":
                container.getMetaData().copy(),
                "readOnly":
                registry.isReadOnly(container.getId()),
                "section":
                container.getMetaDataEntry(self._section_property, ""),
                "weight":
                int(container.getMetaDataEntry("weight", 0))
            })
        for container_metadata in self._instance_containers_metadata.values():
            result.append({
                "name":
                container_metadata["name"],
                "id":
                container_metadata["id"],
                "metadata":
                container_metadata.copy(),
                "readOnly":
                registry.isReadOnly(container_metadata["id"]),
                "section":
                container_metadata.get(self._section_property, ""),
                "weight":
                int(container_metadata.get("weight", 0))
            })
        yield from sorted(result, key=self._sortKey)

    ##  Fetch the list of containers to display.
    #
    #   This method is intended to be overridable by subclasses.
    #
    #   \return A tuple of an ID-to-instance mapping that includes all fully
    #   loaded containers, and an ID-to-metadata mapping that includes the
    #   containers of which only the metadata is known.
    def _fetchInstanceContainers(
        self
    ) -> Tuple[Dict[str, InstanceContainer], Dict[str, Dict[str, Any]]]:
        registry = ContainerRegistry.getInstance()  #Cache this for speed.
        containers = {}  #Mapping from container ID to container.
        metadatas = {}  #Mapping from container ID to metadata.
        for filter_dict in self._filter_dicts:
            this_filter = registry.findInstanceContainersMetadata(
                **filter_dict)
            for metadata in this_filter:
                if metadata["id"] not in containers and metadata[
                        "id"] not in metadatas:  #No duplicates please.
                    if registry.isLoaded(
                            metadata["id"]
                    ):  #Only add it to the full containers if it's already fully loaded.
                        containers[metadata["id"]] = registry.findContainers(
                            id=metadata["id"])[0]
                    else:
                        metadatas[metadata["id"]] = metadata
        return containers, metadatas

    def setSectionProperty(self, property_name):
        if self._section_property != property_name:
            self._section_property = property_name
            self.sectionPropertyChanged.emit()
            self._container_change_timer.start()

    sectionPropertyChanged = pyqtSignal()

    @pyqtProperty(str, fset=setSectionProperty, notify=sectionPropertyChanged)
    def sectionProperty(self):
        return self._section_property

    ##  Set the filter of this model based on a string.
    #   \param filter_dict \type{Dict} Dictionary to do the filtering by.
    def setFilter(self, filter_dict: Dict[str, str]) -> None:
        self.setFilterList([filter_dict])

    filterChanged = pyqtSignal()

    @pyqtProperty("QVariantMap", fset=setFilter, notify=filterChanged)
    def filter(self) -> Dict[str, str]:
        return self._filter_dicts[0] if len(self._filter_dicts) != 0 else None

    ##  Set a list of filters to use when fetching containers.
    #
    #   \param filter_list \type{List[Dict]} List of filter dicts to fetch multiple
    #               sets of containers. The final result is the union of these sets.
    def setFilterList(self, filter_list):
        if filter_list != self._filter_dicts:
            self._filter_dicts = filter_list
            self.filterChanged.emit()
            self._container_change_timer.start()

    @pyqtProperty("QVariantList", fset=setFilterList, notify=filterChanged)
    def filterList(self):
        return self._filter_dicts

    @pyqtSlot(str, str)
    def rename(self, instance_id, new_name):
        if new_name != self.getName():
            containers = ContainerRegistry.getInstance(
            ).findInstanceContainers(id=instance_id)
            if containers:
                containers[0].setName(new_name)
                self._container_change_timer.start()

    ##  Gets a list of the possible file filters that the plugins have
    #   registered they can read or write. The convenience meta-filters
    #   "All Supported Types" and "All Files" are added when listing
    #   readers, but not when listing writers.
    #
    #   \param io_type \type{str} name of the needed IO type
    #   \return A list of strings indicating file name filters for a file
    #   dialog.
    @pyqtSlot(str, result="QVariantList")
    def getFileNameFilters(self, io_type):
        #TODO: This function should be in UM.Resources!
        filters = []
        all_types = []
        for plugin_id, meta_data in self._getIOPlugins(io_type):
            for io_plugin in meta_data[io_type]:
                filters.append(io_plugin["description"] + " (*." +
                               io_plugin["extension"] + ")")
                all_types.append("*.{0}".format(io_plugin["extension"]))

        if "_reader" in io_type:
            # if we're listing readers, add the option to show all supported files as the default option
            filters.insert(
                0,
                catalog.i18nc("@item:inlistbox", "All Supported Types ({0})",
                              " ".join(all_types)))

            filters.append(
                catalog.i18nc("@item:inlistbox", "All Files (*)")
            )  # Also allow arbitrary files, if the user so prefers.
        return filters

    @pyqtSlot(result=QUrl)
    def getDefaultPath(self):
        return QUrl.fromLocalFile(os.path.expanduser("~/"))

    ##  Gets a list of profile reader or writer plugins
    #   \return List of tuples of (plugin_id, meta_data).
    def _getIOPlugins(self, io_type):
        pr = PluginRegistry.getInstance()
        active_plugin_ids = pr.getActivePlugins()

        result = []
        for plugin_id in active_plugin_ids:
            meta_data = pr.getMetaData(plugin_id)
            if io_type in meta_data:
                result.append((plugin_id, meta_data))
        return result

    @pyqtSlot("QVariantList", QUrl, str)
    def exportProfile(self, instance_id, file_url, file_type):
        if not file_url.isValid():
            return
        path = file_url.toLocalFile()
        if not path:
            return
        ContainerRegistry.getInstance().exportProfile(instance_id, path,
                                                      file_type)

    @pyqtSlot(QUrl, result="QVariantMap")
    def importProfile(self, file_url):
        if not file_url.isValid():
            return
        path = file_url.toLocalFile()
        if not path:
            return
        return ContainerRegistry.getInstance().importProfile(path)

    def _sortKey(self, item):
        result = []
        if self._section_property:
            result.append(item.get(self._section_property, ""))

        result.append(
            not ContainerRegistry.getInstance().isReadOnly(item["id"]))
        result.append(int(item.get("weight", 0)))
        result.append(item["name"])

        return result

    def _updateMetaData(self, container):
        index = self.find("id", container.id)

        if self._section_property:
            self.setProperty(
                index, "section",
                container.getMetaDataEntry(self._section_property, ""))

        self.setProperty(index, "metadata", container.getMetaData())
        self.setProperty(index, "name", container.getName())
        self.setProperty(index, "id", container.getId())

    ##  If a container has loaded fully (rather than just metadata) we need to
    #   move it from the dict of metadata to the dict of full containers.
    def _onContainerLoadComplete(self, container_id):
        if container_id in self._instance_containers_metadata:
            del self._instance_containers_metadata[container_id]
            self._instance_containers[
                container_id] = ContainerRegistry.getInstance().findContainers(
                    id=container_id)[0]
            self._instance_containers[container_id].metaDataChanged.connect(
                self._updateMetaData)