class QNEMainWindow(QMainWindow): def __init__(self, parent): super(QNEMainWindow, self).__init__(parent) quitAct = QAction("&Quit", self, shortcut="Ctrl+Q", statusTip="Exit the application", triggered=self.close) addAct = QAction("&Add", self, statusTip="Add a block", triggered=self.addBlock) fileMenu = self.menuBar().addMenu("&File") fileMenu.addAction(addAct) fileMenu.addSeparator() fileMenu.addAction(quitAct) self.setWindowTitle("Node Editor") self.scene = QGraphicsScene(self) self.view = QGraphicsView(self) self.view.setScene(self.scene) self.view.setRenderHint(QPainter.Antialiasing) self.setCentralWidget(self.view) self.nodesEditor = QNodesEditor(self) self.nodesEditor.install(self.scene) block = QNEBlock(None) self.scene.addItem(block) block.addPort("test", 0, QNEPort.NamePort) block.addPort("TestBlock", 0, QNEPort.TypePort) block.addInputPort("in1"); block.addInputPort("in2"); block.addInputPort("in3"); block.addOutputPort("out1"); block.addOutputPort("out2"); block.addOutputPort("out3"); block = block.clone() block.setPos(150,0) block = block.clone() block.setPos(150,150) def addBlock(self): import random import math block = QNEBlock(None) self.scene.addItem(block) names = ["Vin", "Voutsadfasdf", "Imin", "Imax", "mul", "add", "sub", "div", "Conv", "FFT"] for i in range(0,math.floor(random.uniform(3,8))): block.addPort(random.choice(names), random.random()>0.5) block.setPos(self.view.sceneRect().center().toPoint())
class mainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.setWindowTitle("SimulationZ") self.setMinimumSize(700,500) self.workspaceLayout = QGridLayout() self.workspace = QTabWidget() self.workspace.setTabsClosable(True) self.graphScene = QGraphicsScene() self.sceneView = QGraphicsView() self.sceneView.setScene(self.graphScene) self.Editor = Editor(self.sceneView) self.menuBar = menuBar.menuBar(self) self.setMenuBar(self.menuBar) self.mainArea = Layout.layout(self.Editor,self.sceneView) self.setCentralWidget(self.mainArea) self.toolBar = self.mainArea.childAt(1,0) def exitFile(self): if self.i==0: userInfo =QMessageBox.question(self,self.language.Exit,self.language.RUSURE,QMessageBox.Yes | QMessageBox.No) if userInfo == QMessageBox.Yes: self.close() else: pass else: userInfo =QMessageBox.question(self,self.language.Exit,self.language.OTab,QMessageBox.Yes | QMessageBox.No) if userInfo == QMessageBox.Yes: self.close() else: pass
class QNEMainWindow(QMainWindow): def __init__(self, parent): super(QNEMainWindow, self).__init__(parent) self.logger = logging.getLogger("zne") self.logger.setLevel(logging.DEBUG) self.setMinimumSize(560,360) self.setWindowTitle("ZOCP Node Editor") self.setWindowIcon(QIcon('assets/icon.png')) self.scene = QGraphicsScene(self) self.view = QGraphicsView(self) self.view.setScene(self.scene) self.setCentralWidget(self.view) self.nodesEditor = QNodesEditor(self, self.scene, self.view) self.nodesEditor.onAddConnection = self.onAddConnection self.nodesEditor.onRemoveConnection = self.onRemoveConnection self.nodesEditor.onBlockMoved = self.onBlockMoved self.scale = 1 self.installActions() self.initZOCP() self.nodes = {} self.pendingSubscribers = {} QTimer.singleShot(250, lambda: self.scene.invalidate()) def closeEvent(self, *args): self.zocp.stop() def installActions(self): quitAct = QAction("&Quit", self, shortcut="Ctrl+Q", statusTip="Exit the application", triggered=self.close) if zconfigmanager_found: openAct = QAction("&Open...", self, shortcut="Ctrl+O", statusTip="Restore the network from a saved description", triggered=self.readNetwork) saveAct = QAction("&Save...", self, shortcut="Ctrl+S", statusTip="Write a description of the network to disc", triggered=self.writeNetwork) fileMenu = self.menuBar().addMenu("&File") if zconfigmanager_found: fileMenu.addAction(openAct) fileMenu.addAction(saveAct) fileMenu.addSeparator() fileMenu.addAction(quitAct) # for shortcuts self.view.addAction(quitAct) selectAllAct = QAction("Select &All", self, shortcut="Ctrl+A", triggered=self.nodesEditor.selectAll) selectNoneAct = QAction("Select &None", self, shortcut="Ctrl+D", triggered=self.nodesEditor.selectNone) selectInverseAct = QAction("Select &Inverse", self, shortcut="Ctrl+I", triggered=self.nodesEditor.selectInverse) deleteSelectedAct = QAction("&Delete Selected", self, shortcut="Del", triggered=self.nodesEditor.deleteSelected) editMenu = self.menuBar().addMenu("&Edit") editMenu.addAction(selectAllAct) editMenu.addAction(selectNoneAct) editMenu.addAction(selectInverseAct) editMenu.addSeparator() editMenu.addAction(deleteSelectedAct) self.view.addAction(selectAllAct) self.view.addAction(selectNoneAct) self.view.addAction(selectInverseAct) self.view.addAction(deleteSelectedAct) zoomInAct = QAction("Zoom &In", self, shortcut="Ctrl++", triggered=self.zoomIn) zoomOutAct = QAction("Zoom &Out", self, shortcut="Ctrl+-", triggered=self.zoomOut) zoomResetAct = QAction("&Reset Zoom", self, shortcut="Ctrl+0", triggered=self.zoomReset) viewMenu = self.menuBar().addMenu("&View") viewMenu.addAction(zoomInAct) viewMenu.addAction(zoomOutAct) viewMenu.addSeparator() viewMenu.addAction(zoomResetAct) self.view.addAction(zoomInAct) self.view.addAction(zoomOutAct) self.view.addAction(zoomResetAct) aboutAct = QAction("&About", self, triggered=self.about) helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction(aboutAct) self.view.addAction(aboutAct) def writeNetwork(self): fileName, filter = QFileDialog.getSaveFileName(self, caption="Save as", filter="ZOCP (*.zocp);;JSON (*.json)", selectedFilter="ZOCP (*.zocp)") if fileName: # setup ZOCP node, and run it for some time to discover # the current network configManager = ZConfigManagerNode("ConfigManager@%s" % socket.gethostname()) configManager.discover(0.5) # write network description to file configManager.write(fileName) # shut down ZOCP node configManager.stop() configManager = None def readNetwork(self): fileName, filter = QFileDialog.getOpenFileName(self, caption="Open", filter="All files (*.*);;ZOCP (*.zocp);;JSON (*.json)", selectedFilter="ZOCP (*.zocp)") if fileName: # setup ZOCP node, and run it for some time to discover # the current network configManager = ZConfigManagerNode("ConfigManager@%s" % socket.gethostname()) configManager.discover(0.5) # write network description to file configManager.read(fileName) # shut down ZOCP node configManager.stop() configManager = None def zoomIn(self): if self.scale < 4: self.scale *= 1.2 self.view.scale(1.2, 1.2) def zoomOut(self): if self.scale > 0.1: self.scale /= 1.2 self.view.scale(1/1.2, 1/1.2) def zoomReset(self): self.scale = 1 self.view.setTransform(QTransform()) def about(self): QMessageBox.about(self, "About ZOCP Node Editor", "<p>A monitor/editor for ZOCP nodes, implemented in PySide" "(Python/Qt4).</p><p>ZOCP is the Z25 Orchestration Control " "Protocol, currently in development at " "<a href='http://z25.org'>z25.org</a></p>") ######################################### # Node editor callbacks ######################################### def onAddConnection(self, connection, fromPort, toPort): fromBlock = fromPort.block() toBlock = toPort.block() emitter = fromPort.portName() emit_peer = fromBlock.uuid() receiver = toPort.portName() recv_peer = toBlock.uuid() self.zocp.signal_subscribe(recv_peer, receiver, emit_peer, emitter) self.logger.debug("added subscription from %s on %s to %s on %s" % (receiver, fromBlock.name(), emitter, toBlock.name())) def onRemoveConnection(self, connection, fromPort, toPort): fromBlock = fromPort.block() toBlock = toPort.block() emitter = fromPort.portName() emit_peer = fromBlock.uuid() receiver = toPort.portName() recv_peer = toBlock.uuid() self.zocp.signal_unsubscribe(recv_peer, receiver, emit_peer, emitter) self.logger.debug("removed subscription from %s on %s to %s on %s" % (receiver, fromBlock.name(), emitter, toBlock.name())) def onBlockMoved(self, block): pos = block.pos() peer = block.uuid() self.zocp.peer_set(peer, {"_zne_position": [pos.x(), pos.y()]}) def onChangeValue(self, block, port, value): self.logger.debug("block %s port %s changed to %s" % (block.name(), port.portName(), value)) peer = block.uuid() portName = port.portName() capability = self.zocp.peers_capabilities[peer][portName] typeHint = capability["typeHint"] validValue = True if typeHint == "int": try: value = int(float(value)) except: validValue = False elif typeHint == "flt": try: value = float(value.strip()) except: validValue = False elif typeHint == "percent": try: value = float(value.strip()) except: validValue = False elif typeHint == "bool": value = (value.strip().lower() in ["true", "yes", "1"]) elif typeHint == "string": pass elif typeHint.startswith("vec" ) and typeHint.endswith("f") and len(typeHint) == 5: try: value = [float(num) for num in ((value.strip())[1:-1]).split(",")] except: validValue = False if validValue: if len(value) != int(typeHint[3]): validValue = False if validValue: self.zocp.peer_set(peer, {portName: {"value": value}}) port.setValue(str(value)) else: port.setValue(str(capability["value"])) ######################################### # ZOCP implementation ######################################### def initZOCP(self): self.zocp = ZOCP("ZOCP Node Editor@%s" % socket.gethostname()) self.notifier = QSocketNotifier( self.zocp.inbox.getsockopt(zmq.FD), QSocketNotifier.Read) self.notifier.setEnabled(True) self.notifier.activated.connect(self.onZOCPEvent) self.zocp.on_peer_enter = self.onPeerEnter self.zocp.on_peer_exit = self.onPeerExit self.zocp.on_peer_modified = self.onPeerModified self.zocp.on_peer_signaled = self.onPeerSignaled self.zocp.start() zl = logging.getLogger("zocp") zl.setLevel(logging.INFO) def onZOCPEvent(self): self.zocp.run_once(0) def onPeerEnter(self, peer, name, *args, **kwargs): # Subscribe to any and all value changes self.zocp.signal_subscribe(self.zocp.uuid(), None, peer, None) # Add named block; ports are not known at this point block = QNEBlock(None) self.scene.addItem(block) block.setNodeEditor(self) block.setName(name) block.setUuid(peer) block.addPort(name, False, False, QNEPort.NamePort) block.setVisible(False) node = {} node["block"] = block node["ports"] = dict() self.nodes[peer.hex] = node def onPeerExit(self, peer, name, *args, **kwargs): # Unsubscribe from value changes self.zocp.signal_unsubscribe(self.zocp.uuid(), None, peer, None) # Remove block if peer.hex in self.nodes: self.nodes[peer.hex]["block"].delete() self.nodes.pop(peer.hex) def onPeerModified(self, peer, name, data, *args, **kwargs): for portname in data: portdata = data[portname] if portname not in self.nodes[peer.hex]["ports"]: if "access" in portdata: hasInput = "s" in portdata["access"] hasOutput = "e" in portdata["access"] port = self.nodes[peer.hex]["block"].addPort(portname, hasInput, hasOutput) port.setValue(str(portdata["value"])) port.setAccess(str(portdata["access"])) self.nodes[peer.hex]["ports"][portname] = port else: # Metadata, not a capability if portname == "_zne_position": block = self.nodes[peer.hex]["block"] block.setPos(portdata[0], portdata[1]) else: port = self.nodes[peer.hex]["ports"][portname] if "value" in portdata: port.setValue(str(portdata["value"])) if "access" in portdata: port.setAccess(str(portdata["access"])) if "subscribers" in portdata: self.updateSubscribers(port, portdata["subscribers"]) if len(self.nodes[peer.hex]["ports"]) > 0: self.nodes[peer.hex]["block"].setVisible(True) self.updatePendingSubscribers(peer) def onPeerSignaled(self, peer, name, data, *args, **kwargs): [portname, value] = data if portname in self.nodes[peer.hex]["ports"]: self.nodes[peer.hex]["ports"][portname].setValue(str(value)) def updateSubscribers(self, port, subscribers): port1 = port.outputPort # check if any current connections should be removed connections = port.connections() for connection in connections: if(connection.port1() == port1): port2 = connection.port2() else: port2 = connection.port1() if not port2.isOutput(): subscriber = [port2.block().uuid().hex, port2.portName()] if subscriber not in subscribers: connection.delete() self.logger.debug("peer removed subscription from %s on %s to %s on %s" % (port1.portName(), port1.block().name(), port2.portName(), port2.block().name())) # add new connections for new subscriptions for subscriber in subscribers: [uuid, portname] = subscriber if uuid in self.nodes: node = self.nodes[uuid] if portname in node["ports"]: port2 = node["ports"][portname] if not port2.isConnected(port1): # create new connection connection = QNEConnection(None) connection.setPort1(port1) connection.setPort2(port2) connection.updatePosFromPorts() connection.updatePath() self.scene.addItem(connection) self.logger.debug("peer added subscription from %s on %s to %s on %s" % (port1.portName(), port1.block().name(), port2.portName(), port2.block().name())) continue # if the connection could not be made yet, add it to a list of # pending subscriber-connections if uuid not in self.pendingSubscribers: self.pendingSubscribers[uuid] = [] self.pendingSubscribers[uuid].append([port1, portname]) def updatePendingSubscribers(self, peer): if peer.hex in self.pendingSubscribers: for subscriber in self.pendingSubscribers[peer.hex]: [port1, portname] = subscriber if peer.hex in self.nodes and portname in self.nodes[peer.hex]["ports"]: port2 = self.nodes[peer.hex]["ports"][portname] connection = QNEConnection(None) connection.setPort1(port1) connection.setPort2(port2) connection.updatePosFromPorts() connection.updatePath() self.scene.addItem(connection) else: # TODO: handle case where port is still not available pass self.pendingSubscribers.pop(peer.hex)
class SkyPython(QMainWindow): ''' The central widget and heart of the application. Performs all of the initialization of components and contains updates and event handling. ''' class RendererModelUpdateClosure(): ''' A runnable class that updates the state of the model after a change has occurred. Is placed on the event queue. ''' def run(self): pointing = self.model.get_pointing() direction_x = pointing.gc_line_of_sight.x direction_y = pointing.gc_line_of_sight.y direction_z = pointing.gc_line_of_sight.z up_x = pointing.gc_perpendicular.x up_y = pointing.gc_perpendicular.y up_z = pointing.gc_perpendicular.z self.renderer_controller.queue_set_view_orientation(direction_x, direction_y, direction_z, \ up_x, up_y, up_z) acceleration = self.model.acceleration self.renderer_controller.queue_text_angle(math.atan2(-acceleration.x, -acceleration.y)) self.renderer_controller.queue_viewer_up_direction(self.model.get_zenith().copy()) field_of_view = self.model.field_of_view self.renderer_controller.queue_field_of_view(field_of_view) def __init__(self, m_model, controller): ''' constructor ''' self.model = m_model self.renderer_controller = controller layer_manager = None model = None controller = None sky_renderer = None renderer_controller = None def pref_change(self, prefs, manager, layers, event): ''' If a preference button has been pressed, change the preferences according to the selection.. ''' for layer in layers: prefs.PREFERENCES[layer] = not prefs.PREFERENCES[layer] manager.on_shared_preference_change(prefs, layer) return True def eventFilter(self, source, event): ''' Check if the event is a button press in the UI, if not then check if it was a mouse or key press in DRZ_detector. ''' pref_pressed = self.pref_buttons.checkForButtonPress(source, event) zoom_pressed = self.zoom_button.checkForButtonPress(source, event, self.controller) if pref_pressed or zoom_pressed: self.show_menus_func() if pref_pressed: self.pref_change(self.shared_prefs, self.layer_manager, pref_pressed, event) else: update = self.DRZ_detector.on_motion_event(event) if pref_pressed or zoom_pressed or update: self.update_rendering() return True return QMainWindow.eventFilter(self, source, event) def initialize_model_view_controller(self): ''' Set up the graphics view/scene and set the GLWidget. Also sets up the the model and the controller for the model. ''' self.model = AstronomerModel(ZMDC()) # There is no onResume in python so start the controller group here self.controller = create_controller_group(self.shared_prefs) self.controller.set_model(self.model) self.controller.start() self.layer_manager = instantiate_layer_manager(self.model, self.shared_prefs) self.view = QGraphicsView() self.scene = QGraphicsScene() if self.DEBUG_MODE != None: self.sky_renderer = SkyRenderer(self.DEBUG_MODE) else: self.sky_renderer = SkyRenderer() self.sky_renderer.setAutoFillBackground(False) # set up the view with the glwidget inside self.view.setViewport(self.sky_renderer) self.view.setScene(self.scene) self.setCentralWidget(self.view) self.renderer_controller = RendererController(self.sky_renderer, None) self.renderer_controller.add_update_closure(\ self.RendererModelUpdateClosure(self.model, self.renderer_controller)) self.layer_manager.register_with_renderer(self.renderer_controller) self.wire_up_screen_controls() # NOTE: THIS BOOLEAN WILL NEED TO BE REMOVED EVENTUALLY self.magnetic_switcher = MDCS(self.model, self.USE_AUTO_MODE) self.run_queue() def wire_up_screen_controls(self): ''' Set up all of the screen controls, so that the user can zoom in/out and select which layers they wish to view. ''' screen_width = self.sky_renderer.render_state.screen_width screen_height = self.sky_renderer.render_state.screen_height self.pref_buttons = PreferencesButton(self.view) self.zoom_button = ZoomButton(self.view) position_y = ((screen_height - 336) / 2) + 1 self.pref_buttons.setGeometry(QtCore.QRect(1, position_y, 55, 336)) position_x = ((screen_width - 221) / 2) + 1 position_y = ((screen_height - 31) * 9/10) + 1 self.zoom_button.setGeometry(QtCore.QRect(position_x, position_y, 221, 31)) self.pref_buttons_fader = WidgetFader(self.pref_buttons, 2500) self.zoom_button_fader = WidgetFader(self.zoom_button, 2500) self.map_mover = MapMover(self.model, self.controller, self.shared_prefs, screen_height) self.DRZ_detector = DRZDetector(self.map_mover, self.show_menus_func) def show_menus_func(self): self.pref_buttons_fader.make_active() self.zoom_button_fader.make_active() def update_rendering(self): ''' re-render the sky and run updates ''' self.sky_renderer.updateGL() self.run_queue() self.sky_renderer.updateGL() def run_queue(self): ''' In the absence of another thread doing updates, force the updates by running all the runnables on the queue ''' num = len(list(self.renderer_controller.queuer.queue)) while num > 0: runnable = self.renderer_controller.queuer.get() runnable.run() self.renderer_controller.queuer.task_done() num -= 1 def __init__(self, debug_index=None): ''' Set up all state and control that the application requires. ''' QMainWindow.__init__(self) self.setAttribute(QtCore.Qt.WA_AcceptTouchEvents) self.DEBUG_MODE = debug_index self.USE_AUTO_MODE = False self.shared_prefs = SharedPreferences() self.initialize_model_view_controller() self.controller.set_auto_mode(self.USE_AUTO_MODE) # put the window at the screen position (100, 30) # with size 480 by 800 self.setGeometry(100, 30, 480, 800) self.show() # every 5 seconds re-render the sky and run updates self.update_rendering() self.timer = QtCore.QTimer(self) self.timer.timeout.connect(self.update_rendering) self.timer.setInterval(5000) self.timer.start()
class QNEMainWindow(QMainWindow): def __init__(self, parent): super(QNEMainWindow, self).__init__(parent) quitAct = QAction("&Quit", self, shortcut="Ctrl+Q", statusTip="Exit the application", triggered=self.close) addAct = QAction("&Add", self, statusTip="Add a block", triggered=self.addBlock) fileMenu = self.menuBar().addMenu("&File") fileMenu.addAction(addAct) fileMenu.addSeparator() fileMenu.addAction(quitAct) self.setWindowTitle("Node Editor") self.scene = QGraphicsScene(self) self.view = QGraphicsView(self) self.view.setScene(self.scene) self.view.setRenderHint(QPainter.Antialiasing) self.setCentralWidget(self.view) self.nodesEditor = QNodesEditor(self) self.nodesEditor.install(self.scene) block = QNEBlock(None) self.scene.addItem(block) block.addPort("test", 0, QNEPort.NamePort) block.addPort("TestBlock", 0, QNEPort.TypePort) block.addInputPort("in1") block.addInputPort("in2") block.addInputPort("in3") block.addOutputPort("out1") block.addOutputPort("out2") block.addOutputPort("out3") block = block.clone() block.setPos(150, 0) block = block.clone() block.setPos(150, 150) def addBlock(self): import random import math block = QNEBlock(None) self.scene.addItem(block) names = [ "Vin", "Voutsadfasdf", "Imin", "Imax", "mul", "add", "sub", "div", "Conv", "FFT" ] for i in range(0, math.floor(random.uniform(3, 8))): block.addPort(random.choice(names), random.random() > 0.5) block.setPos(self.view.sceneRect().center().toPoint())
class QNEMainWindow(QMainWindow): def __init__(self, parent): super(QNEMainWindow, self).__init__(parent) self.logger = logging.getLogger("zne") self.logger.setLevel(logging.DEBUG) self.setMinimumSize(560, 360) self.setWindowTitle("ZOCP Node Editor") self.setWindowIcon(QIcon('assets/icon.png')) self.scene = QGraphicsScene(self) self.view = QGraphicsView(self) self.view.setScene(self.scene) self.setCentralWidget(self.view) self.nodesEditor = QNodesEditor(self, self.scene, self.view) self.nodesEditor.onAddConnection = self.onAddConnection self.nodesEditor.onRemoveConnection = self.onRemoveConnection self.nodesEditor.onBlockMoved = self.onBlockMoved self.scale = 1 self.installActions() self.initZOCP() self.nodes = {} self.pendingSubscribers = {} QTimer.singleShot(250, lambda: self.scene.invalidate()) def closeEvent(self, *args): self.zocp.stop() def installActions(self): quitAct = QAction("&Quit", self, shortcut="Ctrl+Q", statusTip="Exit the application", triggered=self.close) fileMenu = self.menuBar().addMenu("&File") fileMenu.addAction(quitAct) # for shortcuts self.view.addAction(quitAct) selectAllAct = QAction("Select &All", self, shortcut="Ctrl+A", triggered=self.nodesEditor.selectAll) selectNoneAct = QAction("Select &None", self, shortcut="Ctrl+D", triggered=self.nodesEditor.selectNone) selectInverseAct = QAction("Select &Inverse", self, shortcut="Ctrl+I", triggered=self.nodesEditor.selectInverse) deleteSelectedAct = QAction("&Delete Selected", self, shortcut="Del", triggered=self.nodesEditor.deleteSelected) editMenu = self.menuBar().addMenu("&Edit") editMenu.addAction(selectAllAct) editMenu.addAction(selectNoneAct) editMenu.addAction(selectInverseAct) editMenu.addSeparator() editMenu.addAction(deleteSelectedAct) self.view.addAction(selectAllAct) self.view.addAction(selectNoneAct) self.view.addAction(selectInverseAct) self.view.addAction(deleteSelectedAct) zoomInAct = QAction("Zoom &In", self, shortcut="Ctrl++", triggered=self.zoomIn) zoomOutAct = QAction("Zoom &Out", self, shortcut="Ctrl+-", triggered=self.zoomOut) zoomResetAct = QAction("&Reset Zoom", self, shortcut="Ctrl+0", triggered=self.zoomReset) viewMenu = self.menuBar().addMenu("&View") viewMenu.addAction(zoomInAct) viewMenu.addAction(zoomOutAct) viewMenu.addSeparator() viewMenu.addAction(zoomResetAct) self.view.addAction(zoomInAct) self.view.addAction(zoomOutAct) self.view.addAction(zoomResetAct) aboutAct = QAction("&About", self, triggered=self.about) helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction(aboutAct) self.view.addAction(aboutAct) def zoomIn(self): if self.scale < 4: self.scale *= 1.2 self.view.scale(1.2, 1.2) def zoomOut(self): if self.scale > 0.1: self.scale /= 1.2 self.view.scale(1 / 1.2, 1 / 1.2) def zoomReset(self): self.scale = 1 self.view.setTransform(QTransform()) def about(self): QMessageBox.about( self, "About ZOCP Node Editor", "<p>A monitor/editor for ZOCP nodes, implemented in PySide" "(Python/Qt4).</p><p>ZOCP is the Z25 Orchestration Control " "Protocol, currently in development at " "<a href='http://z25.org'>z25.org</a></p>") ######################################### # Node editor callbacks ######################################### def onAddConnection(self, connection, fromPort, toPort): fromBlock = fromPort.block() toBlock = toPort.block() emitter = toPort.portName() emit_peer = toBlock.uuid() receiver = fromPort.portName() recv_peer = fromBlock.uuid() self.zocp.signal_subscribe(recv_peer, receiver, emit_peer, emitter) self.logger.debug( "added subscription from %s on %s to %s on %s" % (receiver, fromBlock.name(), emitter, toBlock.name())) def onRemoveConnection(self, connection, fromPort, toPort): fromBlock = fromPort.block() toBlock = toPort.block() emitter = toPort.portName() emit_peer = toBlock.uuid() receiver = fromPort.portName() recv_peer = fromBlock.uuid() self.zocp.signal_unsubscribe(recv_peer, receiver, emit_peer, emitter) self.logger.debug( "removed subscription from %s on %s to %s on %s" % (receiver, fromBlock.name(), emitter, toBlock.name())) def onBlockMoved(self, block): pos = block.pos() peer = block.uuid() self.zocp.peer_set(peer, {"_zne_position": [pos.x(), pos.y()]}) def onChangeValue(self, block, port, value): self.logger.debug("block %s port %s changed to %s" % (block.name(), port.portName(), value)) peer = block.uuid() portName = port.portName() capability = self.zocp.peers_capabilities[peer][portName] typeHint = capability["typeHint"] validValue = True if typeHint == "int": try: value = int(float(value)) except: validValue = False elif typeHint == "flt": try: value = float(value.strip()) except: validValue = False elif typeHint == "percent": try: value = float(value.strip()) except: validValue = False elif typeHint == "bool": value = (value.strip() in ["true", "yes", "1"]) elif typeHint == "string": pass elif typeHint == "vec2f" or typeHint == "vec3f" or typeHint == "vec4f": try: value = [ float(num) for num in ((value.strip())[1:-1]).split(",") ] except: validValue = False if validValue: if len(value) != int(typeHint[3]): validValue = False if validValue: self.zocp.peer_set(peer, {portName: {"value": value}}) port.setValue(str(value)) else: port.setValue(str(capability["value"])) ######################################### # ZOCP implementation ######################################### def initZOCP(self): self.zocp = ZOCP() self.zocp.set_name("ZOCP Node Editor@%s" % socket.gethostname()) self.notifier = QSocketNotifier(self.zocp.inbox.getsockopt(zmq.FD), QSocketNotifier.Read) self.notifier.setEnabled(True) self.notifier.activated.connect(self.onZOCPEvent) self.zocp.on_peer_enter = self.onPeerEnter self.zocp.on_peer_exit = self.onPeerExit self.zocp.on_peer_modified = self.onPeerModified self.zocp.on_peer_signaled = self.onPeerSignaled self.zocp.start() zl = logging.getLogger("zocp") zl.setLevel(logging.INFO) def onZOCPEvent(self): self.zocp.run_once(0) def onPeerEnter(self, peer, name, *args, **kwargs): # Subscribe to any and all value changes self.zocp.signal_subscribe(self.zocp.get_uuid(), None, peer, None) # Add named block; ports are not known at this point block = QNEBlock(None) self.scene.addItem(block) block.setNodeEditor(self) block.setName(name) block.setUuid(peer) block.addPort(name, False, False, QNEPort.NamePort) block.setVisible(False) node = {} node["block"] = block node["ports"] = dict() self.nodes[peer.hex] = node def onPeerExit(self, peer, name, *args, **kwargs): # Unsubscribe from value changes self.zocp.signal_unsubscribe(self.zocp.get_uuid(), None, peer, None) # Remove block if peer.hex in self.nodes: self.nodes[peer.hex]["block"].delete() self.nodes.pop(peer.hex) def onPeerModified(self, peer, name, data, *args, **kwargs): for portname in data: portdata = data[portname] if portname not in self.nodes[peer.hex]["ports"]: if "access" in portdata: hasInput = "s" in portdata["access"] hasOutput = "e" in portdata["access"] port = self.nodes[peer.hex]["block"].addPort( portname, hasInput, hasOutput) port.setValue(str(portdata["value"])) self.nodes[peer.hex]["ports"][portname] = port else: # Metadata, not a capability if portname == "_zne_position": block = self.nodes[peer.hex]["block"] block.setPos(portdata[0], portdata[1]) else: port = self.nodes[peer.hex]["ports"][portname] if "value" in portdata: port.setValue(str(portdata["value"])) if "subscribers" in portdata: self.updateSubscribers(port, portdata["subscribers"]) if len(self.nodes[peer.hex]["ports"]) > 0: self.nodes[peer.hex]["block"].setVisible(True) self.updatePendingSubscribers(peer) def onPeerSignaled(self, peer, name, data, *args, **kwargs): [portname, value] = data if portname in self.nodes[peer.hex]["ports"]: self.nodes[peer.hex]["ports"][portname].setValue(str(value)) def updateSubscribers(self, port, subscribers): port1 = port.outputPort # check if any current connections should be removed connections = port.connections() for connection in connections: if (connection.port1() == port1): port2 = connection.port2() else: port2 = connection.port1() if not port2.isOutput(): subscriber = [port2.block().uuid().hex, port2.portName()] if subscriber not in subscribers: connection.delete() self.logger.debug( "peer removed subscription from %s on %s to %s on %s" % (port1.portName(), port1.block().name(), port2.portName(), port2.block().name())) # add new connections for new subscriptions for subscriber in subscribers: [uuid, portname] = subscriber if uuid in self.nodes: node = self.nodes[uuid] if portname in node["ports"]: port2 = node["ports"][portname] if not port2.isConnected(port1): # create new connection connection = QNEConnection(None) connection.setPort1(port1) connection.setPort2(port2) connection.updatePosFromPorts() connection.updatePath() self.scene.addItem(connection) self.logger.debug( "peer added subscription from %s on %s to %s on %s" % (port1.portName(), port1.block().name(), port2.portName(), port2.block().name())) continue # if the connection could not be made yet, add it to a list of # pending subscriber-connections if uuid not in self.pendingSubscribers: self.pendingSubscribers[uuid] = [] self.pendingSubscribers[uuid].append([port1, portname]) def updatePendingSubscribers(self, peer): if peer.hex in self.pendingSubscribers: for subscriber in self.pendingSubscribers[peer.hex]: [port1, portname] = subscriber if peer.hex in self.nodes and portname in self.nodes[ peer.hex]["ports"]: port2 = self.nodes[peer.hex]["ports"][portname] connection = QNEConnection(None) connection.setPort1(port1) connection.setPort2(port2) connection.updatePosFromPorts() connection.updatePath() self.scene.addItem(connection) else: # TODO: handle case where port is still not available pass self.pendingSubscribers.pop(peer.hex)
class PostViewWidget(HorsePanel): def __init__(self, parent, order_overview_widget, find_order_slot): global configuration super(PostViewWidget, self).__init__(parent) self.set_panel_title(_("Post overview")) self.bold_font = QFont(self.font()) self.bold_font.setBold(True) self.nb_cols = 8 # Number of columns in the operation definition table self.order_overview_widget = order_overview_widget self.button = QPushButton(_("Refresh"), self) self.button.clicked.connect(self.refresh_action) self.sort_by_deadline_button = QRadioButton(_("By deadline"), self) self.sort_by_deadline_button.toggled.connect(self.sort_by_deadline) self.sort_by_size_button = QRadioButton(_("By hours left to do"), self) self.sort_by_size_button.toggled.connect(self.sort_by_size) # hlayout = QHBoxLayout() # hlayout.setObjectName("halyout") # hlayout.setContentsMargins(0,0,0,0) # hlayout.addWidget(self.sort_by_deadline_button) # hlayout.addWidget(self.sort_by_size_button) # hlayout.addWidget(self.button) # hlayout.addStretch() self.navbar = NavBar(self, [(self.sort_by_deadline_button, None), (self.sort_by_size_button, None), (self.button, None), (_("Find"), find_order_slot)]) self.navbar.buttons[3].setObjectName("specialMenuButton") self.vlayout = QVBoxLayout(self) self.vlayout.setObjectName("Vlayout") self.vlayout.addWidget( TitleWidget(_("Posts Overview"), self, self.navbar)) self._table_model = QStandardItemModel(1, self.nb_cols, self) self.table_view = QTableView(None) self.table_view.setModel(self._table_model) self.table_view.selectionModel().currentChanged.connect( self.operation_selected) self.table_view.verticalHeader().hide() self.table_view.horizontalHeader().hide() self.table_view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows) # This forces Qt to expand layout once I fill data in # FIXME dirty but I really don't get why setting # the mini width to something smaller (that happens at # startup, on first refresh) doesn't work self.table_view.setMinimumWidth(1) self.table_view.setMaximumWidth(1) self.post_view_scene = PostViewScene(self, order_overview_widget) self.post_view_scene_view = QGraphicsView(self) self.post_view_scene_view.setScene(self.post_view_scene) self.post_view_scene_view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.splitter = QSplitter(Qt.Horizontal) self.splitter.addWidget( SubFrame(_("Posts"), self.table_view, self.splitter)) self.splitter.addWidget( SubFrame(_("Workload"), self.post_view_scene_view, self.splitter)) # self.splitter.setStretchFactor(0,1) self.splitter.setStretchFactor(1, 1) self.vlayout.addWidget(self.splitter) # hlayout = QHBoxLayout() # hlayout.addWidget(SubFrame(_("Posts"),self.table_view,self)) # hlayout.addWidget(SubFrame(_("Workload"),self.post_view_scene_view,self)) # hlayout.setStretch(1,1) # self.vlayout.addLayout(hlayout) self.vlayout.setStretch(0, 0) self.vlayout.setStretch(1, 1) self.setLayout(self.vlayout) self.timer = QTimer(self) self.timer.timeout.connect(self.slidePostsScene) self.current_view_y = 0 def _data_load(self): global dao all_operations = dao.operation_dao.load_all_operations_ready_for_production( ) operation_definitions = dao.operation_definition_dao.all_direct_frozen( ) return operation_definitions, all_operations def _reset_operation_definitions(self, operations): self._table_model.setColumnCount(1) self._table_model.setRowCount(len(operations)) # BUG This should be refreshed on reload() too row = col = 0 first_active = None for opdef in operations: if opdef.operation_definition_id in self.post_view_scene.drawn_operations_data: # currently total planned time t = self.post_view_scene.drawn_operations_data[ opdef.operation_definition_id] ndx = self._table_model.index(row, col) if not first_active: first_active = ndx self._table_model.setData( ndx, u"{} {}".format(opdef.description, t), Qt.DisplayRole) # self._table_model.setData(ndx,self.bold_font,Qt.FontRole) self._table_model.setData(self._table_model.index(row, col), opdef.operation_definition_id, Qt.UserRole) row += 1 else: pass # self._table_model.setData(self._table_model.index(row,col),opdef.description,Qt.DisplayRole) # = col + 1 # if col == self.nb_cols: # col = 0 # row += 1 self._table_model.setRowCount(row) # self.table_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) # self.vlayout.setStretch(0,0) # self.vlayout.setStretch(1,10) # self.vlayout.setStretch(2,10000) # height = 0 # for c in range(self.table_view.model().rowCount()): # height += self.table_view.rowHeight(c) + 1 # +1 for cell border # self.table_view.setMinimumHeight(height) # self.table_view.setMaximumHeight(height) for i in range(self.nb_cols): self.table_view.resizeColumnToContents(i) self.table_view.setMaximumWidth(self.table_view.columnWidth(0)) self.table_view.setMinimumWidth(self.table_view.columnWidth(0)) self.table_view.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Preferred) self.table_view.update() self.splitter.update() return first_active def slide_to_operation(self, opdef_id): if opdef_id in self.post_view_scene.posts_offsets: self.slide_target_opdef_id = opdef_id # mainlog.debug("Target y = {}".format(self.post_view_scene.posts_offsets[self.slide_target_opdef])) self.timer.start(20) @Slot() def slidePostsScene(self): if self.slide_target_opdef_id is None: return # self.post_view_scene_view self.post_view_scene.set_cursor_on( self.slide_target_opdef_id ) # This done here also aviod some screen trashing v = self.post_view_scene_view.verticalScrollBar().value() # mainlog.debug( "slidePostsScene : {}".format(v)) r = self.post_view_scene.posts_offsets[self.slide_target_opdef_id] target_y = r.y() + r.height() / 2 delta = (target_y - self.current_view_y) * 0.4 self.current_view_y = self.current_view_y + delta self.post_view_scene_view.centerOn(0, self.current_view_y) # mainlog.debug( "slidePostsScene : {} / {}".format(target_y, self.current_view_y)) if self.post_view_scene_view.verticalScrollBar().value() == v: # Close enough => stop moving # FIXME not correct because we must stop when the view stops moving, not when the goal we set for centerOn is reached self.timer.stop() @Slot(QModelIndex, QModelIndex) def operation_selected(self, ndx_cur, ndx_old): if ndx_cur.isValid(): opdef = self._table_model.data(ndx_cur, Qt.UserRole) if opdef: self.slide_to_operation( self._table_model.data(ndx_cur, Qt.UserRole)) @Slot() def refresh_action(self): # FIXME reload operations as well operation_definitions, all_operations = self._data_load() # mainlog.debug("reload") if self.sort_by_deadline_button.isChecked(): self.post_view_scene.reload(self, operation_definitions, all_operations, 1) elif self.sort_by_size_button.isChecked(): self.post_view_scene.reload(self, operation_definitions, all_operations, 2) else: self.post_view_scene.reload(self, operation_definitions, all_operations, 0) # mainlog.debug("reset") first_active = self._reset_operation_definitions(operation_definitions) # self.table_view.selectionModel().currentChanged.connect(self.operation_selected) if first_active: self.table_view.setCurrentIndex(first_active) # mainlog.debug("done reset") @Slot(bool) def sort_by_deadline(self, checked): if checked: self.refresh_action() @Slot(bool) def sort_by_size(self, checked): if checked: self.refresh_action() order_part_double_clicked = Signal(int) # Callback that will be called by HooverBar def set_on_order_part(self, order_part_id): self.order_part_double_clicked.emit(order_part_id)
class UVViewer(QDialog): def __init__(self, model, w, h, parent=None): super(UVViewer, self).__init__(parent) self.w = w self.h = h self.mdl = model self.white_b = QBrush(Qt.white) self.black_b = QBrush(Qt.black) self.pen_width = 2 self.initUI() def initUI(self): mainlay = QVBoxLayout() scn = QGraphicsScene(0, 0, self.w, self.h) self.view = QGraphicsView() self.view.setScene(scn) self.view.setSceneRect(QRectF(0, 0, self.w, self.h)) self.view.setMaximumWidth(self.w) self.view.setMaximumHeight(self.h) mainlay.addWidget(self.view) btns = QHBoxLayout() btns.addStretch() self.pen_w = QSpinBox() self.pen_w.setValue(self.pen_width) redraw = QPushButton('Redraw') redraw.clicked.connect(self.draw_uvs) save = QPushButton('Save') save.clicked.connect(self.save) close = QPushButton('Close') close.clicked.connect(self.close) btns.addWidget(QLabel('Stroke Width')) btns.addWidget(self.pen_w) btns.addWidget(redraw) btns.addWidget(save) btns.addWidget(close) mainlay.addLayout(btns) self.draw_uvs() self.setLayout(mainlay) self.setGeometry(340, 340, 512, 560) self.setWindowTitle('MSH Suite UV Viewer') self.show() def draw_uvs(self): self.img = QPixmap(QSize(self.w, self.h)) pen = QPen() pen.setWidth(int(self.pen_w.text())) pen.setBrush(QBrush(Qt.white)) pen.setColor(QColor('white')) painter = QPainter() painter.begin(self.img) painter.setPen(pen) coords = self.get_coords() for face in coords: for n in xrange(len(face) - 1): print face[n][0], face[n][1], face[n + 1][0], face[n + 1][1] painter.drawLine(face[n][0], face[n][1], face[n + 1][0], face[n + 1][1]) painter.end() self.view.scene().addPixmap(self.img) def get_coords(self): coords = [] for seg in self.mdl.segments: if seg.classname == 'SegmentGeometry': print 'doing stuff' vcoll = seg.vertices for face in seg.faces: face_coords = [] for v in face.SIindices(): face_coords.append((vcoll[v].u * self.w, (1 - vcoll[v].v) * self.h)) face_coords.append((vcoll[face.vertices[0]].u * self.w, (1 - vcoll[face.vertices[0]].v) * self.h)) coords.append(face_coords) #print face_coords return coords def save(self): filename, _ = QFileDialog.getSaveFileName(self, 'Save UV Mesh', os.getcwd(), 'PNG Files (*.png)') if not filename: return self.img.save(filename, 'PNG') self.close()
class OrderWorkflowDialog(QDialog): def _makeArrow( self, ra, rb, both_ways=True): ca = QPointF( ra.x() + float(ra.width()) / 2.0, ra.y() + float(ra.height()) / 2.0) cb = QPointF( rb.x() + rb.width() / 2.0, rb.y() + rb.height() / 2.0) dx = cb.x() - ca.x() dy = cb.y() - ca.y() d = dist(dx,dy) a = math.atan2(float(dy),float(dx)) # The result is between -pi and pi # ra_rad = dist(ra.width() / 2, ra.height() / 2) # rb_rad = dist(rb.width() / 2, rb.height() / 2) x,y = squircle(ra.width() / 2, ra.height() / 2, to_squircle_angle(ra.width() / 2, ra.height() / 2,a)) ra_rad = dist(x,y) + 10 # painter.drawLine(ca,QPoint(ca.x() + x, ca.y() + y)) na = a if a < 0: na += math.pi else: na -= math.pi x,y = squircle(float(rb.width()) / 2.0, float(rb.height()) / 2.0, to_squircle_angle(float(rb.width()) / 2.0, float(rb.height()) / 2.0,na)) rb_rad = dist(x,y)+10 # ra_rad = rb_rad = 0 # painter.drawLine(ca,cb) # painter.drawRect(ra) # painter.setPen(Qt.GlobalColor.red) # painter.drawRect(rb) # painter.setPen(Qt.GlobalColor.black) # for t in range(100): # ang = 6.28/100.0*float(t) # x,y = squircle( 200,50,ang) # painter.drawPoint(300 + x,100 + y) qp = QPolygonF() h = 5 v = [] v.append(QPointF(ra_rad,0)) if not both_ways: v.append(QPointF(ra_rad,-h/2)) else: v.append(QPointF(ra_rad + 2*h,-2*h)) v.append(QPointF(ra_rad + 2*h,-h/2)) v.append(QPointF(d - rb_rad - 2*h,-h/2)) v.append(QPointF(d - rb_rad - 2*h,-2*h)) v.append(QPointF(d - rb_rad, 0)) v.append(QPointF(d - rb_rad - 2*h,+2*h)) v.append(QPointF(d - rb_rad - 2*h,+h/2)) if not both_ways: v.append(QPointF(ra_rad,+h/2)) else: v.append(QPointF(ra_rad + 2*h,+h/2)) v.append(QPointF(ra_rad + 2*h,+2*h)) v.append(QPointF(ra_rad,0)) p = QPolygonF(v) item = QGraphicsPolygonItem(p) item.translate(ca.x(),ca.y()) item.rotate(math.degrees(a)) return item def _drawNodes(self,scene,selected_state,initial_state): for i in scene.items(): scene.removeItem(i) pos = dict() sx = 1 sy = 1 if configuration.font_select: sx = sx * 2 sy = sy pos[OrderStatusType.preorder_definition] = (200*sx,200*sy) pos[OrderStatusType.order_definition] = (10*sx,300*sy) pos[OrderStatusType.order_ready_for_production] = (300*sx,300*sy) pos[OrderStatusType.order_production_paused] = (200*sx,550*sy) pos[OrderStatusType.order_completed] = (400*sx,600*sy) pos[OrderStatusType.order_aborted] = (10*sx,600*sy) mainlog.debug("_drawNodes : selected_state {}".format(selected_state)) mainlog.debug("_drawNodes : initial_state {}".format(initial_state)) for org in OrderStatusType.symbols(): x,y = pos[org] item = QGraphicsSimpleTextItem(org.description) if (selected_state and org in OrderStatusType.next_states(selected_state)) or org == initial_state or org == selected_state: item = MyQGraphicsSimpleTextItem(org, self) item.setFlags( item.flags() | QGraphicsItem.ItemIsSelectable) if org == selected_state: mainlog.debug("Preselcting {}".format(org)) item.setSelected(True) item.setFont(self.thinfont) item.setPos(x,y) scene.addItem(item) pos[org] = item.boundingRegion( item.sceneTransform()).boundingRect() # scene.addRect(pos[org]) g_orgs = dict() for org in OrderStatusType.symbols(): g_orgs[org] = OrderStatusType.next_states(org) # Draw all arrows which don't end up or leave the selected_state drawn = [] for org,dests in g_orgs.iteritems(): for dest in dests: if selected_state != org and ((org,dest) not in drawn) and ((dest,org) not in drawn): # If an arrow must have two directions, we draw it # like that directly. item = self._makeArrow( pos[org],pos[dest],dest in g_orgs and org in g_orgs[dest]) item.setPen(self.grey_pen) scene.addItem( item) drawn.append((org,dest)) if initial_state: scene.addRect(pos[initial_state]) if selected_state: for dest in OrderStatusType.next_states(selected_state): item = self._makeArrow( pos[selected_state],pos[dest],False) item.setBrush(QBrush(Qt.green)) scene.addItem( item) drawn.append((org,dest)) @Slot() def selectionChanged(self): pass def __init__(self,parent): global configuration super(OrderWorkflowDialog,self).__init__(parent) self.grey_pen = QPen(Qt.gray) title = _("Order workflow") self.setWindowTitle(title) self.title_widget = TitleWidget(title,self) self.buttons = QDialogButtonBox() self.buttons.addButton( QDialogButtonBox.Ok) self.buttons.addButton( QDialogButtonBox.Cancel) self.thinfont = QFont() if configuration.font_select: self.thinfont.setPointSize(self.thinfont.pointSize()*2) self.scene = QGraphicsScene() self.scene.selectionChanged.connect(self.selectionChanged) self.view = QGraphicsView(self) self.view.setRenderHint(QPainter.Antialiasing) self.view.setScene(self.scene) top_layout = QVBoxLayout() top_layout.addWidget(self.title_widget) top_layout.addWidget(self.view) top_layout.addWidget(self.buttons) self.setLayout(top_layout) # QWidget takes ownership of the layout self.buttons.accepted.connect(self.save_and_accept) self.buttons.rejected.connect(self.cancel) self.initial_state = None self.selected_state = None self._drawNodes(self.scene,self.selected_state,self.initial_state) def set_selected_state(self,selected_state,initial_state): mainlog.debug("selected_state {}".format(selected_state)) mainlog.debug("initial_state {}".format(initial_state)) self.initial_state = initial_state self.selected_state = selected_state self._drawNodes(self.scene,selected_state,self.initial_state) @Slot() def cancel(self): return super(OrderWorkflowDialog,self).reject() @Slot() def save_and_accept(self): super(OrderWorkflowDialog,self).accept() s = self.scene.selectedItems() if s: # In cas there are more than one item selected, # we pick the first one (arbitrarily) self.selected_state = s[0].state else: self.selected_state = self.initial_state