From f81be6f0a9354b2ddeed6d8ea0c609753b6339fe Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Sun, 10 May 2020 13:50:24 +0300 Subject: [PATCH 1/2] - fixed the problem with using comma as decimal separator in Grid Snap fields --- CHANGELOG.md | 4 ++++ flatcamEditors/FlatCAMGeoEditor.py | 22 ++++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7770a326..aaeb8453 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ CHANGELOG for FlatCAM beta ================================================= +10.05.2020 + +- fixed the problem with using comma as decimal separator in Grid Snap fields + 9.05.2020 - modified the GUI for Exclusion areas; now the shapes are displayed in a Table where they can be selected and deleted. Modification applied for Geometry Objects only (for now). diff --git a/flatcamEditors/FlatCAMGeoEditor.py b/flatcamEditors/FlatCAMGeoEditor.py index ccf67ce4..7950ac6a 100644 --- a/flatcamEditors/FlatCAMGeoEditor.py +++ b/flatcamEditors/FlatCAMGeoEditor.py @@ -3467,22 +3467,32 @@ class FlatCAMGeoEditor(QtCore.QObject): :return: """ try: - self.options[opt] = float(entry.text()) + text_value = entry.text() + if ',' in text_value: + text_value = text_value.replace(',', '.') + self.options[opt] = float(text_value) except Exception as e: + entry.set_value(self.app.defaults[opt]) log.debug("FlatCAMGeoEditor.__init__().entry2option() --> %s" % str(e)) return - def gridx_changed(goption, gentry): + def grid_changed(goption, gentry): """ - :param goption: String. Can be either 'global_gridx' or 'global_gridy' - :param gentry: A GUI element which text value is read and used + :param goption: String. Can be either 'global_gridx' or 'global_gridy' + :param gentry: A GUI element which text value is read and used :return: """ + if goption not in ['global_gridx', 'global_gridy']: + return + entry2option(opt=goption, entry=gentry) # if the grid link is checked copy the value in the GridX field to GridY try: - val = float(gentry.get_value()) + text_value = gentry.text() + if ',' in text_value: + text_value = text_value.replace(',', '.') + val = float(text_value) except ValueError: return @@ -3491,7 +3501,7 @@ class FlatCAMGeoEditor(QtCore.QObject): self.app.ui.grid_gap_x_entry.setValidator(QtGui.QDoubleValidator()) self.app.ui.grid_gap_x_entry.textChanged.connect( - lambda: gridx_changed("global_gridx", self.app.ui.grid_gap_x_entry)) + lambda: grid_changed("global_gridx", self.app.ui.grid_gap_x_entry)) self.app.ui.grid_gap_y_entry.setValidator(QtGui.QDoubleValidator()) self.app.ui.grid_gap_y_entry.textChanged.connect( From 46367c433f4320c28ed59bff08c0fa139d9e9ea3 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Mon, 11 May 2020 07:29:46 +0300 Subject: [PATCH 2/2] - removed the labels in status bar that display X,Y positions and replaced it with a HUD display on canvas (combo key SHIFT+H) will toggle the display of the HUD - made the HUD work in Legacy2D mode - fixed situation when the mouse cursor is outside of the canvas and no therefore returning None values --- CHANGELOG.md | 6 + FlatCAMApp.py | 65 ++++++---- FlatCAMCommon.py | 13 +- Utils/vispy_example.py | 195 +++++++++++++++++++++++++++++ defaults.py | 1 + flatcamEditors/FlatCAMExcEditor.py | 18 +-- flatcamEditors/FlatCAMGeoEditor.py | 19 +-- flatcamEditors/FlatCAMGrbEditor.py | 19 +-- flatcamGUI/FlatCAMGUI.py | 27 ++-- flatcamGUI/PlotCanvas.py | 33 ++++- flatcamGUI/PlotCanvasLegacy.py | 73 +++++++++++ flatcamGUI/VisPyCanvas.py | 1 + flatcamTools/ToolCopperThieving.py | 16 ++- flatcamTools/ToolDistance.py | 15 ++- flatcamTools/ToolNCC.py | 16 ++- flatcamTools/ToolPaint.py | 16 ++- 16 files changed, 453 insertions(+), 80 deletions(-) create mode 100644 Utils/vispy_example.py diff --git a/CHANGELOG.md b/CHANGELOG.md index aaeb8453..1f6c6b3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ CHANGELOG for FlatCAM beta ================================================= +11.05.2020 + +- removed the labels in status bar that display X,Y positions and replaced it with a HUD display on canvas (combo key SHIFT+H) will toggle the display of the HUD +- made the HUD work in Legacy2D mode +- fixed situation when the mouse cursor is outside of the canvas and no therefore returning None values + 10.05.2020 - fixed the problem with using comma as decimal separator in Grid Snap fields diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 4641420d..5864bb2a 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -285,6 +285,8 @@ class App(QtCore.QObject): :rtype: App """ + super().__init__() + App.log.info("FlatCAM Starting...") self.main_thread = QtWidgets.QApplication.instance().thread() @@ -504,8 +506,6 @@ class App(QtCore.QObject): self.FC_light_blue = '#a5a5ffbf' self.FC_dark_blue = '#0000ffbf' - QtCore.QObject.__init__(self) - self.ui = FlatCAMGUI(self) theme_settings = QtCore.QSettings("Open Source", "FlatCAM") @@ -5378,14 +5378,20 @@ class App(QtCore.QObject): edge_width=self.defaults["global_cursor_width"], size=self.defaults["global_cursor_size"]) - # Set the position label - self.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (location[0], location[1])) # Set the relative position label dx = location[0] - float(self.rel_point1[0]) dy = location[1] - float(self.rel_point1[1]) - self.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (dx, dy)) + # self.ui.position_label.setText("    X: %.4f   " + # "Y: %.4f" % (location[0], location[1])) + # # Set the position label + # + # self.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (dx, dy)) + + units = self.defaults["units"].lower() + self.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + dx, units, dy, units, location[0], units, location[1], units) self.inform.emit('[success] %s' % _("Done.")) return location @@ -5527,14 +5533,19 @@ class App(QtCore.QObject): edge_width=self.defaults["global_cursor_width"], size=self.defaults["global_cursor_size"]) - # Set the position label - self.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (location[0], location[1])) # Set the relative position label self.dx = location[0] - float(self.rel_point1[0]) self.dy = location[1] - float(self.rel_point1[1]) - self.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (self.dx, self.dy)) + # Set the position label + # self.ui.position_label.setText("    X: %.4f   " + # "Y: %.4f" % (location[0], location[1])) + # self.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.dx, self.dy)) + + units = self.defaults["units"].lower() + self.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.dx, units, self.dy, units, location[0], units, location[1], units) self.inform.emit('[success] %s' % _("Done.")) return location @@ -5843,8 +5854,8 @@ class App(QtCore.QObject): self.ui.plot_tab_area.addTab(self.ui.preferences_tab, _("Preferences")) # delete the absolute and relative position and messages in the infobar - self.ui.position_label.setText("") - self.ui.rel_position_label.setText("") + # self.ui.position_label.setText("") + # self.ui.rel_position_label.setText("") # Switch plot_area to preferences page self.ui.plot_tab_area.setCurrentWidget(self.ui.preferences_tab) @@ -6738,6 +6749,9 @@ class App(QtCore.QObject): try: # May fail in case mouse not within axes pos_canvas = self.plotcanvas.translate_coords(event_pos) + if pos_canvas[0] is None or pos_canvas[1] is None: + return + if self.grid_status(): pos = self.geo_editor.snap(pos_canvas[0], pos_canvas[1]) @@ -6749,13 +6763,19 @@ class App(QtCore.QObject): else: pos = (pos_canvas[0], pos_canvas[1]) - self.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (pos[0], pos[1])) - self.dx = pos[0] - float(self.rel_point1[0]) self.dy = pos[1] - float(self.rel_point1[1]) - self.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (self.dx, self.dy)) + + # self.ui.position_label.setText("    X: %.4f   " + # "Y: %.4f" % (pos[0], pos[1])) + # self.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.dx, self.dy)) + + units = self.defaults["units"].lower() + self.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.dx, units, self.dy, units, pos[0], units, pos[1], units) + self.mouse = [pos[0], pos[1]] # if the mouse is moved and the LMB is clicked then the action is a selection @@ -6804,9 +6824,10 @@ class App(QtCore.QObject): # In this case poly_obj creation (see above) will fail pass - except Exception: - self.ui.position_label.setText("") - self.ui.rel_position_label.setText("") + except Exception as e: + log.debug("App.on_mouse_move_over_plot() - rel_point1 is not None -> %s" % str(e)) + # self.ui.position_label.setText("") + # self.ui.rel_position_label.setText("") self.mouse = None def on_mouse_click_release_over_plot(self, event): diff --git a/FlatCAMCommon.py b/FlatCAMCommon.py index b95091aa..bcd61a98 100644 --- a/FlatCAMCommon.py +++ b/FlatCAMCommon.py @@ -466,15 +466,20 @@ class ExclusionAreas(QtCore.QObject): size=self.app.defaults["global_cursor_size"]) # update the positions on status bar - self.app.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (curr_pos[0], curr_pos[1])) if self.cursor_pos is None: self.cursor_pos = (0, 0) self.app.dx = curr_pos[0] - float(self.cursor_pos[0]) self.app.dy = curr_pos[1] - float(self.cursor_pos[1]) - self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (self.app.dx, self.app.dy)) + # self.app.ui.position_label.setText("    X: %.4f   " + # "Y: %.4f" % (curr_pos[0], curr_pos[1])) + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.app.dx, self.app.dy)) + + units = self.app.defaults["units"].lower() + self.app.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.app.dx, units, self.app.dy, units, curr_pos[0], units, curr_pos[1], units) if self.obj_type == 'excellon': color = "#FF7400" diff --git a/Utils/vispy_example.py b/Utils/vispy_example.py new file mode 100644 index 00000000..a9eaee12 --- /dev/null +++ b/Utils/vispy_example.py @@ -0,0 +1,195 @@ +from PyQt5.QtGui import QPalette +from PyQt5 import QtCore, QtWidgets + +import vispy.scene as scene +from vispy.scene.visuals import Rectangle, Text +from vispy.color import Color + +import sys + + +class VisPyCanvas(scene.SceneCanvas): + + def __init__(self, config=None): + super().__init__(config=config, keys=None) + + self.unfreeze() + + # Colors used by the Scene + theme_color = Color('#FFFFFF') + tick_color = Color('#000000') + back_color = str(QPalette().color(QPalette.Window).name()) + + # Central Widget Colors + self.central_widget.bgcolor = back_color + self.central_widget.border_color = back_color + + self.grid_widget = self.central_widget.add_grid(margin=10) + self.grid_widget.spacing = 0 + + # TOP Padding + top_padding = self.grid_widget.add_widget(row=0, col=0, col_span=2) + top_padding.height_max = 0 + + # RIGHT Padding + right_padding = self.grid_widget.add_widget(row=0, col=2, row_span=2) + right_padding.width_max = 0 + + # X Axis + self.xaxis = scene.AxisWidget( + orientation='bottom', axis_color=tick_color, text_color=tick_color, + font_size=8, axis_width=1, + anchors=['center', 'bottom'] + ) + self.xaxis.height_max = 30 + self.grid_widget.add_widget(self.xaxis, row=2, col=1) + + # Y Axis + self.yaxis = scene.AxisWidget( + orientation='left', axis_color=tick_color, text_color=tick_color, + font_size=8, axis_width=1 + ) + self.yaxis.width_max = 55 + self.grid_widget.add_widget(self.yaxis, row=1, col=0) + + # View & Camera + self.view = self.grid_widget.add_view(row=1, col=1, border_color=tick_color, + bgcolor=theme_color) + self.view.camera = scene.PanZoomCamera(aspect=1, rect=(-25, -25, 150, 150)) + + self.xaxis.link_view(self.view) + self.yaxis.link_view(self.view) + + self.grid = scene.GridLines(parent=self.view.scene, color='dimgray') + self.grid.set_gl_state(depth_test=False) + + self.rect = Rectangle(center=(65,30), color=Color('#0000FF10'), border_color=Color('#0000FF10'), + width=120, height=50, radius=[5, 5, 5, 5], parent=self.view) + self.rect.set_gl_state(depth_test=False) + + self.text = Text('', parent=self.view, color='black', pos=(5, 30), method='gpu', anchor_x='left') + self.text.font_size = 8 + self.text.text = 'Coordinates:\nX: %s\nY: %s' % ('0.0000', '0.0000') + + self.freeze() + + # self.measure_fps() + + +class PlotCanvas(QtCore.QObject): + + def __init__(self, container, my_app): + """ + The constructor configures the VisPy figure that + will contain all plots, creates the base axes and connects + events to the plotting area. + + :param container: The parent container in which to draw plots. + :rtype: PlotCanvas + """ + + super().__init__() + + # VisPyCanvas instance + self.vispy_canvas = VisPyCanvas() + + self.vispy_canvas.unfreeze() + + self.my_app = my_app + + # Parent container + self.container = container + + # + self.vispy_canvas.create_native() + self.vispy_canvas.native.setParent(self.my_app.ui) + + # + self.container.addWidget(self.vispy_canvas.native) + + # add two Infinite Lines to act as markers for the X,Y axis + self.v_line = scene.visuals.InfiniteLine( + pos=0, color=(0.0, 0.0, 1.0, 0.3), vertical=True, + parent=self.vispy_canvas.view.scene) + + self.h_line = scene.visuals.InfiniteLine( + pos=0, color=(0.00, 0.0, 1.0, 0.3), vertical=False, + parent=self.vispy_canvas.view.scene) + + self.vispy_canvas.freeze() + + def event_connect(self, event, callback): + getattr(self.vispy_canvas.events, event).connect(callback) + + def event_disconnect(self, event, callback): + getattr(self.vispy_canvas.events, event).disconnect(callback) + + def translate_coords(self, pos): + """ + Translate pixels to canvas units. + """ + tr = self.vispy_canvas.grid.get_transform('canvas', 'visual') + return tr.map(pos) + + +class MyGui(QtWidgets.QMainWindow): + + def __init__(self): + super().__init__() + + self.setWindowTitle("VisPy Test") + + # add Menubar + self.menu = self.menuBar() + self.menufile = self.menu.addMenu("File") + self.menuedit = self.menu.addMenu("Edit") + self.menufhelp = self.menu.addMenu("Help") + + # add a Toolbar + self.file_toolbar = QtWidgets.QToolBar("File Toolbar") + self.addToolBar(self.file_toolbar) + self.button = self.file_toolbar.addAction("Open") + + # add Central Widget + self.c_widget = QtWidgets.QWidget() + self.central_layout = QtWidgets.QVBoxLayout() + self.c_widget.setLayout(self.central_layout) + self.setCentralWidget(self.c_widget) + + # add InfoBar + # self.infobar = self.statusBar() + # self.position_label = QtWidgets.QLabel("Position: X: 0.0000\tY: 0.0000") + # self.infobar.addWidget(self.position_label) + + +class MyApp(QtCore.QObject): + + def __init__(self): + super().__init__() + + self.ui = MyGui() + self.plot = PlotCanvas(container=self.ui.central_layout, my_app=self) + + self.ui.show() + + self.plot.event_connect(event="mouse_move", callback=self.on_mouse_move) + + def on_mouse_move(self, event): + cursor_pos = event.pos + + pos_canvas = self.plot.translate_coords(cursor_pos) + + # we don't need all the info in the tuple returned by the translate_coords() + # only first 2 elements + pos_canvas = [pos_canvas[0], pos_canvas[1]] + # self.ui.position_label.setText("Position: X: %.4f\tY: %.4f" % (pos_canvas[0], pos_canvas[1])) + # pos_text = 'Coordinates: \nX: {:<7.4f}\nY: {:<7.4f}'.format(pos_canvas[0], pos_canvas[1]) + pos_text = 'Coordinates: \nX: {:<.4f}\nY: {:<.4f}'.format(pos_canvas[0], pos_canvas[1]) + self.plot.vispy_canvas.text.text = pos_text + + +if __name__ == '__main__': + app = QtWidgets.QApplication(sys.argv) + + m_app = MyApp() + sys.exit(app.exec_()) diff --git a/defaults.py b/defaults.py index b446a39a..64f0fa70 100644 --- a/defaults.py +++ b/defaults.py @@ -43,6 +43,7 @@ class FlatCAMDefaults: # General "global_graphic_engine": '3D', + "global_hud": True, "global_app_level": 'b', "global_portable": False, "global_language": 'English', diff --git a/flatcamEditors/FlatCAMExcEditor.py b/flatcamEditors/FlatCAMExcEditor.py index 49dc5eb5..7b68f11c 100644 --- a/flatcamEditors/FlatCAMExcEditor.py +++ b/flatcamEditors/FlatCAMExcEditor.py @@ -3801,18 +3801,22 @@ class FlatCAMExcEditor(QtCore.QObject): self.snap_x = x self.snap_y = y - # update the position label in the infobar since the APP mouse event handlers are disconnected - self.app.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (x, y)) - if self.pos is None: self.pos = (0, 0) self.app.dx = x - self.pos[0] self.app.dy = y - self.pos[1] - # update the reference position label in the infobar since the APP mouse event handlers are disconnected - self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (self.app.dx, self.app.dy)) + # # update the position label in the infobar since the APP mouse event handlers are disconnected + # self.app.ui.position_label.setText("    X: %.4f   " + # "Y: %.4f" % (x, y)) + # # update the reference position label in the infobar since the APP mouse event handlers are disconnected + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.app.dx, self.app.dy)) + + units = self.app.defaults["units"].lower() + self.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.app.dx, units, self.app.dy, units, x, units, y, units) # ## Utility geometry (animated) self.update_utility_geometry(data=(x, y)) diff --git a/flatcamEditors/FlatCAMGeoEditor.py b/flatcamEditors/FlatCAMGeoEditor.py index 7950ac6a..e8d969cb 100644 --- a/flatcamEditors/FlatCAMGeoEditor.py +++ b/flatcamEditors/FlatCAMGeoEditor.py @@ -4271,18 +4271,23 @@ class FlatCAMGeoEditor(QtCore.QObject): self.snap_y = y self.app.mouse = [x, y] - # update the position label in the infobar since the APP mouse event handlers are disconnected - self.app.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (x, y)) - if self.pos is None: self.pos = (0, 0) self.app.dx = x - self.pos[0] self.app.dy = y - self.pos[1] - # update the reference position label in the infobar since the APP mouse event handlers are disconnected - self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (self.app.dx, self.app.dy)) + # # update the position label in the infobar since the APP mouse event handlers are disconnected + # self.app.ui.position_label.setText("    X: %.4f   " + # "Y: %.4f" % (x, y)) + # + # # update the reference position label in the infobar since the APP mouse event handlers are disconnected + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.app.dx, self.app.dy)) + + units = self.app.defaults["units"].lower() + self.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.app.dx, units, self.app.dy, units, x, units, y, units) if event.button == 1 and event_is_dragging and isinstance(self.active_tool, FCEraser): pass diff --git a/flatcamEditors/FlatCAMGrbEditor.py b/flatcamEditors/FlatCAMGrbEditor.py index 3362c5ba..d5659fbf 100644 --- a/flatcamEditors/FlatCAMGrbEditor.py +++ b/flatcamEditors/FlatCAMGrbEditor.py @@ -4774,18 +4774,23 @@ class FlatCAMGrbEditor(QtCore.QObject): self.app.mouse = [x, y] - # update the position label in the infobar since the APP mouse event handlers are disconnected - self.app.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (x, y)) - if self.pos is None: self.pos = (0, 0) self.app.dx = x - self.pos[0] self.app.dy = y - self.pos[1] - # update the reference position label in the infobar since the APP mouse event handlers are disconnected - self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (self.app.dx, self.app.dy)) + # # update the position label in the infobar since the APP mouse event handlers are disconnected + # self.app.ui.position_label.setText("    X: %.4f   " + # "Y: %.4f" % (x, y)) + # + # # update the reference position label in the infobar since the APP mouse event handlers are disconnected + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.app.dx, self.app.dy)) + + units = self.app.defaults["units"].lower() + self.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.app.dx, units, self.app.dy, units, x, units, y, units) self.update_utility_geometry(data=(x, y)) diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index 09d7383b..aeb7b86d 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -2306,17 +2306,17 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.snap_infobar_label.setPixmap(QtGui.QPixmap(self.app.resource_location + '/snap_16.png')) self.infobar.addWidget(self.snap_infobar_label) - self.rel_position_label = QtWidgets.QLabel( - "Dx: 0.0000   Dy: 0.0000    ") - self.rel_position_label.setMinimumWidth(110) - self.rel_position_label.setToolTip(_("Relative measurement.\nReference is last click position")) - self.infobar.addWidget(self.rel_position_label) - - self.position_label = QtWidgets.QLabel( - "    X: 0.0000   Y: 0.0000") - self.position_label.setMinimumWidth(110) - self.position_label.setToolTip(_("Absolute measurement.\nReference is (X=0, Y= 0) position")) - self.infobar.addWidget(self.position_label) + # self.rel_position_label = QtWidgets.QLabel( + # "Dx: 0.0000   Dy: 0.0000    ") + # self.rel_position_label.setMinimumWidth(110) + # self.rel_position_label.setToolTip(_("Relative measurement.\nReference is last click position")) + # self.infobar.addWidget(self.rel_position_label) + # + # self.position_label = QtWidgets.QLabel( + # "    X: 0.0000   Y: 0.0000") + # self.position_label.setMinimumWidth(110) + # self.position_label.setToolTip(_("Absolute measurement.\nReference is (X=0, Y= 0) position")) + # self.infobar.addWidget(self.position_label) self.units_label = QtWidgets.QLabel("[in]") self.units_label.setMargin(2) @@ -2993,6 +2993,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow): if key == QtCore.Qt.Key_G: self.app.on_toggle_axis() + # Toggle HUD (Heads-Up Display) + if key == QtCore.Qt.Key_H: + state = False if self.app.plotcanvas.hud_enabled else True + self.app.plotcanvas.on_toggle_hud(state=state) + # Locate in Object if key == QtCore.Qt.Key_J: self.app.on_locate(obj=self.app.collection.get_active()) diff --git a/flatcamGUI/PlotCanvas.py b/flatcamGUI/PlotCanvas.py index 44af74d0..a1be6099 100644 --- a/flatcamGUI/PlotCanvas.py +++ b/flatcamGUI/PlotCanvas.py @@ -10,7 +10,7 @@ from PyQt5 import QtCore import logging from flatcamGUI.VisPyCanvas import VisPyCanvas, Color from flatcamGUI.VisPyVisuals import ShapeGroup, ShapeCollection, TextCollection, TextGroup, Cursor -from vispy.scene.visuals import InfiniteLine, Line +from vispy.scene.visuals import InfiniteLine, Line, Rectangle, Text import numpy as np from vispy.geometry import Rect @@ -54,8 +54,12 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): if theme == 'white': self.line_color = (0.3, 0.0, 0.0, 1.0) + self.rect_hud_color = Color('#0000FF10') + self.text_hud_color = 'black' else: self.line_color = (0.4, 0.4, 0.4, 1.0) + self.rect_hud_color = Color('#0000FF10') + self.text_hud_color = 'white' # workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node, # which might decrease performance @@ -146,13 +150,28 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): self.cursor_h_line = InfiniteLine(pos=None, color=c_color, vertical=False, parent=self.line_parent) + self.rect_hud = Rectangle(center=(90,45), color=self.rect_hud_color, border_color=self.rect_hud_color, + width=170, height=80, radius=[5, 5, 5, 5], parent=None) + self.rect_hud.set_gl_state(depth_test=False) + + # HUD Display + self.hud_enabled = False + + self.text_hud = Text('', color=self.text_hud_color, pos=(8, 45), method='gpu', anchor_x='left', parent=None) + self.text_hud.font_size = 8 + units = self.fcapp.defaults["units"].lower() + self.text_hud.text = 'Dx:\t%s [%s]\nDy:\t%s [%s]\nX: \t%s [%s]\nY: \t%s [%s]' % \ + ('0.0000', units, '0.0000', units, '0.0000', units, '0.0000', units) + + if self.fcapp.defaults['global_hud'] is True: + self.on_toggle_hud(state=True) + self.shape_collections = [] self.shape_collection = self.new_shape_collection() self.fcapp.pool_recreated.connect(self.on_pool_recreated) self.text_collection = self.new_text_collection() - # TODO: Should be setting to show/hide CNC job annotations (global or per object) self.text_collection.enabled = True self.c = None @@ -163,6 +182,16 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): self.graph_event_connect('mouse_wheel', self.on_mouse_scroll) + def on_toggle_hud(self, state): + if state: + self.hud_enabled = True + self.rect_hud.parent = self.view + self.text_hud.parent = self.view + else: + self.hud_enabled = False + self.rect_hud.parent = None + self.text_hud.parent = None + def draw_workspace(self, workspace_size): """ Draw a rectangular shape on canvas to specify our valid workspace. diff --git a/flatcamGUI/PlotCanvasLegacy.py b/flatcamGUI/PlotCanvasLegacy.py index a9a6216f..7856bb8f 100644 --- a/flatcamGUI/PlotCanvasLegacy.py +++ b/flatcamGUI/PlotCanvasLegacy.py @@ -29,6 +29,7 @@ mpl_use("Qt5Agg") from matplotlib.figure import Figure from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.lines import Line2D +from matplotlib.offsetbox import AnchoredText # from matplotlib.widgets import Cursor fcTranslate.apply_language('strings') @@ -147,9 +148,13 @@ class PlotCanvasLegacy(QtCore.QObject): if self.app.defaults['global_theme'] == 'white': theme_color = '#FFFFFF' tick_color = '#000000' + self.rect_hud_color = '#0000FF10' + self.text_hud_color = '#000000' else: theme_color = '#000000' tick_color = '#FFFFFF' + self.rect_hud_color = '#0000FF10' + self.text_hud_color = '#000000' # workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node, # which might decrease performance @@ -298,11 +303,79 @@ class PlotCanvasLegacy(QtCore.QObject): # signal if there is a doubleclick self.is_dblclk = False + self.hud_enabled = False + self.text_hud = self.Thud(plotcanvas=self) + + # bbox_props = dict(boxstyle="round,pad=0.3", fc="blue", ec="b", lw=0) + # self.text_hud = self.figure.text(0, 0, "Direction", ha="left", va="center", rotation=0, + # size=15, + # bbox=bbox_props) + # draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area # all CNC have a limited workspace if self.app.defaults['global_workspace'] is True: self.draw_workspace(workspace_size=self.app.defaults["global_workspaceT"]) + if self.app.defaults['global_hud'] is True: + self.on_toggle_hud(state=True) + + def on_toggle_hud(self, state): + if state: + self.hud_enabled = True + self.text_hud.add_artist() + else: + self.hud_enabled = False + self.text_hud.remove_artist() + self.canvas.draw() + + class Thud(QtCore.QObject): + text_changed = QtCore.pyqtSignal(str) + + def __init__(self, plotcanvas): + super().__init__() + + self.p = plotcanvas + units = self.p.app.defaults['units'] + self._text = 'Dx: %s [%s]\nDy: %s [%s]\nX: %s [%s]\nY: %s [%s]' % \ + ('0.0000', units, '0.0000', units, '0.0000', units, '0.0000', units) + + self.hud_holder = AnchoredText(self._text, + prop=dict(size=20), frameon=True, + loc='upper left', + ) + self.hud_holder.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") + + self.hud_holder.patch.set_facecolor('blue') + self.hud_holder.patch.set_alpha(0.3) + self.hud_holder.patch.set_edgecolor((0, 0, 0, 0)) + + self.text_changed.connect(self.on_text_changed) + + @property + def text(self): + return self._text + + @text.setter + def text(self, val): + self.text_changed.emit(val) + self._text = val + + def on_text_changed(self, txt): + try: + txt = txt.replace('\t', ' ') + self.hud_holder.txt.set_text(txt) + self.p.canvas.draw() + except Exception: + pass + + def add_artist(self): + if self.hud_holder not in self.p.axes.artists: + self.p.axes.add_artist(self.hud_holder) + + def remove_artist(self): + if self.hud_holder in self.p.axes.artists: + self.p.axes.artists.remove(self.hud_holder) + def draw_workspace(self, workspace_size): """ Draw a rectangular shape on canvas to specify our valid workspace. diff --git a/flatcamGUI/VisPyCanvas.py b/flatcamGUI/VisPyCanvas.py index 7d7efe13..aa55675f 100644 --- a/flatcamGUI/VisPyCanvas.py +++ b/flatcamGUI/VisPyCanvas.py @@ -13,6 +13,7 @@ import numpy as np import vispy.scene as scene from vispy.scene.cameras.base_camera import BaseCamera +# from vispy.scene.widgets import Widget as VisPyWidget from vispy.color import Color import time diff --git a/flatcamTools/ToolCopperThieving.py b/flatcamTools/ToolCopperThieving.py index d6fd310a..f6c85731 100644 --- a/flatcamTools/ToolCopperThieving.py +++ b/flatcamTools/ToolCopperThieving.py @@ -910,16 +910,22 @@ class ToolCopperThieving(FlatCAMTool): edge_width=self.app.defaults["global_cursor_width"], size=self.app.defaults["global_cursor_size"]) - # update the positions on status bar - self.app.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (curr_pos[0], curr_pos[1])) if self.cursor_pos is None: self.cursor_pos = (0, 0) self.app.dx = curr_pos[0] - float(self.cursor_pos[0]) self.app.dy = curr_pos[1] - float(self.cursor_pos[1]) - self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (self.app.dx, self.app.dy)) + + # # update the positions on status bar + # self.app.ui.position_label.setText("    X: %.4f   " + # "Y: %.4f" % (curr_pos[0], curr_pos[1])) + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.app.dx, self.app.dy)) + + units = self.app.defaults["units"].lower() + self.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.app.dx, units, self.app.dy, units, curr_pos[0], units, curr_pos[1], units) # draw the utility geometry if self.first_click: diff --git a/flatcamTools/ToolDistance.py b/flatcamTools/ToolDistance.py index ea62c64e..bcc506fb 100644 --- a/flatcamTools/ToolDistance.py +++ b/flatcamTools/ToolDistance.py @@ -544,11 +544,16 @@ class Distance(FlatCAMTool): else: pos = (pos_canvas[0], pos_canvas[1]) - self.app.ui.position_label.setText( - "    X: {}   Y: {}".format( - '%.*f' % (self.decimals, pos[0]), '%.*f' % (self.decimals, pos[1]) - ) - ) + # self.app.ui.position_label.setText( + # "    X: {}   Y: {}".format( + # '%.*f' % (self.decimals, pos[0]), '%.*f' % (self.decimals, pos[1]) + # ) + # ) + + units = self.app.defaults["units"].lower() + self.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + 0.0000, units, 0.0000, units, pos[0], units, pos[1], units) if self.rel_point1 is not None: dx = pos[0] - float(self.rel_point1[0]) diff --git a/flatcamTools/ToolNCC.py b/flatcamTools/ToolNCC.py index 0b80c735..d01f02de 100644 --- a/flatcamTools/ToolNCC.py +++ b/flatcamTools/ToolNCC.py @@ -1825,16 +1825,22 @@ class NonCopperClear(FlatCAMTool, Gerber): edge_width=self.app.defaults["global_cursor_width"], size=self.app.defaults["global_cursor_size"]) - # update the positions on status bar - self.app.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (curr_pos[0], curr_pos[1])) if self.cursor_pos is None: self.cursor_pos = (0, 0) self.app.dx = curr_pos[0] - float(self.cursor_pos[0]) self.app.dy = curr_pos[1] - float(self.cursor_pos[1]) - self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (self.app.dx, self.app.dy)) + + # # update the positions on status bar + # self.app.ui.position_label.setText("    X: %.4f   " + # "Y: %.4f" % (curr_pos[0], curr_pos[1])) + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.app.dx, self.app.dy)) + + units = self.app.defaults["units"].lower() + self.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.app.dx, units, self.app.dy, units, curr_pos[0], units, curr_pos[1], units) # draw the utility geometry if shape_type == "square": diff --git a/flatcamTools/ToolPaint.py b/flatcamTools/ToolPaint.py index 4dda2d36..ae1b23cc 100644 --- a/flatcamTools/ToolPaint.py +++ b/flatcamTools/ToolPaint.py @@ -1724,16 +1724,22 @@ class ToolPaint(FlatCAMTool, Gerber): edge_width=self.app.defaults["global_cursor_width"], size=self.app.defaults["global_cursor_size"]) - # update the positions on status bar - self.app.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (curr_pos[0], curr_pos[1])) if self.cursor_pos is None: self.cursor_pos = (0, 0) self.app.dx = curr_pos[0] - float(self.cursor_pos[0]) self.app.dy = curr_pos[1] - float(self.cursor_pos[1]) - self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (self.app.dx, self.app.dy)) + + # # update the positions on status bar + # self.app.ui.position_label.setText("    X: %.4f   " + # "Y: %.4f" % (curr_pos[0], curr_pos[1])) + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.app.dx, self.app.dy)) + + units = self.app.defaults["units"].lower() + self.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.app.dx, units, self.app.dy, units, curr_pos[0], units, curr_pos[1], units) # draw the utility geometry if shape_type == "square":