Skip to content

Commit 56b906c

Browse files
committed
Merge branch '2.9'
2 parents 09e2d51 + 22bebc4 commit 56b906c

12 files changed

Lines changed: 339 additions & 15 deletions

File tree

configs/sim/qtdragon/qtdragon_tool_probe/qtdragon_auto_tool_probe.ini

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,17 @@ USE_PROBE = versaprobe
163163
# Abs (G53) coordinates remap tool change point
164164
X = 10
165165
Y = 10
166-
Z = -2
166+
Z = 8
167167

168168
# where in absolute machine units to probe the tool
169169
[VERSA_TOOLSETTER]
170170
X = 300
171171
Y = 300
172172
Z = -50
173+
# how migh to lift when moving to the tool setter
174+
Z_MAX_CLEAR = 9.999
175+
# tool setter diameter for diameter probing
176+
DIAMETER = 25
173177
# maximuim machine units to lower while probing
174178
MAXPROBE = -40
175179

docs/src/gui/images/qtdragon.png

65.8 KB
Loading

docs/src/gui/qtdragon.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,7 @@ Probing is very unforgiving to mistakes; be sure to check settings before using.
707707
QtDragon has 2 methods for setting Z0.
708708
The first is a touchplate, where a metal plate of known thickness is placed on top of the workpiece,
709709
then the tool is lowered until it touches the plate, triggering the probe signal.
710-
Z0 is set to probe height - plate thickness.
710+
The current user system's (G5x) Z0 is set to probe height - the entered plate thickness.
711711

712712
The second method uses a tool setter in a fixed position and a known height above the table where the probe signal will be triggered.
713713
In order to set Z0 to the top of the workpiece, it has to know

docs/src/user/images/qtdragon.png

224 KB
Loading

docs/src/user/user-intro.adoc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,16 @@ image::images/tklinuxcnc_fr.png["TkLinuxCNC graphical interface",align="center"]
138138

139139
Xemc:: an X-Window program
140140

141+
QtDragon:: <<cha:qtdragon-gui,QtDragon>>, a touch screen GUI based on QtVCP using the PyQt5 library. It comes in two versions
142+
'QtDragon' and 'QtDragon_hd'. They are very similar in features but QtDragon_hd is made for larger monitors.
143+
144+
[[fig:QtDragon-graphical-interface]]
145+
.QtDragon, a touch screen GUI based on QtVCP
146+
image::images/qtdragon.png["QtDragon, a touch screen GUI based on QtVCP",align="center"]
147+
148+
== User Interfaces
149+
These User interfaces are a way to interact with linuxcnc outside of the graphical user interfaces.
150+
141151
halui:: A HAL based user interface allowing to control LinuxCNC using buttons and switches
142152

143153
linuxcncrsh:: A telnet based user interface allowing to send commands from remote computers.
@@ -165,6 +175,14 @@ GladeVCP:: <<cha:glade-vcp,'GladeVCP'>>, a Glade-based virtual control panel tha
165175
.GladeVCP Example Embedded Into AXIS GUI
166176
image::../gui/images/axis-gladevcp.png["GladeVCP embedded into AXIS",align="center"]
167177

178+
QtVCP:: <<cha:qtvcp,'QtVCP'>>, a PyQT5-based virtual control panel that can be added to most GUIs or run as a standalone panel.
179+
QtVCP has the advantage over PyVCP in that it is not limited to the display or control of HAL virtual signals,
180+
but can include other external interfaces outside LinuxCNC such as window or network events by extending with python code.
181+
QtVCP is also more flexible in how it may be configured to appear on the GUI with many special widgets:
182+
183+
.QtVCP Example Embedded Into QtDragon GUI
184+
image::../gui/images/qtvcp_spindle_belts.png["QtVCP panel embedded into QtDragon",align="center"]
185+
168186
== Languages
169187

170188
LinuxCNC uses translation files to translate LinuxCNC User Interfaces into many languages including

lib/python/qtvcp/qt_istat.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,8 @@ def update(self):
518518
###################
519519
# helper functions
520520
###################
521-
521+
# return a found string or else None by default, anything else by option
522+
# since this is used in this file there are some workarounds for plasma machines
522523
def get_error_safe_setting(self, heading, detail, default=None):
523524
result = self.INI.find(heading, detail)
524525
if result:
@@ -531,6 +532,22 @@ def get_error_safe_setting(self, heading, detail, default=None):
531532
log.warning('INI Parsing Error, No {} Entry in {}, Using: {}'.format(detail, heading, default))
532533
return default
533534

535+
# return a found float or else None by default, anything else by option
536+
def get_safe_float(self, heading, detail, default=None):
537+
try:
538+
result = float(self.INI.find(heading, detail))
539+
return result
540+
except:
541+
return default
542+
543+
# return a found integer or else None by default, anything else by option
544+
def get_safe_int(self, heading, detail, default=None):
545+
try:
546+
result = int(self.INI.find(heading, detail))
547+
return result
548+
except:
549+
return default
550+
534551
def convert_machine_to_metric(self, data):
535552
if self.MACHINE_IS_METRIC:
536553
return data

lib/python/qtvcp/widgets/probe_routines.py

Lines changed: 193 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
class ProbeRoutines():
2121
def __init__(self):
2222
self.timeout = 30
23-
2423
##################
2524
# Helper Functions
2625
##################
@@ -126,6 +125,47 @@ def probe(self, name):
126125
return 'Probe {} failed: {}'.format(name, rtn)
127126
return 1
128127

128+
def goto_toolsetter(self):
129+
try:
130+
# basic sanity checks
131+
for test in('z_max_clear','ts_x','ts_y','ts_z','ts_max'):
132+
if self['data_{}'.format(test)] is None:
133+
return'Missing toolsetter setting: {}'.format(test)
134+
if self.data_tool_diameter is None:
135+
return 'No tool diameter found'
136+
137+
# raise to safe Z height
138+
# move to tool setter (XY then Z)
139+
# offset X by tool radius (from toolfile)
140+
# probe Z
141+
# raise z clear
142+
# move back X by tool radius
143+
144+
cmdList = []
145+
cmdList.append('F{}'.format(self.data_rapid_vel))
146+
cmdList.append('G53 G1 Z{}'.format(self.data_z_max_clear))
147+
cmdList.append('G53 G1 X{} Y{}'.format(self.data_ts_x, self.data_ts_y))
148+
cmdList.append('G53 G1 Z{}'.format(self.data_ts_z))
149+
cmdList.append('G91 ')
150+
cmdList.append('G1 X{}'.format(self.data_tool_diameter/2))
151+
cmdList.append('G38.2 Z{} F{}'.format(self.data_ts_max,self.data_search_vel))
152+
cmdList.append('G1 Z{} F{}'.format(self.data_latch_return_dist, self.data_rapid_vel))
153+
cmdList.append('F{} G4 P0.5'.format(self.data_probe_vel))
154+
cmdList.append('G38.2 Z-{}'.format(self.data_latch_return_dist*2))
155+
cmdList.append('G1 Z{} F{}'.format(self.data_z_clearance, self.data_rapid_vel))
156+
cmdList.append('G1 X-{}'.format(self.data_tool_diameter/2))
157+
cmdList.append('G90')
158+
159+
# call each command - if fail report the error and gcode command
160+
for s in cmdList:
161+
rtn = self.CALL_MDI_WAIT(s, self.timeout)
162+
if rtn != 1:
163+
return 'failed: {} cmd: {}'.format(rtn, s)
164+
# report success
165+
return 1
166+
except Exception as e:
167+
return e
168+
129169
def CALL_MDI_WAIT(self, code, timeout = 5):
130170
LOG.debug('MDI_WAIT_COMMAND= {}, maxt = {}'.format(code, timeout))
131171
for l in code.split("\n"):
@@ -1483,7 +1523,7 @@ def probe_valley_y(self):
14831523
def probe_cal_round_pocket(self):
14841524
if self.data_cal_diameter_hint <= 0 :
14851525
return 'Calibration diameter hint must be larger then 0'
1486-
if self.data_probe_diameter >= self.data_cal_diameter_hint:
1526+
if self.data_probe_diam >= self.data_cal_diameter_hint:
14871527
return 'Probe diameter too large for Calibration diameter hint'
14881528

14891529
self.data_side_edge_length = self.data_cal_diameter / 2
@@ -1631,3 +1671,154 @@ def probe_angle_right(self):
16311671
self.status_a = math.degrees(math.atan2(self.status_delta, self.data_side_edge_length))
16321672
self.rotate_coord_system(self.status_a)
16331673
return error
1674+
1675+
# TOOL setter Diameter/height
1676+
# returns 1 for success or a string error message for failure
1677+
def probe_tool_z_d(self):
1678+
try:
1679+
# move XY to Tool Setter point
1680+
# Start goto ts (probes Z height too)
1681+
rtn = self.goto_toolsetter()
1682+
if rtn != 1:
1683+
return 'failed: {}'.format(rtn)
1684+
# move X - edge_length- xy_clearance
1685+
s="""G91
1686+
G1 F%s X-%f
1687+
G90""" % (self.data_rapid_vel, 0.5 * self.data_ts_diam + self.data_xy_clearance)
1688+
rtn = self.CALL_MDI_WAIT(s, self.timeout)
1689+
if rtn != 1:
1690+
return 'failed: {}'.format(rtn)
1691+
rtn = self.z_clearance_down()
1692+
if rtn != 1:
1693+
return 'failed: {}'.format(rtn)
1694+
1695+
# Start xplus
1696+
rtn = self.probe('xplus')
1697+
if rtn != 1:
1698+
return 'failed: {}'.format(rtn)
1699+
1700+
# show X result
1701+
a = STATUS.get_probed_position_with_offsets()
1702+
xpres=float(a[0])+0.5*self.data_probe_diam
1703+
1704+
# move Z to start point up
1705+
rtn = self.z_clearance_up()
1706+
if rtn != 1:
1707+
return 'failed: {}'.format(rtn)
1708+
1709+
# move to finded point X
1710+
s = "G1 F%s X%f" % (self.data_rapid_vel, xpres)
1711+
rtn = self.CALL_MDI_WAIT(s, self.timeout)
1712+
if rtn != 1:
1713+
return 'failed: {}'.format(rtn)
1714+
1715+
# move X + data_ts_diam + xy_clearance
1716+
aa=self.data_ts_diam+self.data_xy_clearance
1717+
s="""G91
1718+
G1 X%f
1719+
G90""" % (aa)
1720+
rtn = self.CALL_MDI_WAIT(s, self.timeout)
1721+
if rtn != 1:
1722+
return 'failed: {}'.format(rtn)
1723+
1724+
rtn = self.z_clearance_down()
1725+
if rtn != 1:
1726+
return 'failed: {}'.format(rtn)
1727+
1728+
# Start xminus
1729+
rtn = self.probe('xminus')
1730+
if rtn != 1:
1731+
return 'failed: {}'.format(rtn)
1732+
1733+
# show X result
1734+
a = STATUS.get_probed_position_with_offsets()
1735+
xmres=float(a[0])-0.5*self.data_probe_diam
1736+
self.length_x()
1737+
xcres=0.5*(xpres+xmres)
1738+
self.status_xc = xcres
1739+
1740+
# move Z to start point up
1741+
rtn = self.z_clearance_up()
1742+
if rtn != 1:
1743+
return 'failed: {}'.format(rtn)
1744+
1745+
# go to the new center of X
1746+
s = "G1 F%s X%f" % (self.data_rapid_vel, xcres)
1747+
rtn = self.CALL_MDI_WAIT(s, self.timeout)
1748+
if rtn != 1:
1749+
return 'failed: {}'.format(rtn)
1750+
1751+
# move Y - data_ts_diam/2 - xy_clearance
1752+
a=0.5*self.data_ts_diam+self.data_xy_clearance
1753+
s="""G91
1754+
G1 Y-%f
1755+
G90""" % a
1756+
rtn = self.CALL_MDI_WAIT(s, self.timeout)
1757+
if rtn != 1:
1758+
return 'failed: {}'.format(rtn)
1759+
1760+
rtn = self.z_clearance_down()
1761+
if rtn != 1:
1762+
return 'failed: {}'.format(rtn)
1763+
1764+
# Start yplus
1765+
rtn = self.probe('yplus')
1766+
if rtn != 1:
1767+
return 'failed: {}'.format(rtn)
1768+
1769+
# show Y result
1770+
a = STATUS.get_probed_position_with_offsets()
1771+
ypres=float(a[1])+0.5*self.data_probe_diam
1772+
# move Z to start point up
1773+
if self.z_clearance_up() == -1:
1774+
return
1775+
1776+
# move to found point Y
1777+
s = "G1 Y%f" % ypres
1778+
rtn = self.CALL_MDI_WAIT(s, self.timeout)
1779+
if rtn != 1:
1780+
return 'failed: {}'.format(rtn)
1781+
1782+
# move Y + data_ts_diam + xy_clearance
1783+
aa=self.data_ts_diam+self.data_xy_clearance
1784+
s="""G91
1785+
G1 Y%f
1786+
G90""" % (aa)
1787+
rtn = self.CALL_MDI_WAIT(s, self.timeout)
1788+
if rtn != 1:
1789+
return 'failed: {}'.format(rtn)
1790+
rtn = self.z_clearance_down()
1791+
if rtn != 1:
1792+
return 'failed: {}'.format(rtn)
1793+
1794+
# Start yminus
1795+
rtn = self.probe('yminus')
1796+
if rtn != 1:
1797+
return 'failed: {}'.format(rtn)
1798+
# show Y result
1799+
a = STATUS.get_probed_position_with_offsets()
1800+
ymres=float(a[1])-0.5*self.data_probe_diam
1801+
self.length_y()
1802+
1803+
# find, show and move to finded point
1804+
ycres=0.5*(ypres+ymres)
1805+
self.status_yc = ycres
1806+
diam=self.data_probe_diam + (ymres-ypres-self.data_ts_diam)
1807+
self.status_d = diam
1808+
1809+
# move Z to start point up
1810+
rtn = self.z_clearance_up()
1811+
if rtn != 1:
1812+
return 'failed: {}'.format(rtn)
1813+
tmpz=STATUS.stat.position[2] - self.data_z_clearance
1814+
self.status_z=tmpz
1815+
self.add_history('Tool diameter',"XcYcZD",0,xcres,0,0,0,ycres,0,0,tmpz,diam,0)
1816+
# move to finded point
1817+
s = "G1 Y%f" % ycres
1818+
rtn = self.CALL_MDI_WAIT(s, self.timeout)
1819+
if rtn != 1:
1820+
return 'failed: {}'.format(rtn)
1821+
# success
1822+
return 1
1823+
except Exception as e:
1824+
return e

lib/python/qtvcp/widgets/probe_subprog.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,15 @@ def __init__(self):
6363
'cal_x_width',
6464
'cal_y_width',
6565
'cal_diameter',
66-
'calibration_offset']
66+
'calibration_offset',
67+
'ts_diam',
68+
'z_max_clear',
69+
'ts_x',
70+
'ts_y',
71+
'ts_z',
72+
'ts_max',
73+
'tool_diameter']
74+
6775
# data structure to hold parameters
6876
# common
6977
self.data_probe_diam = 1.0
@@ -84,6 +92,13 @@ def __init__(self):
8492
self.data_adj_y = 0.0
8593
self.data_adj_z = 0.0
8694
self.data_adj_angle = 0.0
95+
self.data_ts_diam = 1.0
96+
self.data_z_max_clear = None
97+
self.data_ts_x= None
98+
self.data_ts_y= None
99+
self.data_ts_z= None
100+
self.data_ts_max = None
101+
self.data_tool_diameter = None
87102
# BasicProbe exclusive
88103
self.data_x_hint_bp = 0.0
89104
self.data_y_hint_bp = 0.0

lib/python/qtvcp/widgets/versa_probe.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class VersaProbe(QtWidgets.QWidget, _HalWidgetBase):
4949
def __init__(self, parent=None):
5050
super(VersaProbe, self).__init__(parent)
5151
self.proc = None
52+
self.tool_diameter = None
53+
STATUS.connect('tool-info-changed', lambda w, data: self._tool_info(data))
5254
if INFO.MACHINE_IS_METRIC:
5355
self.valid = QtGui.QRegExpValidator(QtCore.QRegExp('^((\d{1,4}(\.\d{1,3})?)|(\.\d{1,3}))$'))
5456
else:
@@ -113,6 +115,14 @@ def eventFilter(self, obj, event):
113115
self.popEntry(obj)
114116
return super(VersaProbe, self).eventFilter(obj, event)
115117

118+
119+
def _tool_info(self, data):
120+
if data.id != -1:
121+
self.tool_diameter = data.diameter
122+
print(data)
123+
return
124+
self.tool_diameter = None
125+
116126
def _hal_init(self):
117127

118128
def homed_on_test():
@@ -177,6 +187,13 @@ def homed_on_test():
177187
self.input_adj_angle.setText(str(self.PREFS_.getpref( "ps_offs_angle", 0.0, float, 'VERSA_PROBE_OPTIONS')) )
178188
self.input_rapid_vel.setText(str(self.PREFS_.getpref( "ps_probe_rapid_vel", 60.0, float, 'VERSA_PROBE_OPTIONS')) )
179189

190+
self.z_max_clear = INFO.get_safe_float("VERSA_TOOLSETTER", "Z_MAX_CLEAR")
191+
self.ts_x = INFO.get_safe_float('VERSA_TOOLSETTER','X')
192+
self.ts_y = INFO.get_safe_float('VERSA_TOOLSETTER','Y')
193+
self.ts_z = INFO.get_safe_float('VERSA_TOOLSETTER','Z')
194+
self.ts_max = INFO.get_safe_float('VERSA_TOOLSETTER','MAXPROBE')
195+
self.ts_diam = INFO.get_safe_float('VERSA_TOOLSETTER','DIAMETER')
196+
180197
# make pins available for tool measure remaps
181198
oldname = self.HAL_GCOMP_.comp.getprefix()
182199
self.HAL_GCOMP_.comp.setprefix('qtversaprobe')
@@ -397,6 +414,11 @@ def get_parms(self):
397414
for key in ['allow_auto_zero', 'allow_auto_skew']:
398415
val = '1' if self[key].isChecked() else '0'
399416
self.send_dict.update( {key: val} )
417+
# come from INI
418+
for key in ['ts_diam','z_max_clear','ts_x','ts_y','ts_z','ts_max','tool_diameter']:
419+
val = str(self[key])
420+
if val == 'NONE': val = None
421+
self.send_dict.update( {key: val} )
400422

401423
def check_probe(self):
402424
try:

0 commit comments

Comments
 (0)