Просмотр исходного кода

- 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

Marius Stanciu 5 лет назад
Родитель
Сommit
46367c433f

+ 6 - 0
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
 10.05.2020
 
 
 - fixed the problem with using comma as decimal separator in Grid Snap fields
 - fixed the problem with using comma as decimal separator in Grid Snap fields

+ 43 - 22
FlatCAMApp.py

@@ -285,6 +285,8 @@ class App(QtCore.QObject):
         :rtype: App
         :rtype: App
         """
         """
 
 
+        super().__init__()
+
         App.log.info("FlatCAM Starting...")
         App.log.info("FlatCAM Starting...")
 
 
         self.main_thread = QtWidgets.QApplication.instance().thread()
         self.main_thread = QtWidgets.QApplication.instance().thread()
@@ -504,8 +506,6 @@ class App(QtCore.QObject):
         self.FC_light_blue = '#a5a5ffbf'
         self.FC_light_blue = '#a5a5ffbf'
         self.FC_dark_blue = '#0000ffbf'
         self.FC_dark_blue = '#0000ffbf'
 
 
-        QtCore.QObject.__init__(self)
-
         self.ui = FlatCAMGUI(self)
         self.ui = FlatCAMGUI(self)
 
 
         theme_settings = QtCore.QSettings("Open Source", "FlatCAM")
         theme_settings = QtCore.QSettings("Open Source", "FlatCAM")
@@ -5378,14 +5378,20 @@ class App(QtCore.QObject):
                                      edge_width=self.defaults["global_cursor_width"],
                                      edge_width=self.defaults["global_cursor_width"],
                                      size=self.defaults["global_cursor_size"])
                                      size=self.defaults["global_cursor_size"])
 
 
-        # Set the position label
-        self.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                       "<b>Y</b>: %.4f" % (location[0], location[1]))
         # Set the relative position label
         # Set the relative position label
         dx = location[0] - float(self.rel_point1[0])
         dx = location[0] - float(self.rel_point1[0])
         dy = location[1] - float(self.rel_point1[1])
         dy = location[1] - float(self.rel_point1[1])
-        self.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-                                           "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (dx, dy))
+        # self.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+        #                                "<b>Y</b>: %.4f" % (location[0], location[1]))
+        # # Set the position label
+        #
+        # self.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+        #                                    "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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."))
         self.inform.emit('[success] %s' % _("Done."))
         return location
         return location
@@ -5527,14 +5533,19 @@ class App(QtCore.QObject):
                                      edge_width=self.defaults["global_cursor_width"],
                                      edge_width=self.defaults["global_cursor_width"],
                                      size=self.defaults["global_cursor_size"])
                                      size=self.defaults["global_cursor_size"])
 
 
-        # Set the position label
-        self.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                       "<b>Y</b>: %.4f" % (location[0], location[1]))
         # Set the relative position label
         # Set the relative position label
         self.dx = location[0] - float(self.rel_point1[0])
         self.dx = location[0] - float(self.rel_point1[0])
         self.dy = location[1] - float(self.rel_point1[1])
         self.dy = location[1] - float(self.rel_point1[1])
-        self.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-                                           "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.dx, self.dy))
+        # Set the position label
+        # self.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+        #                                "<b>Y</b>: %.4f" % (location[0], location[1]))
+        # self.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+        #                                    "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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."))
         self.inform.emit('[success] %s' % _("Done."))
         return location
         return location
@@ -5843,8 +5854,8 @@ class App(QtCore.QObject):
         self.ui.plot_tab_area.addTab(self.ui.preferences_tab, _("Preferences"))
         self.ui.plot_tab_area.addTab(self.ui.preferences_tab, _("Preferences"))
 
 
         # delete the absolute and relative position and messages in the infobar
         # 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
         # Switch plot_area to preferences page
         self.ui.plot_tab_area.setCurrentWidget(self.ui.preferences_tab)
         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
             try:  # May fail in case mouse not within axes
                 pos_canvas = self.plotcanvas.translate_coords(event_pos)
                 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():
                 if self.grid_status():
                     pos = self.geo_editor.snap(pos_canvas[0], pos_canvas[1])
                     pos = self.geo_editor.snap(pos_canvas[0], pos_canvas[1])
 
 
@@ -6749,13 +6763,19 @@ class App(QtCore.QObject):
                 else:
                 else:
                     pos = (pos_canvas[0], pos_canvas[1])
                     pos = (pos_canvas[0], pos_canvas[1])
 
 
-                self.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                               "<b>Y</b>: %.4f" % (pos[0], pos[1]))
-
                 self.dx = pos[0] - float(self.rel_point1[0])
                 self.dx = pos[0] - float(self.rel_point1[0])
                 self.dy = pos[1] - float(self.rel_point1[1])
                 self.dy = pos[1] - float(self.rel_point1[1])
-                self.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-                                                   "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.dx, self.dy))
+
+                # self.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+                #                                "<b>Y</b>: %.4f" % (pos[0], pos[1]))
+                # self.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+                #                                    "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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]]
                 self.mouse = [pos[0], pos[1]]
 
 
                 # if the mouse is moved and the LMB is clicked then the action is a selection
                 # 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
                             # In this case poly_obj creation (see above) will fail
                             pass
                             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
                 self.mouse = None
 
 
     def on_mouse_click_release_over_plot(self, event):
     def on_mouse_click_release_over_plot(self, event):

+ 9 - 4
FlatCAMCommon.py

@@ -466,15 +466,20 @@ class ExclusionAreas(QtCore.QObject):
                                          size=self.app.defaults["global_cursor_size"])
                                          size=self.app.defaults["global_cursor_size"])
 
 
         # update the positions on status bar
         # update the positions on status bar
-        self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                           "<b>Y</b>: %.4f" % (curr_pos[0], curr_pos[1]))
         if self.cursor_pos is None:
         if self.cursor_pos is None:
             self.cursor_pos = (0, 0)
             self.cursor_pos = (0, 0)
 
 
         self.app.dx = curr_pos[0] - float(self.cursor_pos[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.dy = curr_pos[1] - float(self.cursor_pos[1])
-        self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
+        # self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+        #                                    "<b>Y</b>: %.4f" % (curr_pos[0], curr_pos[1]))
+        # self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+        #                                        "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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':
         if self.obj_type == 'excellon':
             color = "#FF7400"
             color = "#FF7400"

+ 195 - 0
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
+        
+        # <VisPyCanvas>
+        self.vispy_canvas.create_native()
+        self.vispy_canvas.native.setParent(self.my_app.ui)
+
+        # <QtCore.QObject>
+        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_())

+ 1 - 0
defaults.py

@@ -43,6 +43,7 @@ class FlatCAMDefaults:
 
 
         # General
         # General
         "global_graphic_engine": '3D',
         "global_graphic_engine": '3D',
+        "global_hud": True,
         "global_app_level": 'b',
         "global_app_level": 'b',
         "global_portable": False,
         "global_portable": False,
         "global_language": 'English',
         "global_language": 'English',

+ 11 - 7
flatcamEditors/FlatCAMExcEditor.py

@@ -3801,18 +3801,22 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.snap_x = x
         self.snap_x = x
         self.snap_y = y
         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("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                           "<b>Y</b>: %.4f" % (x, y))
-
         if self.pos is None:
         if self.pos is None:
             self.pos = (0, 0)
             self.pos = (0, 0)
         self.app.dx = x - self.pos[0]
         self.app.dx = x - self.pos[0]
         self.app.dy = y - self.pos[1]
         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("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+        #                                    "<b>Y</b>: %.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("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+        #                                        "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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)
         # ## Utility geometry (animated)
         self.update_utility_geometry(data=(x, y))
         self.update_utility_geometry(data=(x, y))

+ 12 - 7
flatcamEditors/FlatCAMGeoEditor.py

@@ -4271,18 +4271,23 @@ class FlatCAMGeoEditor(QtCore.QObject):
         self.snap_y = y
         self.snap_y = y
         self.app.mouse = [x, 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("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                           "<b>Y</b>: %.4f" % (x, y))
-
         if self.pos is None:
         if self.pos is None:
             self.pos = (0, 0)
             self.pos = (0, 0)
         self.app.dx = x - self.pos[0]
         self.app.dx = x - self.pos[0]
         self.app.dy = y - self.pos[1]
         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("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+        #                                    "<b>Y</b>: %.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("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+        #                                        "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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):
         if event.button == 1 and event_is_dragging and isinstance(self.active_tool, FCEraser):
             pass
             pass

+ 12 - 7
flatcamEditors/FlatCAMGrbEditor.py

@@ -4774,18 +4774,23 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
 
         self.app.mouse = [x, 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("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   " 
-                                           "<b>Y</b>: %.4f" % (x, y))
-
         if self.pos is None:
         if self.pos is None:
             self.pos = (0, 0)
             self.pos = (0, 0)
         self.app.dx = x - self.pos[0]
         self.app.dx = x - self.pos[0]
         self.app.dy = y - self.pos[1]
         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("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: " 
-                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+        #                                    "<b>Y</b>: %.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("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+        #                                        "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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))
         self.update_utility_geometry(data=(x, y))
 
 

+ 16 - 11
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.snap_infobar_label.setPixmap(QtGui.QPixmap(self.app.resource_location + '/snap_16.png'))
         self.infobar.addWidget(self.snap_infobar_label)
         self.infobar.addWidget(self.snap_infobar_label)
 
 
-        self.rel_position_label = QtWidgets.QLabel(
-            "<b>Dx</b>: 0.0000&nbsp;&nbsp;   <b>Dy</b>: 0.0000&nbsp;&nbsp;&nbsp;&nbsp;")
-        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(
-            "&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: 0.0000&nbsp;&nbsp;   <b>Y</b>: 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(
+        #     "<b>Dx</b>: 0.0000&nbsp;&nbsp;   <b>Dy</b>: 0.0000&nbsp;&nbsp;&nbsp;&nbsp;")
+        # 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(
+        #     "&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: 0.0000&nbsp;&nbsp;   <b>Y</b>: 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 = QtWidgets.QLabel("[in]")
         self.units_label.setMargin(2)
         self.units_label.setMargin(2)
@@ -2993,6 +2993,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 if key == QtCore.Qt.Key_G:
                 if key == QtCore.Qt.Key_G:
                     self.app.on_toggle_axis()
                     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
                 # Locate in Object
                 if key == QtCore.Qt.Key_J:
                 if key == QtCore.Qt.Key_J:
                     self.app.on_locate(obj=self.app.collection.get_active())
                     self.app.on_locate(obj=self.app.collection.get_active())

+ 31 - 2
flatcamGUI/PlotCanvas.py

@@ -10,7 +10,7 @@ from PyQt5 import QtCore
 import logging
 import logging
 from flatcamGUI.VisPyCanvas import VisPyCanvas, Color
 from flatcamGUI.VisPyCanvas import VisPyCanvas, Color
 from flatcamGUI.VisPyVisuals import ShapeGroup, ShapeCollection, TextCollection, TextGroup, Cursor
 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
 import numpy as np
 from vispy.geometry import Rect
 from vispy.geometry import Rect
@@ -54,8 +54,12 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
 
 
         if theme == 'white':
         if theme == 'white':
             self.line_color = (0.3, 0.0, 0.0, 1.0)
             self.line_color = (0.3, 0.0, 0.0, 1.0)
+            self.rect_hud_color = Color('#0000FF10')
+            self.text_hud_color = 'black'
         else:
         else:
             self.line_color = (0.4, 0.4, 0.4, 1.0)
             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,
         # workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node,
         # which might decrease performance
         # 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,
         self.cursor_h_line = InfiniteLine(pos=None, color=c_color, vertical=False,
                                           parent=self.line_parent)
                                           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_collections = []
 
 
         self.shape_collection = self.new_shape_collection()
         self.shape_collection = self.new_shape_collection()
         self.fcapp.pool_recreated.connect(self.on_pool_recreated)
         self.fcapp.pool_recreated.connect(self.on_pool_recreated)
         self.text_collection = self.new_text_collection()
         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.text_collection.enabled = True
 
 
         self.c = None
         self.c = None
@@ -163,6 +182,16 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
 
 
         self.graph_event_connect('mouse_wheel', self.on_mouse_scroll)
         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):
     def draw_workspace(self, workspace_size):
         """
         """
         Draw a rectangular shape on canvas to specify our valid workspace.
         Draw a rectangular shape on canvas to specify our valid workspace.

+ 73 - 0
flatcamGUI/PlotCanvasLegacy.py

@@ -29,6 +29,7 @@ mpl_use("Qt5Agg")
 from matplotlib.figure import Figure
 from matplotlib.figure import Figure
 from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
 from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
 from matplotlib.lines import Line2D
 from matplotlib.lines import Line2D
+from matplotlib.offsetbox import AnchoredText
 # from matplotlib.widgets import Cursor
 # from matplotlib.widgets import Cursor
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -147,9 +148,13 @@ class PlotCanvasLegacy(QtCore.QObject):
         if self.app.defaults['global_theme'] == 'white':
         if self.app.defaults['global_theme'] == 'white':
             theme_color = '#FFFFFF'
             theme_color = '#FFFFFF'
             tick_color = '#000000'
             tick_color = '#000000'
+            self.rect_hud_color = '#0000FF10'
+            self.text_hud_color = '#000000'
         else:
         else:
             theme_color = '#000000'
             theme_color = '#000000'
             tick_color = '#FFFFFF'
             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,
         # workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node,
         # which might decrease performance
         # which might decrease performance
@@ -298,11 +303,79 @@ class PlotCanvasLegacy(QtCore.QObject):
         # signal if there is a doubleclick
         # signal if there is a doubleclick
         self.is_dblclk = False
         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
         # 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
         # all CNC have a limited workspace
         if self.app.defaults['global_workspace'] is True:
         if self.app.defaults['global_workspace'] is True:
             self.draw_workspace(workspace_size=self.app.defaults["global_workspaceT"])
             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):
     def draw_workspace(self, workspace_size):
         """
         """
         Draw a rectangular shape on canvas to specify our valid workspace.
         Draw a rectangular shape on canvas to specify our valid workspace.

+ 1 - 0
flatcamGUI/VisPyCanvas.py

@@ -13,6 +13,7 @@ import numpy as np
 
 
 import vispy.scene as scene
 import vispy.scene as scene
 from vispy.scene.cameras.base_camera import BaseCamera
 from vispy.scene.cameras.base_camera import BaseCamera
+# from vispy.scene.widgets import Widget as VisPyWidget
 from vispy.color import Color
 from vispy.color import Color
 
 
 import time
 import time

+ 11 - 5
flatcamTools/ToolCopperThieving.py

@@ -910,16 +910,22 @@ class ToolCopperThieving(FlatCAMTool):
                                          edge_width=self.app.defaults["global_cursor_width"],
                                          edge_width=self.app.defaults["global_cursor_width"],
                                          size=self.app.defaults["global_cursor_size"])
                                          size=self.app.defaults["global_cursor_size"])
 
 
-        # update the positions on status bar
-        self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                           "<b>Y</b>: %.4f" % (curr_pos[0], curr_pos[1]))
         if self.cursor_pos is None:
         if self.cursor_pos is None:
             self.cursor_pos = (0, 0)
             self.cursor_pos = (0, 0)
 
 
         self.app.dx = curr_pos[0] - float(self.cursor_pos[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.dy = curr_pos[1] - float(self.cursor_pos[1])
-        self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
+
+        # # update the positions on status bar
+        # self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+        #                                    "<b>Y</b>: %.4f" % (curr_pos[0], curr_pos[1]))
+        # self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+        #                                        "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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
         # draw the utility geometry
         if self.first_click:
         if self.first_click:

+ 10 - 5
flatcamTools/ToolDistance.py

@@ -544,11 +544,16 @@ class Distance(FlatCAMTool):
             else:
             else:
                 pos = (pos_canvas[0], pos_canvas[1])
                 pos = (pos_canvas[0], pos_canvas[1])
 
 
-            self.app.ui.position_label.setText(
-                "&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: {}&nbsp;&nbsp;   <b>Y</b>: {}".format(
-                    '%.*f' % (self.decimals, pos[0]), '%.*f' % (self.decimals, pos[1])
-                )
-            )
+            # self.app.ui.position_label.setText(
+            #     "&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: {}&nbsp;&nbsp;   <b>Y</b>: {}".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:
             if self.rel_point1 is not None:
                 dx = pos[0] - float(self.rel_point1[0])
                 dx = pos[0] - float(self.rel_point1[0])

+ 11 - 5
flatcamTools/ToolNCC.py

@@ -1825,16 +1825,22 @@ class NonCopperClear(FlatCAMTool, Gerber):
                                          edge_width=self.app.defaults["global_cursor_width"],
                                          edge_width=self.app.defaults["global_cursor_width"],
                                          size=self.app.defaults["global_cursor_size"])
                                          size=self.app.defaults["global_cursor_size"])
 
 
-        # update the positions on status bar
-        self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                           "<b>Y</b>: %.4f" % (curr_pos[0], curr_pos[1]))
         if self.cursor_pos is None:
         if self.cursor_pos is None:
             self.cursor_pos = (0, 0)
             self.cursor_pos = (0, 0)
 
 
         self.app.dx = curr_pos[0] - float(self.cursor_pos[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.dy = curr_pos[1] - float(self.cursor_pos[1])
-        self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
+
+        # # update the positions on status bar
+        # self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+        #                                    "<b>Y</b>: %.4f" % (curr_pos[0], curr_pos[1]))
+        # self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+        #                                        "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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
         # draw the utility geometry
         if shape_type == "square":
         if shape_type == "square":

+ 11 - 5
flatcamTools/ToolPaint.py

@@ -1724,16 +1724,22 @@ class ToolPaint(FlatCAMTool, Gerber):
                                          edge_width=self.app.defaults["global_cursor_width"],
                                          edge_width=self.app.defaults["global_cursor_width"],
                                          size=self.app.defaults["global_cursor_size"])
                                          size=self.app.defaults["global_cursor_size"])
 
 
-        # update the positions on status bar
-        self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                           "<b>Y</b>: %.4f" % (curr_pos[0], curr_pos[1]))
         if self.cursor_pos is None:
         if self.cursor_pos is None:
             self.cursor_pos = (0, 0)
             self.cursor_pos = (0, 0)
 
 
         self.app.dx = curr_pos[0] - float(self.cursor_pos[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.dy = curr_pos[1] - float(self.cursor_pos[1])
-        self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
+
+        # # update the positions on status bar
+        # self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+        #                                    "<b>Y</b>: %.4f" % (curr_pos[0], curr_pos[1]))
+        # self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+        #                                        "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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
         # draw the utility geometry
         if shape_type == "square":
         if shape_type == "square":