Merge branch 'Beta' into preferences-refactoring

This commit is contained in:
David Robertson 2020-05-11 20:34:43 +01:00
commit 28a437aede
16 changed files with 473 additions and 86 deletions

View File

@ -7,6 +7,16 @@ 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
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).

View File

@ -285,6 +285,8 @@ class App(QtCore.QObject):
:rtype: App
"""
super().__init__()
App.log.info("FlatCAM Starting...")
self.main_thread = QtWidgets.QApplication.instance().thread()
@ -506,8 +508,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")
@ -5242,14 +5242,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("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
"<b>Y</b>: %.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("<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."))
return location
@ -5391,14 +5397,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("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
"<b>Y</b>: %.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("<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."))
return location
@ -5707,8 +5718,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)
@ -6602,6 +6613,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])
@ -6613,13 +6627,19 @@ class App(QtCore.QObject):
else:
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.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]]
# if the mouse is moved and the LMB is clicked then the action is a selection
@ -6668,9 +6688,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):

View File

@ -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("&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:
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("<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':
color = "#FF7400"

195
Utils/vispy_example.py Normal file
View File

@ -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_())

View File

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

View File

@ -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("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
"<b>Y</b>: %.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("<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)
self.update_utility_geometry(data=(x, y))

View File

@ -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(
@ -4261,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("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
"<b>Y</b>: %.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("<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):
pass

View File

@ -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("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
"<b>Y</b>: %.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("<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))

View File

@ -2223,17 +2223,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(
"<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.setMargin(2)
@ -2910,6 +2910,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())

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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("&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:
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("<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
if self.first_click:

View File

@ -544,11 +544,16 @@ class Distance(FlatCAMTool):
else:
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:
dx = pos[0] - float(self.rel_point1[0])

View File

@ -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("&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:
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("<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
if shape_type == "square":

View File

@ -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("&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:
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("<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
if shape_type == "square":