def __init__(self, parent=None):
        QtGui.QMainWindow.__init__(self, parent)
        self.setWindowTitle("QtSimiam")
        self.setWindowIcon(QtGui.QIcon("./res/image/appicon.png"))
        self.resize(700, 700)

        self.create_actions()
        self.create_toolbars()
        self.create_menu()
        self.create_statusbar()
        # Set intro message
        self.status_label.setText("Welcome to QtSimiam")

        # create XML file dialog
        self.world_dialog = QtGui.QFileDialog(self, "Select World File",
                                              "worlds", "WorldFile (*.xml)")
        self.world_dialog.setAcceptMode(QtGui.QFileDialog.AcceptOpen)
        self.world_dialog.setFileMode(QtGui.QFileDialog.ExistingFile)

        # create supervisor file dialog
        self.supervisor_dialog = QtGui.QFileDialog(self,
                                                   "Select Supervisor File",
                                                   "supervisors",
                                                   "Supervisor (*.py)")
        self.supervisor_dialog.setAcceptMode(QtGui.QFileDialog.AcceptOpen)
        self.supervisor_dialog.setFileMode(QtGui.QFileDialog.ExistingFile)

        scrollArea = QtGui.QScrollArea(self)
        self.setCentralWidget(scrollArea)
        self.viewer = SimulatorViewer()
        self.viewer.resized.connect(self.refresh_view)
        scrollArea.setWidget(self.viewer)
        scrollArea.setWidgetResizable(True)

        self.__clear_graph_on_start = False
        self.plots = []

        #self.setDockOptions(QtGui.QMainWindow.AllowNestedDocks)
        self.setDockOptions(QtGui.QMainWindow.DockOptions())

        self.coursera_dock = None
        self.logdock = None
        self.add_logdock()

        self.dockmanager = DockManager(self)
        self.dockmanager.apply_request.connect(self.apply_parameters)

        self.sim_timer = QtCore.QTimer(self)
        self.sim_timer.setInterval(10)
        self.sim_timer.timeout.connect(self.update_time)

        SimUI.__init__(self, self.viewer.renderer)

        self.sim_timer.start()
Exemple #2
0
    def __init__(self,parent=None):
        QtGui.QMainWindow.__init__(self,parent)
        self.setWindowTitle("QtSimiam")
        self.setWindowIcon(QtGui.QIcon("./res/image/appicon.png"))
        self.resize(700,700)
        
        self.create_actions()
        self.create_toolbars()
        self.create_menu()
        self.create_statusbar()
        # Set intro message
        self.status_label.setText("Welcome to QtSimiam")
        
        # create XML file dialog
        self.world_dialog = QtGui.QFileDialog(self,
                                "Select World File",
                                "worlds", 
                                "WorldFile (*.xml)")
        self.world_dialog.setAcceptMode(QtGui.QFileDialog.AcceptOpen)
        self.world_dialog.setFileMode(QtGui.QFileDialog.ExistingFile)     

        # create supervisor file dialog
        self.supervisor_dialog = QtGui.QFileDialog(self,
                                     "Select Supervisor File",
                                     "supervisors", 
                                     "Supervisor (*.py)")
        self.supervisor_dialog.setAcceptMode(QtGui.QFileDialog.AcceptOpen)
        self.supervisor_dialog.setFileMode(QtGui.QFileDialog.ExistingFile)     
        
        scrollArea = QtGui.QScrollArea(self)
        self.setCentralWidget(scrollArea)
        self.viewer = SimulatorViewer()
        self.viewer.resized.connect(self.refresh_view)
        scrollArea.setWidget(self.viewer)
        scrollArea.setWidgetResizable(True)
        
        self.__clear_graph_on_start = False
        self.plots = []

        #self.setDockOptions(QtGui.QMainWindow.AllowNestedDocks)
        self.setDockOptions(QtGui.QMainWindow.DockOptions())
        
        self.coursera_dock = None
        self.logdock = None
        self.add_logdock()

        self.dockmanager = DockManager(self)
        self.dockmanager.apply_request.connect(self.apply_parameters)

        self.sim_timer = QtCore.QTimer(self)
        self.sim_timer.setInterval(10)
        self.sim_timer.timeout.connect(self.update_time)
        
        SimUI.__init__(self,self.viewer.renderer)

        self.sim_timer.start()
Exemple #3
0
class SimulationWidget(SimUI, QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QMainWindow.__init__(self, parent)
        self.setWindowTitle("QtSimiam")
        self.setWindowIcon(QtGui.QIcon("./res/image/appicon.png"))
        self.resize(700, 700)

        self.create_actions()
        self.create_toolbars()
        self.create_menu()
        self.create_statusbar()
        # Set intro message
        self.status_label.setText("Welcome to QtSimiam")

        # create XML file dialog
        self.world_dialog = QtGui.QFileDialog(self, "Select World File",
                                              "worlds", "WorldFile (*.xml)")
        self.world_dialog.setAcceptMode(QtGui.QFileDialog.AcceptOpen)
        self.world_dialog.setFileMode(QtGui.QFileDialog.ExistingFile)

        # create supervisor file dialog
        self.supervisor_dialog = QtGui.QFileDialog(self,
                                                   "Select Supervisor File",
                                                   "supervisors",
                                                   "Supervisor (*.py)")
        self.supervisor_dialog.setAcceptMode(QtGui.QFileDialog.AcceptOpen)
        self.supervisor_dialog.setFileMode(QtGui.QFileDialog.ExistingFile)

        scrollArea = QtGui.QScrollArea(self)
        self.setCentralWidget(scrollArea)
        self.viewer = SimulatorViewer()
        self.viewer.resized.connect(self.refresh_view)
        scrollArea.setWidget(self.viewer)
        scrollArea.setWidgetResizable(True)

        #self.setDockOptions(QtGui.QMainWindow.AllowNestedDocks)
        self.setDockOptions(QtGui.QMainWindow.DockOptions())

        self.coursera_dock = None
        self.logdock = None
        self.add_logdock()

        self.dockmanager = DockManager(self)
        self.dockmanager.apply_request.connect(self.apply_parameters)

        self.sim_timer = QtCore.QTimer(self)
        self.sim_timer.setInterval(10)
        self.sim_timer.timeout.connect(self.update_time)

        SimUI.__init__(self, self.viewer.renderer)

        self.sim_timer.start()

    def create_actions(self):

        self.open_world_action = \
            QtGui.QAction(QtGui.QIcon.fromTheme("document-open",
                            QtGui.QIcon("./res/image/open.png")),
                          "Open XML &World",
                          self)
        self.open_world_action.triggered.connect(self.on_open_world)
        self.open_world_action.setShortcut(
            QtGui.QKeySequence(QtGui.QKeySequence.Open))

        self.open_world_action.setStatusTip("Open a new simulation")

        self.exit_action = \
            QtGui.QAction(QtGui.QIcon.fromTheme("application-exit"),
                    "E&xit",
                    self)
        self.exit_action.triggered.connect(self.close)
        self.exit_action.setShortcut(
            QtGui.QKeySequence(QtGui.QKeySequence.Quit))
        self.exit_action.setToolTip("Quit the Program")
        self.exit_action.setStatusTip("Exit QtSimiam")

        self.rev_action = \
            QtGui.QAction(QtGui.QIcon.fromTheme("media-seek-backward",
                            QtGui.QIcon("./res/image/arrow-left-double.png")),
                          "Rewind", self)
        self.rev_action.triggered.connect(self.on_rewind)
        self.rev_action.setStatusTip("Reset simulation")

        self.run_action = PlayPauseAction(self, self.on_run, self.on_pause)
        self.run_action.setEnabled(False)

        self.step_action = \
            QtGui.QAction(QtGui.QIcon.fromTheme("media-skip-forward",
                            QtGui.QIcon("./res/image/media-skip-forward-7.png")),
                          "Step", self)
        self.step_action.triggered.connect(self.on_step)
        self.step_action.setStatusTip("Do one simulation step")
        self.step_action.setEnabled(False)

        self.grid_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/grid.png"),
                          "Show/Hide grid", self)
        self.grid_action.setStatusTip("Show/hide grid")
        self.grid_action.triggered[bool].connect(self.show_grid)
        self.grid_action.setCheckable(True)
        self.grid_action.setChecked(False)

        self.sens_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/robot-sensors.png"),
                          "Show/Hide sensors", self)
        self.sens_action.setStatusTip("Show/hide robot sensors")
        self.sens_action.triggered[bool].connect(self.show_sensors)
        self.sens_action.setCheckable(True)
        self.sens_action.setChecked(True)

        self.trace_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/robot-tracks.png"),
                          "Show/Hide robot trajectores", self)
        self.trace_action.setStatusTip("Show/hide robot trajectores")
        self.trace_action.triggered[bool].connect(self.show_tracks)
        self.trace_action.setCheckable(True)
        self.trace_action.setChecked(True)

        self.superv_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/robot-supervisors.png"),
                          "Show/Hide supervisor information", self)
        self.superv_action.setStatusTip("Show/hide supervisor information")
        self.superv_action.triggered[bool].connect(self.show_supervisors)
        self.superv_action.setCheckable(True)
        self.superv_action.setChecked(False)

        zoom_group = QtGui.QActionGroup(self)

        self.zoom_world_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/zoom-scene.png"),
                          "Show all", self)
        self.zoom_world_action.triggered.connect(self.zoom_scene)
        self.zoom_world_action.setStatusTip("Show the whole world in view")
        self.zoom_world_action.setCheckable(True)
        self.zoom_world_action.setChecked(True)
        zoom_group.addAction(self.zoom_world_action)

        self.zoom_robot_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/zoom-robot.png"),
                          "Follow robot", self)
        self.zoom_robot_action.triggered.connect(self.zoom_robot)
        self.zoom_robot_action.setStatusTip("Center the view on robot")
        self.zoom_robot_action.setCheckable(True)
        self.zoom_robot_action.setChecked(False)
        zoom_group.addAction(self.zoom_robot_action)

        self.rotate_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/zoom-robot-rot.png"),
                          "Follow robot orientation", self)
        self.rotate_action.triggered.connect(self.rot_robot)
        self.rotate_action.setStatusTip("Rotate the view with the robot")
        self.rotate_action.setCheckable(True)
        self.rotate_action.setChecked(False)
        self.rotate_action.setEnabled(False)

        self.showlog_action = QtGui.QAction("Show log", self)
        self.showlog_action.triggered.connect(self.add_logdock)

        self.about_action = \
            QtGui.QAction(QtGui.QIcon.fromTheme("help-about",
                            self.windowIcon()),
                          "About",self)
        self.about_action.setStatusTip("About QtSimiam")
        self.about_action.triggered.connect(self.about)

    def create_toolbars(self):

        self.simulator_toolbar = QtGui.QToolBar("Control", self)
        self.simulator_toolbar.setAllowedAreas(QtCore.Qt.TopToolBarArea
                                               | QtCore.Qt.BottomToolBarArea)

        self.simulator_toolbar.addAction(self.open_world_action)
        self.simulator_toolbar.addSeparator()

        self.simulator_toolbar.addAction(self.rev_action)
        self.simulator_toolbar.addAction(self.run_action)
        self.simulator_toolbar.addAction(self.step_action)

        self.speed_slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
        self.speed_slider.setToolTip("Adjust speed")
        self.speed_slider.setStatusTip("Adjust simulation speed")
        self.speed_slider.setTickPosition(QtGui.QSlider.NoTicks)
        self.speed_slider.setMaximumWidth(300)
        self.speed_slider.setRange(-100, 100)
        self.speed_slider.setValue(0)
        self.speed_slider.setEnabled(False)
        self.speed_slider.valueChanged[int].connect(self.scale_time)
        self.simulator_toolbar.addWidget(self.speed_slider)

        self.speed_label = QtGui.QLabel(" Speed: 1.0x ", self)
        self.speed_label.setToolTip("Current speed multiplier")
        self.simulator_toolbar.addWidget(self.speed_label)

        self.addToolBar(self.simulator_toolbar)

        self.view_toolbar = QtGui.QToolBar("View", self)
        self.view_toolbar.setAllowedAreas(QtCore.Qt.TopToolBarArea
                                          | QtCore.Qt.BottomToolBarArea)

        self.view_toolbar.addAction(self.grid_action)
        self.view_toolbar.addAction(self.sens_action)
        self.view_toolbar.addAction(self.trace_action)
        self.view_toolbar.addAction(self.superv_action)
        self.view_toolbar.addSeparator()

        self.view_toolbar.addAction(self.zoom_world_action)
        self.view_toolbar.addAction(self.zoom_robot_action)
        self.view_toolbar.addAction(self.rotate_action)

        self.zoom_slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
        self.zoom_slider.setTickPosition(QtGui.QSlider.NoTicks)
        self.zoom_slider.setToolTip("Adjust zoom")
        self.zoom_slider.setStatusTip("Zoom in/out on robot")
        self.zoom_slider.setMaximumWidth(300)
        self.zoom_slider.setRange(-100, 100)
        self.zoom_slider.setValue(0)
        self.zoom_slider.setEnabled(False)
        self.zoom_slider.valueChanged[int].connect(self.scale_zoom)
        self.view_toolbar.addWidget(self.zoom_slider)
        self.zoom_label = QtGui.QLabel(" Zoom: 1.0x ", self)
        self.zoom_label.setToolTip("Current zoom factor")
        self.view_toolbar.addWidget(self.zoom_label)

        self.zoom_factor = 0

        self.addToolBar(self.view_toolbar)

    def create_menu(self):
        menu = QtGui.QMenuBar(self)
        self.setMenuBar(menu)

        file_menu = menu.addMenu("&File")

        file_menu.addAction(self.open_world_action)
        file_menu.addSeparator()
        file_menu.addAction(self.exit_action)

        view_menu = menu.addMenu("&View")

        view_menu.addAction(self.zoom_world_action)
        view_menu.addAction(self.zoom_robot_action)
        view_menu.addAction(self.rotate_action)
        view_menu.addSeparator()

        view_menu.addAction(self.grid_action)
        view_menu.addAction(self.sens_action)
        view_menu.addAction(self.trace_action)
        view_menu.addAction(self.superv_action)

        run_menu = menu.addMenu("&Simulation")

        run_menu.addAction(self.run_action)
        run_menu.addAction(self.step_action)
        run_menu.addAction(self.rev_action)

        self.run_menu = run_menu

        help_menu = menu.addMenu("&Help")
        help_menu.addAction(self.showlog_action)
        help_menu.addSeparator()
        help_menu.addAction(self.about_action)

    def create_statusbar(self):
        self.setStatusBar(QtGui.QStatusBar())
        self.status_label = QtGui.QLabel("", self.statusBar())
        self.status_label.setFrameShape(QtGui.QFrame.NoFrame)
        self.statusBar().addWidget(self.status_label)

    def closeEvent(self, event):
        self.sim_timer.stop()
        self.run_simulator_command('stop')
        while self.simulator_thread.isAlive():
            self.process_events(True)
            self.simulator_thread.join(0.1)
        super(SimulationWidget, self).closeEvent(event)

    def load_world(self, filename):
        self.run_action.setEnabled(False)
        if not os.path.exists(filename):
            filename = os.path.join('worlds', filename)
            if not os.path.exists(filename):
                print("Cannot open file {}".format(filename))
                return
        self.dockmanager.clear()
        self.run_simulator_command('read_config', filename)

    def setTestSuite(self, TestClass):
        self.menuBar().addSeparator()
        self.coursera_action = self.menuBar().addAction("&Submit assignment")

        tb = QtGui.QToolButton(self)
        tb.setDefaultAction(self.coursera_action)
        self.coursera_action.setIcon(QtGui.QIcon("./res/image/coursera.png"))
        self.simulator_toolbar.insertWidget(self.rev_action, tb)
        self.simulator_toolbar.insertSeparator(self.rev_action)

        self.coursera_action.triggered.connect(self.create_coursera_widget)

        self.tester = TestClass(self)
        self.create_coursera_widget()

    def create_coursera_widget(self):
        self.coursera_action.setEnabled(False)
        if self.coursera_dock is None:
            self.coursera_dock = CourseraDock(self, self.tester)
            self.addDockWidget(QtCore.Qt.LeftDockWidgetArea,
                               self.coursera_dock)
            self.coursera_dock.closed.connect(self.coursera_action.setEnabled)
        else:
            self.coursera_dock.show()

    def add_logdock(self):
        self.showlog_action.setEnabled(False)
        if self.logdock is None:
            self.logdock = LogDock(self)
            self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.logdock)
            self.logdock.closed.connect(self.showlog_action.setEnabled)
        else:
            self.logdock.show()

    # Slots
    def about(self):
        QtGui.QMessageBox.about(
            self, "About QtSimiam", """<b>PySimiam (Qt)</b><br>
        Robot simulator<br>
        &copy; Pysimiam Team
        """)

    @QtCore.pyqtSlot()
    def on_rewind(self):  # Start from the beginning
        self.speed_slider.setEnabled(False)
        #self.time_label.setText("00:00.0")
        self.run_simulator_command('reset_simulation')

    @QtCore.pyqtSlot()
    def on_run(self):  # Run/unpause
        self.run_simulation()

    @QtCore.pyqtSlot()
    def on_pause(self):  # Pause
        self.speed_slider.setEnabled(False)
        self.pause_simulation()

    @QtCore.pyqtSlot()
    def on_step(self):  # Pause
        #self.speed_slider.setEnabled(False)
        self.step_simulation()

    @QtCore.pyqtSlot()
    def on_open_world(self):
        self.on_pause()
        if self.world_dialog.exec_():
            self.load_world(self.world_dialog.selectedFiles()[0])

    @QtCore.pyqtSlot()
    def refresh_view(self):
        self.run_simulator_command('refresh')

    @QtCore.pyqtSlot(bool)
    def show_grid(self, show):
        self.run_simulator_command('show_grid', show)

    @QtCore.pyqtSlot(bool)
    def show_sensors(self, show):
        self.run_simulator_command('show_sensors', show)

    @QtCore.pyqtSlot(bool)
    def show_tracks(self, show):
        self.run_simulator_command('show_tracks', show)

    @QtCore.pyqtSlot(bool)
    def show_supervisors(self, show):
        self.run_simulator_command('show_supervisors', show)

    @QtCore.pyqtSlot()
    def zoom_scene(self):
        self.zoom_slider.setEnabled(False)
        self.rotate_action.setEnabled(False)
        self.run_simulator_command('focus_on_world')

    @QtCore.pyqtSlot()
    def zoom_robot(self):
        self.zoom_slider.setEnabled(True)
        self.rotate_action.setEnabled(True)
        self.run_simulator_command('focus_on_robot',
                                   self.rotate_action.isChecked())
        self.run_simulator_command('adjust_zoom',
                                   5.0**(self.zoom_slider.value() / 100.0))

    @QtCore.pyqtSlot()
    def rot_robot(self):
        self.run_simulator_command('focus_on_robot',
                                   self.rotate_action.isChecked())

    @QtCore.pyqtSlot(int)
    def scale_zoom(self, value):
        zoom = 5.0**(value / 100.0)
        self.run_simulator_command('adjust_zoom', zoom)
        self.zoom_label.setText(" Zoom: %.1fx " % (zoom))

    @QtCore.pyqtSlot(int)
    def scale_time(self, value):
        m = 10.0**((value - self.zoom_factor) / 100.0)
        self.run_simulator_command('set_time_multiplier', m)
        self.speed_label.setText(" Speed: %.1fx " % m)

    @QtCore.pyqtSlot()
    def update_time(self):
        if self.simulator_thread.is_running():
            t = self.simulator_thread.get_time()
            minutes = int(t // 60)
            #self.time_label.setText("%02d:%04.1f"%(minutes,t - minutes*60))
            self.status_label.setText(
                "Simulation running... {:02d}:{:04.1f}".format(
                    minutes, t - minutes * 60))
        self.process_events(True)

    def apply_parameters(self, robot_id, params):
        self.run_simulator_command('apply_parameters', robot_id, params)

    def start_testing(self):
        self.open_world_action.setEnabled(False)
        self.run_menu.setEnabled(False)
        self.simulator_toolbar.setEnabled(False)

    def stop_testing(self):
        self.open_world_action.setEnabled(True)
        self.run_menu.setEnabled(True)
        self.simulator_toolbar.setEnabled(True)

### Simulator events

    def simulator_make_param_window(self, robot_id, name, parameters):
        # FIXME adding to the right for no reason
        self.dockmanager.add_dock_right(robot_id, name, parameters)

    def simulator_running(self):
        self.speed_slider.setEnabled(True)
        self.step_action.setEnabled(False)

    def simulator_paused(self):
        self.speed_slider.setEnabled(False)
        self.step_action.setEnabled(True)
        t = self.simulator_thread.get_time()
        minutes = int(t // 60)
        self.status_label.setText(
            "Simulation paused... {:02d}:{:04.1f}".format(
                minutes, t - minutes * 60))

    def simulator_reset(self):
        self.run_action.reset()
        self.run_action.setEnabled(True)
        self.status_label.setText("Simulation ready")

    def simulator_stopped(self):
        # FIXME this function isn't necessary
        self.speed_slider.setEnabled(False)

    def simulator_update_view(self):
        self.viewer.update_bitmap()

    def simulator_exception(self, e_type, e_value, e_traceback):
        QtGui.QMessageBox.critical(
            self, "{}: {}".format(e_type.__name__, e_value),
            "\n".join(format_exception(e_type, e_value, e_traceback)))
        self.run_action.setEnabled(False)

    def simulator_log(self, message, objclass, objcolor):
        self.logdock.append(message, objclass, objcolor)
Exemple #4
0
class SimulationWidget(SimUI, QtGui.QMainWindow):
    def __init__(self,parent=None):
        QtGui.QMainWindow.__init__(self,parent)
        self.setWindowTitle("QtSimiam")
        self.setWindowIcon(QtGui.QIcon("./res/image/appicon.png"))
        self.resize(700,700)
        
        self.create_actions()
        self.create_toolbars()
        self.create_menu()
        self.create_statusbar()
        # Set intro message
        self.status_label.setText("Welcome to QtSimiam")
        
        # create XML file dialog
        self.world_dialog = QtGui.QFileDialog(self,
                                "Select World File",
                                "worlds", 
                                "WorldFile (*.xml)")
        self.world_dialog.setAcceptMode(QtGui.QFileDialog.AcceptOpen)
        self.world_dialog.setFileMode(QtGui.QFileDialog.ExistingFile)     

        # create supervisor file dialog
        self.supervisor_dialog = QtGui.QFileDialog(self,
                                     "Select Supervisor File",
                                     "supervisors", 
                                     "Supervisor (*.py)")
        self.supervisor_dialog.setAcceptMode(QtGui.QFileDialog.AcceptOpen)
        self.supervisor_dialog.setFileMode(QtGui.QFileDialog.ExistingFile)     
        
        scrollArea = QtGui.QScrollArea(self)
        self.setCentralWidget(scrollArea)
        self.viewer = SimulatorViewer()
        self.viewer.resized.connect(self.refresh_view)
        scrollArea.setWidget(self.viewer)
        scrollArea.setWidgetResizable(True)
        
        self.__clear_graph_on_start = False
        self.plots = []

        #self.setDockOptions(QtGui.QMainWindow.AllowNestedDocks)
        self.setDockOptions(QtGui.QMainWindow.DockOptions())
        
        self.coursera_dock = None
        self.logdock = None
        self.add_logdock()

        self.dockmanager = DockManager(self)
        self.dockmanager.apply_request.connect(self.apply_parameters)

        self.sim_timer = QtCore.QTimer(self)
        self.sim_timer.setInterval(10)
        self.sim_timer.timeout.connect(self.update_time)
        
        SimUI.__init__(self,self.viewer.renderer)

        self.sim_timer.start()

    def create_actions(self):
        
        self.open_world_action = \
            QtGui.QAction(QtGui.QIcon.fromTheme("document-open",
                            QtGui.QIcon("./res/image/open.png")),
                          "Open XML &World",
                          self)
        self.open_world_action.triggered.connect(self.on_open_world)
        self.open_world_action.setShortcut(QtGui.QKeySequence(QtGui.QKeySequence.Open))

        self.open_world_action.setStatusTip("Open a new simulation")
                            
        self.exit_action = \
            QtGui.QAction(QtGui.QIcon.fromTheme("application-exit"),
                    "E&xit",
                    self)
        self.exit_action.triggered.connect(self.close)
        self.exit_action.setShortcut(QtGui.QKeySequence(QtGui.QKeySequence.Quit))
        self.exit_action.setToolTip("Quit the Program")
        self.exit_action.setStatusTip("Exit QtSimiam")
        
        self.rev_action = \
            QtGui.QAction(QtGui.QIcon.fromTheme("media-seek-backward",
                            QtGui.QIcon("./res/image/arrow-left-double.png")),
                          "Rewind", self)
        self.rev_action.triggered.connect(self.on_rewind)
        self.rev_action.setStatusTip("Reset simulation")
        
        self.run_action = PlayPauseAction(self, self.on_run,self.on_pause)        
        self.run_action.setEnabled(False)
        
        self.step_action = \
            QtGui.QAction(QtGui.QIcon.fromTheme("media-skip-forward",
                            QtGui.QIcon("./res/image/media-skip-forward-7.png")),
                          "Step", self)
        self.step_action.triggered.connect(self.on_step)
        self.step_action.setStatusTip("Do one simulation step")
        self.step_action.setEnabled(False)
        
        self.grid_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/grid.png"),
                          "Show/Hide grid", self)
        self.grid_action.setStatusTip("Show/hide grid")
        self.grid_action.triggered[bool].connect(self.show_grid)
        self.grid_action.setCheckable(True)
        self.grid_action.setChecked(False)

        self.sens_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/robot-sensors.png"),
                          "Show/Hide sensors", self)
        self.sens_action.setStatusTip("Show/hide robot sensors")
        self.sens_action.triggered[bool].connect(self.show_sensors)
        self.sens_action.setCheckable(True)
        self.sens_action.setChecked(True)

        self.trace_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/robot-tracks.png"),
                          "Show/Hide robot trajectores", self)
        self.trace_action.setStatusTip("Show/hide robot trajectores")
        self.trace_action.triggered[bool].connect(self.show_tracks)
        self.trace_action.setCheckable(True)
        self.trace_action.setChecked(True)

        self.superv_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/robot-supervisors.png"),
                          "Show/Hide supervisor information", self)
        self.superv_action.setStatusTip("Show/hide supervisor information")
        self.superv_action.triggered[bool].connect(self.show_supervisors)
        self.superv_action.setCheckable(True)
        self.superv_action.setChecked(False)

        zoom_group = QtGui.QActionGroup(self)

        self.zoom_world_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/zoom-scene.png"),
                          "Show all", self)
        self.zoom_world_action.triggered.connect(self.zoom_scene)
        self.zoom_world_action.setStatusTip("Show the whole world in view")
        self.zoom_world_action.setCheckable(True)
        self.zoom_world_action.setChecked(True)
        zoom_group.addAction(self.zoom_world_action)
        
        self.zoom_robot_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/zoom-robot.png"),
                          "Follow robot", self)
        self.zoom_robot_action.triggered.connect(self.zoom_robot)
        self.zoom_robot_action.setStatusTip("Center the view on robot")
        self.zoom_robot_action.setCheckable(True)
        self.zoom_robot_action.setChecked(False)
        zoom_group.addAction(self.zoom_robot_action)

        self.rotate_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/zoom-robot-rot.png"),
                          "Follow robot orientation", self)
        self.rotate_action.triggered.connect(self.rot_robot)
        self.rotate_action.setStatusTip("Rotate the view with the robot")
        self.rotate_action.setCheckable(True)
        self.rotate_action.setChecked(False)
        self.rotate_action.setEnabled(False)

        self.showlog_action = QtGui.QAction("Show log",self)
        self.showlog_action.triggered.connect(self.add_logdock)
        
        self.about_action = \
            QtGui.QAction(QtGui.QIcon.fromTheme("help-about",
                            self.windowIcon()),
                          "About",self)
        self.about_action.setStatusTip("About QtSimiam")
        self.about_action.triggered.connect(self.about)
        
    def create_toolbars(self):
        
        self.simulator_toolbar = QtGui.QToolBar("Control",self)
        self.simulator_toolbar.setAllowedAreas(QtCore.Qt.TopToolBarArea | QtCore.Qt.BottomToolBarArea)
        
        self.simulator_toolbar.addAction(self.open_world_action)
        self.simulator_toolbar.addSeparator()
        
        self.simulator_toolbar.addAction(self.rev_action)
        self.simulator_toolbar.addAction(self.run_action)
        self.simulator_toolbar.addAction(self.step_action)
        
        self.speed_slider = QtGui.QSlider(QtCore.Qt.Horizontal,self)
        self.speed_slider.setToolTip("Adjust speed")
        self.speed_slider.setStatusTip("Adjust simulation speed")
        self.speed_slider.setTickPosition(QtGui.QSlider.NoTicks)
        self.speed_slider.setMaximumWidth(300)
        self.speed_slider.setRange(-100,100)
        self.speed_slider.setValue(0)
        self.speed_slider.setEnabled(False)
        self.speed_slider.valueChanged[int].connect(self.scale_time)
        self.simulator_toolbar.addWidget(self.speed_slider)
        
        self.speed_label = QtGui.QLabel(" Speed: 1.0x ",self)
        self.speed_label.setToolTip("Current speed multiplier")
        self.simulator_toolbar.addWidget(self.speed_label)
                       
        self.addToolBar(self.simulator_toolbar)

        self.view_toolbar = QtGui.QToolBar("View",self)
        self.view_toolbar.setAllowedAreas(QtCore.Qt.TopToolBarArea | QtCore.Qt.BottomToolBarArea)

        self.view_toolbar.addAction(self.grid_action)        
        self.view_toolbar.addAction(self.sens_action)
        self.view_toolbar.addAction(self.trace_action)
        self.view_toolbar.addAction(self.superv_action)
        self.view_toolbar.addSeparator()
        
        self.view_toolbar.addAction(self.zoom_world_action)
        self.view_toolbar.addAction(self.zoom_robot_action)
        self.view_toolbar.addAction(self.rotate_action)
        
        self.zoom_slider = QtGui.QSlider(QtCore.Qt.Horizontal,self)
        self.zoom_slider.setTickPosition(QtGui.QSlider.NoTicks)
        self.zoom_slider.setToolTip("Adjust zoom")
        self.zoom_slider.setStatusTip("Zoom in/out on robot")
        self.zoom_slider.setMaximumWidth(300)
        self.zoom_slider.setRange(-100,100)
        self.zoom_slider.setValue(0)
        self.zoom_slider.setEnabled(False)
        self.zoom_slider.valueChanged[int].connect(self.scale_zoom)
        self.view_toolbar.addWidget(self.zoom_slider)
        self.zoom_label = QtGui.QLabel(" Zoom: 1.0x ",self)
        self.zoom_label.setToolTip("Current zoom factor")
        self.view_toolbar.addWidget(self.zoom_label)
        
        self.zoom_factor = 0
                       
        self.addToolBar(self.view_toolbar)

    def create_menu(self):
        menu = QtGui.QMenuBar(self)
        self.setMenuBar(menu)
        
        file_menu = menu.addMenu("&File")
        
        file_menu.addAction(self.open_world_action)
        file_menu.addSeparator()
        file_menu.addAction(self.exit_action)
        
        view_menu = menu.addMenu("&View")
        
        view_menu.addAction(self.zoom_world_action)
        view_menu.addAction(self.zoom_robot_action)
        view_menu.addAction(self.rotate_action)
        view_menu.addSeparator()
        
        view_menu.addAction(self.grid_action)
        view_menu.addAction(self.sens_action)
        view_menu.addAction(self.trace_action)
        view_menu.addAction(self.superv_action)
        
        run_menu = menu.addMenu("&Simulation")
        
        run_menu.addAction(self.run_action)
        run_menu.addAction(self.step_action)
        run_menu.addAction(self.rev_action)
        
        self.run_menu = run_menu
        
        help_menu = menu.addMenu("&Help")
        help_menu.addAction(self.showlog_action)
        help_menu.addSeparator()
        help_menu.addAction(self.about_action)
        
    def create_statusbar(self):      
        self.setStatusBar(QtGui.QStatusBar())
        self.status_label = QtGui.QLabel("",self.statusBar())
        self.status_label.setFrameShape(QtGui.QFrame.NoFrame)
        self.statusBar().addWidget(self.status_label)

    def closeEvent(self,event):
        self.sim_timer.stop()
        self.run_simulator_command('stop')
        while self.simulator_thread.isAlive():
            self.process_events(True)
            self.simulator_thread.join(0.1)
        while self.plots:
            self.plots.pop().close()            
        super(SimulationWidget,self).closeEvent(event)

    def load_world(self,filename):
        self.run_action.setEnabled(False)
        if not os.path.exists(filename):
            filename = os.path.join('worlds',filename)
            if not os.path.exists(filename):
                print("Cannot open file {}".format(filename))
                return
        self.dockmanager.clear()
        self.run_simulator_command('read_config',filename)

    def setTestSuite(self,TestClass):
        self.menuBar().addSeparator()
        self.coursera_action = self.menuBar().addAction("&Submit assignment")
        
        tb = QtGui.QToolButton(self)
        tb.setDefaultAction(self.coursera_action)
        self.coursera_action.setIcon(QtGui.QIcon("./res/image/coursera.png"))
        self.simulator_toolbar.insertWidget(self.rev_action, tb)
        self.simulator_toolbar.insertSeparator(self.rev_action)
        
        self.coursera_action.triggered.connect(self.create_coursera_widget)

        self.tester = TestClass(self)
        self.create_coursera_widget()
     
    def create_coursera_widget(self):
        self.coursera_action.setEnabled(False)
        if self.coursera_dock is None:
            self.coursera_dock = CourseraDock(self, self.tester)
            self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.coursera_dock)
            self.coursera_dock.closed.connect(self.coursera_action.setEnabled)
        else:
            self.coursera_dock.show()

    def add_logdock(self):
        self.showlog_action.setEnabled(False)
        if self.logdock is None:
            self.logdock = LogDock(self)
            self.addDockWidget(QtCore.Qt.BottomDockWidgetArea,self.logdock)
            self.logdock.closed.connect(self.showlog_action.setEnabled)
        else:
            self.logdock.show()
        
    # Slots
    def about(self):
        QtGui.QMessageBox.about(self,"About QtSimiam",
        """<b>PySimiam (Qt)</b><br>
        Robot simulator<br>
        &copy; Pysimiam Team
        """)
    
    @QtCore.pyqtSlot()
    def on_rewind(self): # Start from the beginning
        self.speed_slider.setEnabled(False)
        #self.time_label.setText("00:00.0")
        self.run_simulator_command('reset_simulation')

    @QtCore.pyqtSlot()
    def on_run(self): # Run/unpause
        self.run_simulation()

    @QtCore.pyqtSlot()
    def on_pause(self): # Pause
        self.speed_slider.setEnabled(False)        
        self.pause_simulation()

    @QtCore.pyqtSlot()
    def on_step(self): # Pause
        #self.speed_slider.setEnabled(False)
        self.step_simulation()

    @QtCore.pyqtSlot()
    def on_open_world(self):
        self.on_pause()
        if self.world_dialog.exec_():
            self.load_world(self.world_dialog.selectedFiles()[0])

    @QtCore.pyqtSlot()
    def refresh_view(self):
        self.run_simulator_command('refresh')
            
    @QtCore.pyqtSlot(bool)
    def show_grid(self,show):
        self.run_simulator_command('show_grid',show)

    @QtCore.pyqtSlot(bool)
    def show_sensors(self,show):
        self.run_simulator_command('show_sensors',show)
            
    @QtCore.pyqtSlot(bool)
    def show_tracks(self,show):
        self.run_simulator_command('show_tracks',show)
            
    @QtCore.pyqtSlot(bool)
    def show_supervisors(self,show):
        self.run_simulator_command('show_supervisors',show)
            
    @QtCore.pyqtSlot()
    def zoom_scene(self):
        self.zoom_slider.setEnabled(False)
        self.rotate_action.setEnabled(False)
        self.run_simulator_command('focus_on_world')

    @QtCore.pyqtSlot()
    def zoom_robot(self):
        self.zoom_slider.setEnabled(True)
        self.rotate_action.setEnabled(True)
        self.run_simulator_command('focus_on_robot',self.rotate_action.isChecked())
        self.run_simulator_command('adjust_zoom',5.0**(self.zoom_slider.value()/100.0))

    @QtCore.pyqtSlot()
    def rot_robot(self):
        self.run_simulator_command('focus_on_robot',self.rotate_action.isChecked())
            
    @QtCore.pyqtSlot(int)
    def scale_zoom(self,value):
        zoom = 5.0**(value/100.0)
        self.run_simulator_command('adjust_zoom',zoom)
        self.zoom_label.setText(" Zoom: %.1fx "%(zoom))

    @QtCore.pyqtSlot(int)
    def scale_time(self,value):
        m = 10.0**((value-self.zoom_factor)/100.0)
        self.run_simulator_command('set_time_multiplier',m)
        self.speed_label.setText(" Speed: %.1fx "%m)

    @QtCore.pyqtSlot()
    def update_time(self):
        if self.simulator_thread.is_running():
            t = self.simulator_thread.get_time()
            minutes = int(t//60)
            #self.time_label.setText("%02d:%04.1f"%(minutes,t - minutes*60))
            self.status_label.setText(
                "Simulation running... {:02d}:{:04.1f}".format(minutes,t - minutes*60))
        self.process_events(True)
        
    def apply_parameters(self, robot_id, params):
        self.run_simulator_command('apply_parameters', robot_id, params)
        
    def start_testing(self):
        self.open_world_action.setEnabled(False)
        self.run_menu.setEnabled(False)
        self.simulator_toolbar.setEnabled(False)

    def stop_testing(self):
        self.open_world_action.setEnabled(True)
        self.run_menu.setEnabled(True)
        self.simulator_toolbar.setEnabled(True)

    def _new_plot(self,exprs, plot):
        if exprs:
            plot.show()
            self.plots.append(plot)
            for expr in exprs:
                self.run_simulator_command('add_plotable',expr)        
                
    def add_graph(self, description):
        self._new_plot(*create_predefined_plot_window(description))
            
### Simulator events

    def simulator_make_param_window(self,robot_id,name,parameters):       
        # FIXME adding to the right for no reason
        self.dockmanager.add_dock_right(robot_id, name, parameters)
        
    def simulator_running(self):
        self.speed_slider.setEnabled(True)
        self.step_action.setEnabled(False)
        if self.__clear_graph_on_start:
            self.__clear_graph_on_start = False
            for plot in self.plots:
                plot.clear_data()
    
    def simulator_paused(self):
        self.speed_slider.setEnabled(False)
        self.step_action.setEnabled(True)
        t = self.simulator_thread.get_time()
        minutes = int(t//60)
        self.status_label.setText(
            "Simulation paused... {:02d}:{:04.1f}".format(minutes,t - minutes*60))

    def simulator_reset(self):
        self.run_action.reset()
        self.run_action.setEnabled(True)
        self.status_label.setText("Simulation ready")
        self.__clear_graph_on_start = True
 
    def simulator_stopped(self):
        # FIXME this function isn't necessary
        self.speed_slider.setEnabled(False)
        
    def simulator_update_view(self):
        self.viewer.update_bitmap()
        
    def simulator_exception(self,e_type, e_value, e_traceback):
        QtGui.QMessageBox.critical(self,"{}: {}".format(e_type.__name__,e_value),"\n".join(format_exception(e_type,e_value,e_traceback)))
        self.run_action.setEnabled(False)
        
    def simulator_log(self, message, objclass, objcolor):
        self.logdock.append(message,objclass,objcolor)
        
    def simulator_plot_update(self,data):
        for plot in self.plots:
            plot.add_data(data)        
Exemple #5
0
class SimulationWidget(QtGui.QMainWindow):
    def __init__(self,parent=None):
        QtGui.QMainWindow.__init__(self,parent)
        self.setWindowTitle("QtSimiam")
        self.setWindowIcon(QtGui.QIcon("./res/image/appicon.png"))
        self.resize(700,700)
        
        self.create_actions()
        self.create_toolbars()
        self.create_menu()
        self.create_statusbar()
        # Set intro message
        self.status_label.setText("Welcome to QtSimiam")
        
        # create XML file dialog
        self.world_dialog = QtGui.QFileDialog(self,
                                "Select World File",
                                "worlds", 
                                "WorldFile (*.xml)")
        self.world_dialog.setAcceptMode(QtGui.QFileDialog.AcceptOpen)
        self.world_dialog.setFileMode(QtGui.QFileDialog.ExistingFile)     

        # create supervisor file dialog
        self.supervisor_dialog = QtGui.QFileDialog(self,
                                     "Select Supervisor File",
                                     "supervisors", 
                                     "Supervisor (*.py)")
        self.supervisor_dialog.setAcceptMode(QtGui.QFileDialog.AcceptOpen)
        self.supervisor_dialog.setFileMode(QtGui.QFileDialog.ExistingFile)     
        
        scrollArea = QtGui.QScrollArea(self)
        self.setCentralWidget(scrollArea)
        self.viewer = SimulatorViewer()
        self.viewer.resized.connect(self.refresh_view)
        scrollArea.setWidget(self.viewer)
        scrollArea.setWidgetResizable(True)

        #self.setDockOptions(QtGui.QMainWindow.AllowNestedDocks)
        self.setDockOptions(QtGui.QMainWindow.DockOptions())

        self.sim_timer = QtCore.QTimer(self)
        self.sim_timer.setInterval(10)
        self.sim_timer.timeout.connect(self.update_time)
        
        self.sim_queue = queue.Queue()
        
        # create the simulator thread
        self.simulator_thread = sim.Simulator(self.viewer.renderer,
                                               self.sim_queue)

        self.in_queue = self.simulator_thread._out_queue
                                               
        self.dockmanager = DockManager(self)
        self.dockmanager.apply_request.connect(self.apply_parameters)

        self.simulator_thread.start()
        self.sim_timer.start()

    def create_actions(self):
        
        self.open_world_action = \
            QtGui.QAction(QtGui.QIcon.fromTheme("document-open",
                            QtGui.QIcon("./res/image/open.png")),
                          "Open XML &World",
                          self)
        self.open_world_action.triggered.connect(self.on_open_world)
        self.open_world_action.setShortcut(QtGui.QKeySequence(QtGui.QKeySequence.Open))

        self.open_world_action.setStatusTip("Open a new simulation")
                            
        self.exit_action = \
            QtGui.QAction(QtGui.QIcon.fromTheme("application-exit"),
                    "E&xit",
                    self)
        self.exit_action.triggered.connect(self.close)
        self.exit_action.setShortcut(QtGui.QKeySequence(QtGui.QKeySequence.Quit))
        self.exit_action.setToolTip("Quit the Program")
        self.exit_action.setStatusTip("Exit QtSimiam")
        
        self.rev_action = \
            QtGui.QAction(QtGui.QIcon.fromTheme("media-seek-backward",
                            QtGui.QIcon("./res/image/arrow-left-double.png")),
                          "Rewind", self)
        self.rev_action.triggered.connect(self.on_rewind)
        self.rev_action.setStatusTip("Reset simulation")
        
        self.run_action = PlayPauseAction(self, self.on_run,self.on_pause)        
        self.run_action.setEnabled(False)
        
        self.step_action = \
            QtGui.QAction(QtGui.QIcon.fromTheme("media-skip-forward",
                            QtGui.QIcon("./res/image/media-skip-forward-7.png")),
                          "Step", self)
        self.step_action.triggered.connect(self.on_step)
        self.step_action.setStatusTip("Do one simulation step")
        self.step_action.setEnabled(False)
        
        self.grid_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/grid.png"),
                          "Show/Hide grid", self)
        self.grid_action.setStatusTip("Show/hide grid")
        self.grid_action.triggered[bool].connect(self.show_grid)
        self.grid_action.setCheckable(True)
        self.grid_action.setChecked(False)

        self.sens_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/robot-sensors.png"),
                          "Show/Hide sensors", self)
        self.sens_action.setStatusTip("Show/hide robot sensors")
        self.sens_action.triggered[bool].connect(self.show_sensors)
        self.sens_action.setCheckable(True)
        self.sens_action.setChecked(True)

        self.trace_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/robot-tracks.png"),
                          "Show/Hide robot trajectores", self)
        self.trace_action.setStatusTip("Show/hide robot trajectores")
        self.trace_action.triggered[bool].connect(self.show_tracks)
        self.trace_action.setCheckable(True)
        self.trace_action.setChecked(True)

        self.superv_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/robot-supervisors.png"),
                          "Show/Hide supervisor information", self)
        self.superv_action.setStatusTip("Show/hide supervisor information")
        self.superv_action.triggered[bool].connect(self.show_supervisors)
        self.superv_action.setCheckable(True)
        self.superv_action.setChecked(False)

        zoom_group = QtGui.QActionGroup(self)

        self.zoom_world_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/zoom-scene.png"),
                          "Show all", self)
        self.zoom_world_action.triggered.connect(self.zoom_scene)
        self.zoom_world_action.setStatusTip("Show the whole world in view")
        self.zoom_world_action.setCheckable(True)
        self.zoom_world_action.setChecked(True)
        zoom_group.addAction(self.zoom_world_action)
        
        self.zoom_robot_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/zoom-robot.png"),
                          "Follow robot", self)
        self.zoom_robot_action.triggered.connect(self.zoom_robot)
        self.zoom_robot_action.setStatusTip("Center the view on robot")
        self.zoom_robot_action.setCheckable(True)
        self.zoom_robot_action.setChecked(False)
        zoom_group.addAction(self.zoom_robot_action)

        self.rotate_action = \
            QtGui.QAction(QtGui.QIcon("./res/image/zoom-robot-rot.png"),
                          "Follow robot orientation", self)
        self.rotate_action.triggered.connect(self.rot_robot)
        self.rotate_action.setStatusTip("Rotate the view with the robot")
        self.rotate_action.setCheckable(True)
        self.rotate_action.setChecked(False)
        self.rotate_action.setEnabled(False)
        
        self.about_action = \
            QtGui.QAction(QtGui.QIcon.fromTheme("help-about",
                            self.windowIcon()),
                          "About",self)
        self.about_action.setStatusTip("About QtSimiam")
        self.about_action.triggered.connect(self.about)
        
    def create_toolbars(self):
        
        tbar = QtGui.QToolBar("Control",self)
        tbar.setAllowedAreas(QtCore.Qt.TopToolBarArea | QtCore.Qt.BottomToolBarArea)
        
        tbar.addAction(self.open_world_action)
        tbar.addSeparator()
        
        tbar.addAction(self.rev_action)
        tbar.addAction(self.run_action)
        tbar.addAction(self.step_action)
        
        self.speed_slider = QtGui.QSlider(QtCore.Qt.Horizontal,self)
        self.speed_slider.setToolTip("Adjust speed")
        self.speed_slider.setStatusTip("Adjust simulation speed")
        self.speed_slider.setTickPosition(QtGui.QSlider.NoTicks)
        self.speed_slider.setMaximumWidth(300)
        self.speed_slider.setRange(-100,100)
        self.speed_slider.setValue(0)
        self.speed_slider.setEnabled(False)
        self.speed_slider.valueChanged[int].connect(self.scale_time)
        tbar.addWidget(self.speed_slider)
        
        self.speed_label = QtGui.QLabel(" Speed: 1.0x ",self)
        self.speed_label.setToolTip("Current speed multiplier")
        tbar.addWidget(self.speed_label)
                       
        self.addToolBar(tbar)

        tbar = QtGui.QToolBar("View",self)
        tbar.setAllowedAreas(QtCore.Qt.TopToolBarArea | QtCore.Qt.BottomToolBarArea)

        tbar.addAction(self.grid_action)        
        tbar.addAction(self.sens_action)
        tbar.addAction(self.trace_action)
        tbar.addAction(self.superv_action)
        tbar.addSeparator()
        
        tbar.addAction(self.zoom_world_action)
        tbar.addAction(self.zoom_robot_action)
        tbar.addAction(self.rotate_action)
        
        self.zoom_slider = QtGui.QSlider(QtCore.Qt.Horizontal,self)
        self.zoom_slider.setTickPosition(QtGui.QSlider.NoTicks)
        self.zoom_slider.setToolTip("Adjust zoom")
        self.zoom_slider.setStatusTip("Zoom in/out on robot")
        self.zoom_slider.setMaximumWidth(300)
        self.zoom_slider.setRange(-100,100)
        self.zoom_slider.setValue(0)
        self.zoom_slider.setEnabled(False)
        self.zoom_slider.valueChanged[int].connect(self.scale_zoom)
        tbar.addWidget(self.zoom_slider)
        self.zoom_label = QtGui.QLabel(" Zoom: 1.0x ",self)
        self.zoom_label.setToolTip("Current zoom factor")
        tbar.addWidget(self.zoom_label)
        
        self.zoom_factor = 0
                       
        self.addToolBar(tbar)

    def create_menu(self):
        menu = QtGui.QMenuBar(self)
        self.setMenuBar(menu)
        
        file_menu = menu.addMenu("&File")
        
        file_menu.addAction(self.open_world_action)
        file_menu.addSeparator()
        file_menu.addAction(self.exit_action)
        
        view_menu = menu.addMenu("&View")
        
        view_menu.addAction(self.zoom_world_action)
        view_menu.addAction(self.zoom_robot_action)
        view_menu.addAction(self.rotate_action)
        view_menu.addSeparator()
        
        view_menu.addAction(self.grid_action)
        view_menu.addAction(self.sens_action)
        view_menu.addAction(self.trace_action)
        view_menu.addAction(self.superv_action)
        
        run_menu = menu.addMenu("&Simulation")
        
        run_menu.addAction(self.run_action)
        run_menu.addAction(self.step_action)
        run_menu.addAction(self.rev_action)
        
        help_menu = menu.addMenu("&Help")
        help_menu.addAction(self.about_action)
        
    def create_statusbar(self):      
        self.setStatusBar(QtGui.QStatusBar())
        self.status_label = QtGui.QLabel("",self.statusBar())
        self.status_label.setFrameShape(QtGui.QFrame.NoFrame)
        self.statusBar().addWidget(self.status_label)

    def closeEvent(self,event):
        self.sim_timer.stop()
        self.sim_queue.put(('stop',()))
        while self.simulator_thread.isAlive():
            self.process_events(True)
            self.simulator_thread.join(0.1)
        super(SimulationWidget,self).closeEvent(event)

    def load_world(self,filename):
        self.run_action.setEnabled(False)
        if not os.path.exists(filename):
            filename = os.path.join('worlds',filename)
            if not os.path.exists(filename):
                print "Cannot open file {}".format(filename)
                return
        self.dockmanager.clear()
        self.sim_queue.put(('read_config',(filename,)))

    # Slots
    def about(self):
        QtGui.QMessageBox.about(self,"About QtSimiam",
        """<b>PySimiam (Qt)</b><br>
        Robot simulator<br>
        &copy; Pysimiam Team
        """)
    
    @QtCore.pyqtSlot()
    def on_rewind(self): # Start from the beginning
        self.speed_slider.setEnabled(False)
        #self.time_label.setText("00:00.0")
        self.sim_queue.put(('reset_simulation',()))

    @QtCore.pyqtSlot()
    def on_run(self): # Run/unpause
        self.sim_queue.put(('start_simulation',()))

    @QtCore.pyqtSlot()
    def on_pause(self): # Pause
        self.speed_slider.setEnabled(False)        
        self.sim_queue.put(('pause_simulation',()))

    @QtCore.pyqtSlot()
    def on_step(self): # Pause
        #self.speed_slider.setEnabled(False)        
        self.sim_queue.put(('step_simulation',()))

    @QtCore.pyqtSlot()
    def on_open_world(self):
        self.on_pause()
        if self.world_dialog.exec_():
            self.load_world(self.world_dialog.selectedFiles()[0])

    @QtCore.pyqtSlot()
    def refresh_view(self):
        self.sim_queue.put(('refresh',()))
            
    @QtCore.pyqtSlot(bool)
    def show_grid(self,show):
        self.sim_queue.put(('show_grid',(show,)))

    @QtCore.pyqtSlot(bool)
    def show_sensors(self,show):
        self.sim_queue.put(('show_sensors',(show,)))
            
    @QtCore.pyqtSlot(bool)
    def show_tracks(self,show):
        self.sim_queue.put(('show_tracks',(show,)))
            
    @QtCore.pyqtSlot(bool)
    def show_supervisors(self,show):
        self.sim_queue.put(('show_supervisors',(show,)))
            
    @QtCore.pyqtSlot()
    def zoom_scene(self):
        self.zoom_slider.setEnabled(False)
        self.rotate_action.setEnabled(False)
        self.sim_queue.put(('focus_on_world',()))

    @QtCore.pyqtSlot()
    def zoom_robot(self):
        self.zoom_slider.setEnabled(True)
        self.rotate_action.setEnabled(True)
        self.sim_queue.put(('focus_on_robot',(self.rotate_action.isChecked(),)))
        self.sim_queue.put(('adjust_zoom',(5.0**(self.zoom_slider.value()/100.0),)))

    @QtCore.pyqtSlot()
    def rot_robot(self):
        self.sim_queue.put(('focus_on_robot',(self.rotate_action.isChecked(),)))
            
    @QtCore.pyqtSlot(int)
    def scale_zoom(self,value):
        zoom = 5.0**(value/100.0)
        self.sim_queue.put(('adjust_zoom',(zoom,)))
        self.zoom_label.setText(" Zoom: %.1fx "%(zoom))

    @QtCore.pyqtSlot(int)
    def scale_time(self,value):
        m = 10.0**((value-self.zoom_factor)/100.0)
        self.sim_queue.put(('set_time_multiplier',(m,)))
        self.speed_label.setText(" Speed: %.1fx "%m)

    @QtCore.pyqtSlot()
    def update_time(self):
        if self.simulator_thread.is_running():
            t = self.simulator_thread.get_time()
            minutes = int(t//60)
            #self.time_label.setText("%02d:%04.1f"%(minutes,t - minutes*60))
            self.status_label.setText(
                "Simulation running... {:02d}:{:04.1f}".format(minutes,t - minutes*60))
        self.process_events(True)
    
    def process_events(self, process_all = False):
        while not self.in_queue.empty():
            tpl = self.in_queue.get()
            if isinstance(tpl,tuple) and len(tpl) == 2:
                name, args = tpl
                # Scramble
                name = "simulator_{}".format(name)
                if name in self.__class__.__dict__:
                    try:
                        self.__class__.__dict__[name](self,*args)
                    except TypeError:
                        print "Wrong UI event parameters {}{}".format(name,args)
                        raise
                else:
                    print "Unknown UI event '{}'".format(name)
            else:
                print "Wrong UI event format '{}'".format(tpl)
            self.in_queue.task_done()
            if not process_all:
                return
    
    def apply_parameters(self, robot_id, params):
        self.sim_queue.put(('apply_parameters', (robot_id, params)))
            
### Simulator events

    def simulator_make_param_window(self,robot_id,name,parameters):       
        # FIXME adding to the right for no reason
        self.dockmanager.add_dock_right(robot_id, name, parameters)
        
    def simulator_running(self):
        self.speed_slider.setEnabled(True)
        self.step_action.setEnabled(False)
    
    def simulator_paused(self):
        self.speed_slider.setEnabled(False)
        self.step_action.setEnabled(True)
        t = self.simulator_thread.get_time()
        minutes = int(t//60)
        self.status_label.setText(
            "Simulation paused... {:02d}:{:04.1f}".format(minutes,t - minutes*60))

    def simulator_reset(self):
        self.run_action.reset()
        self.run_action.setEnabled(True)
        self.status_label.setText("Simulation ready")

    def simulator_stopped(self):
        # FIXME this function isn't necessary
        self.speed_slider.setEnabled(False)
        
    def simulator_update_view(self):
        self.viewer.update_bitmap()
        
    def simulator_exception(self,e_type, e_value, e_traceback):
        QtGui.QMessageBox.critical(self,"{}: {}".format(e_type.__name__,e_value),"\n".join(format_exception(e_type,e_value,e_traceback)))
        self.run_action.setEnabled(False)
    def __init__(self,parent=None):
        QtGui.QMainWindow.__init__(self,parent)
        self.setWindowTitle("QtSimiam")
        self.setWindowIcon(QtGui.QIcon("./res/image/appicon.png"))
        self.resize(700,700)
        
        self.create_actions()
        self.create_toolbars()
        self.create_menu()
        self.create_statusbar()
        # Set intro message
        self.status_label.setText("Welcome to QtSimiam")
        
        # create XML file dialog
        self.world_dialog = QtGui.QFileDialog(self,
                                "Select World File",
                                "worlds", 
                                "WorldFile (*.xml)")
        self.world_dialog.setAcceptMode(QtGui.QFileDialog.AcceptOpen)
        self.world_dialog.setFileMode(QtGui.QFileDialog.ExistingFile)     

        # create supervisor file dialog
        
        if sys.version_info.major == 3:
            formats = [bytes(fmt).decode('utf-8') for fmt in QtGui.QImageWriter.supportedImageFormats()]
        else:
            formats = [str(fmt) for fmt in QtGui.QImageWriter.supportedImageFormats()]
        
        fmtstring = "All supported image formats ({});;{}".format( \
                    " ".join('*.' + fmt for fmt in formats), \
                    ";;".join( "{} files (*.{})".format(fmt.upper(), fmt) for fmt in formats ))
        
        self.screenshot_dialog = QtGui.QFileDialog(self, "Export view", ".", fmtstring)
        self.screenshot_dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
        self.screenshot_dialog.setFileMode(QtGui.QFileDialog.AnyFile)     
       
        scrollArea = QtGui.QScrollArea(self)
        self.setCentralWidget(scrollArea)
        self.viewer = SimulatorViewer()
        self.viewer.resized.connect(self.refresh_view)
        scrollArea.setWidget(self.viewer)
        scrollArea.setWidgetResizable(True)
        
        self.__screenshot_filename = ""
        self.__clear_graph_on_start = False
        self.plots = []

        #self.setDockOptions(QtGui.QMainWindow.AllowNestedDocks)
        self.setDockOptions(QtGui.QMainWindow.DockOptions())
        
        self.coursera_dock = None
        self.logdock = None
        self.add_logdock()

        self.dockmanager = DockManager(self)
        self.dockmanager.apply_request.connect(self.apply_parameters)

        self.sim_timer = QtCore.QTimer(self)
        self.sim_timer.setInterval(10)
        self.sim_timer.timeout.connect(self.update_time)
        
        SimUI.__init__(self,self.viewer.renderer)

        self.sim_timer.start()