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()
class SimulationWidget(SimUI, QtWidgets.QMainWindow): def __init__(self, parent=None): QtWidgets.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 = QtWidgets.QFileDialog(self, "Select World File", "worlds", "WorldFile (*.xml)") self.world_dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen) self.world_dialog.setFileMode(QtWidgets.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 = QtWidgets.QFileDialog(self, "Export view", ".", fmtstring) self.screenshot_dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptSave) self.screenshot_dialog.setFileMode(QtWidgets.QFileDialog.AnyFile) scrollArea = QtWidgets.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(QtWidgets.QMainWindow.AllowNestedDocks) self.setDockOptions(QtWidgets.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, Simulator) self.sim_timer.start() def create_actions(self): self.open_world_action = \ QtWidgets.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.screenshot_action = \ QtWidgets.QAction(QtGui.QIcon.fromTheme("camera-photo", QtGui.QIcon("./res/image/screenshot.png")), "Export &screenshot", self) self.screenshot_action.triggered.connect(self.on_screenshot) self.screenshot_action.setStatusTip("Export current simulation view") self.exit_action = \ QtWidgets.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 = \ QtWidgets.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 = \ QtWidgets.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 = \ QtWidgets.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 = \ QtWidgets.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 = \ QtWidgets.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 = \ QtWidgets.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 = QtWidgets.QActionGroup(self) self.zoom_world_action = \ QtWidgets.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 = \ QtWidgets.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 = \ QtWidgets.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 = QtWidgets.QAction("Show log", self) self.showlog_action.triggered.connect(self.add_logdock) self.about_action = \ QtWidgets.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 = QtWidgets.QToolBar("Control", self) self.simulator_toolbar.setAllowedAreas(QtCore.Qt.TopToolBarArea | QtCore.Qt.BottomToolBarArea) self.simulator_toolbar.addAction(self.open_world_action) self.simulator_toolbar.addAction(self.screenshot_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 = QtWidgets.QSlider(QtCore.Qt.Horizontal, self) self.speed_slider.setToolTip("Adjust speed") self.speed_slider.setStatusTip("Adjust simulation speed") self.speed_slider.setTickPosition(QtWidgets.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 = QtWidgets.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 = QtWidgets.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 = QtWidgets.QSlider(QtCore.Qt.Horizontal, self) self.zoom_slider.setTickPosition(QtWidgets.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 = QtWidgets.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 = QtWidgets.QMenuBar(self) self.setMenuBar(menu) file_menu = menu.addMenu("&File") file_menu.addAction(self.open_world_action) file_menu.addAction(self.screenshot_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(QtWidgets.QStatusBar()) self.status_label = QtWidgets.QLabel("", self.statusBar()) self.status_label.setFrameShape(QtWidgets.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 = QtWidgets.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): QtWidgets.QMessageBox.about( self, "About QtSimiam", """<b>PySimiam (Qt)</b><br> Robot simulator<br> © 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 on_screenshot(self): if self.screenshot_dialog.exec_(): # Remember, direct access to the renderer is not thread-safe if self.simulator_thread.is_running(): self.__screenshot_filename = self.screenshot_dialog.selectedFiles( )[0] else: self.viewer.export_bitmap( self.screenshot_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)) if not self.screenshot_dialog.isVisible( ): # During screenshot the simulator is paused 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() if len(self.__screenshot_filename): self.viewer.export_bitmap(self.__screenshot_filename) self.__screenshot_filename = "" def simulator_exception(self, e_type, e_value, e_traceback): QtWidgets.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)
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 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() 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.screenshot_action = QtGui.QAction( QtGui.QIcon.fromTheme("camera-photo", QtGui.QIcon("./res/image/screenshot.png")), "Export &screenshot", self ) self.screenshot_action.triggered.connect(self.on_screenshot) self.screenshot_action.setStatusTip("Export current simulation view") 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.addAction(self.screenshot_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.addAction(self.screenshot_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> © 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 on_screenshot(self): if self.screenshot_dialog.exec_(): # Remember, direct access to the renderer is not thread-safe if self.simulator_thread.is_running(): self.__screenshot_filename = self.screenshot_dialog.selectedFiles()[0] else: self.viewer.export_bitmap(self.screenshot_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)) if not self.screenshot_dialog.isVisible(): # During screenshot the simulator is paused 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() if len(self.__screenshot_filename): self.viewer.export_bitmap(self.__screenshot_filename) self.__screenshot_filename = "" 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)