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()
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)
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)
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)
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()
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()
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)
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()
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)
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
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()
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)
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()
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()
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()
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)
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()
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
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
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))
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_()
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
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)
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
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()
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()
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
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()
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()
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}')
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
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
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()
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)
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
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)
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) '''
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
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)
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
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]
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)
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)
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()
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()
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()])
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()
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.')
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()
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
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
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)
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):
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))
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)