Files
2026-08-20 06:17:39 +08:00

6261 lines
295 KiB
Python

'''
plasmac2 is an add-on to the LinuxCNC Axis GUI for creating
a plasma cutting configuration using the plasmac component
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
'''
VER = '21'
##############################################################################
# the next line suppresses undefined variable errors in VSCode #
# pyright: reportUndefinedVariable = false
##############################################################################
##############################################################################
# UPDATES #
##############################################################################
def update_check():
''' check for updates - the newest update must be added last here '''
# v15 (2024 Jan 30) set user_m_path to include <plasmac2/lib>
path = 'plasmac2/lib'
mPath = inifile.find('RS274NGC', 'USER_M_PATH')
if path not in mPath:
version = 'v15'
try:
if os.path.isfile(os.path.join(configPath, 'M190')):
os.rename(os.path.join(configPath, 'M190'), os.path.join(configPath, 'M190.bak'))
tmpFile = f'{s.ini_filename}~'
COPY(s.ini_filename, tmpFile)
with open(tmpFile, 'r') as inFile:
with open(s.ini_filename, 'w') as outFile:
for line in inFile:
if line.startswith('USER_M_PATH'):
line = line.replace(mPath, f'./:{path}')
outFile.write(line)
if os.path.isfile(tmpFile):
os.remove(tmpFile)
except Exception as e:
title = 'error'
msg0 = f"Update to {version} failed due to:\n\n{e}"
notifications.add('error', f"{msg0}\n")
else:
title = 'info'
msg0 = f"Updated successfully to {version}\nA restart is required"
notifications.add(title, f"{msg0}\n")
##############################################################################
# NEW CLASSES #
##############################################################################
# class for preferences file
prefP = configparser.ConfigParser
class plasmacPreferences(prefP):
optionxform = str
types = {bool: prefP.getboolean,
float: prefP.getfloat,
int: prefP.getint,
str: prefP.get,
repr: lambda self, section, option: eval(prefP.get(self, section, option)),
}
def __init__(self):
prefP.__init__(self, strict=False, interpolation=None)
self.fn = os.path.join(configPath, f"{vars.machine.get()}.prefs")
self.read(self.fn)
# class for materials file
matsP = configparser.ConfigParser
class plasmacMaterials(matsP):
optionxform = str
types = {bool: matsP.getboolean,
float: matsP.getfloat,
int: matsP.getint,
str: matsP.get,
repr: lambda self, section, option: eval(matsP.get(self, section, option)),
}
def __init__(self):
matsP.__init__(self)
self.fn = os.path.join(configPath, f"{vars.machine.get()}_material.cfg")
self.read(self.fn)
# class for temp materials file
tempP = configparser.ConfigParser
class plasmacTempMaterial(tempP):
optionxform = str
types = {bool: tempP.getboolean,
float: tempP.getfloat,
int: tempP.getint,
str: tempP.get,
repr: lambda self,section, option: eval(tempP.get(self, section, option)),
}
def __init__(self):
tempP.__init__(self)
self.fn = os.path.join(tmpPath, f"{vars.machine.get()}_material.gcode")
self.read(self.fn)
# class for popup dialogs
class plasmacPopUp(Tkinter.Toplevel):
def __init__(self, func, title, msg, vkb=None, optional=None):
super().__init__(root_window)
self.attributes('-type', 'popup_menu')
self.overrideredirect(True)
if pVars.popLocation.get() == 'pointer':
rE(f"tk::PlaceWindow {self} pointer")
elif pVars.popLocation.get() == 'gui center':
rE(f"tk::PlaceWindow {self} widget {root_window}")
else:
rE(f"tk::PlaceWindow {self} center")
self.wait_visibility()
if func != 'offsets':
self.grab_set()
self.title(title)
self.protocol("WM_DELETE_WINDOW", lambda:self.popup_complete(False, False))
self.frm = Tkinter.Frame(self, bg=colorBack, bd=2, relief='ridge')
ttl = Tkinter.Label(self.frm, text=title, fg=colorBack, bg=colorFore)
ttl.pack(fill='x')
b1Text = b2Text = b3Text = b4Text = None
if func == 'rfl_type':
b1Text, b2Text = self.popup_rfl_type(func)
elif func == 'rfl':
b1Text, b2Text = self.popup_run_from_line(func)
elif func == 'sc':
b1Text, b2Text = self.popup_single_cut(func)
elif func == 'offsets':
b1Text, b2Text, b3Text, b4Text = self.popup_entry(func, msg, optional)
else:
b1Text, b2Text = self.popup_entry(func, msg, optional)
bbox = Tkinter.Frame(self.frm, bg=colorBack)
b1Value = True if func != 'offsets' else 'laser'
b1 = Tkinter.Button(bbox, text=b1Text, command=lambda:self.popup_complete(b1Value, func), width=8)
b1.configure(fg=colorFore, bg=colorBack, activebackground=colorBack, highlightthickness=0)
b1.pack(side='left')
b2Value = False if func != 'offsets' else 'scribe'
if b2Text:
b2 = Tkinter.Button(bbox, text=b2Text, command=lambda:self.popup_complete(b2Value, func), width=8)
b2.configure(fg=colorFore, bg=colorBack, activebackground=colorBack, highlightthickness=0)
b2.pack(side='left', padx=(8,0))
if b3Text:
b3 = Tkinter.Button(bbox, text=b3Text, command=lambda:self.popup_complete('probe', func), width=8)
b3.configure(fg=colorFore, bg=colorBack, activebackground=colorBack, highlightthickness=0)
b3.pack(side='left', padx=(8,0))
if b4Text:
b4 = Tkinter.Button(bbox, text=b4Text, command=lambda:self.popup_complete('cancel', func), width=8)
b4.configure(fg=colorFore, bg=colorBack, activebackground=colorBack, highlightthickness=0)
b4.pack(side='left', padx=(8,0))
bbox.pack(padx=4, pady=4)
self.frm.pack()
if vkb:
vkbData['required'] = True
vkb_show(vkb)
root_window.wait_window(self)
def popup_rfl_type(self, func):
self.rflType = Tkinter.StringVar(self, 'run')
f1 = Tkinter.Frame(self.frm, bg=colorBack)
run = Tkinter.Radiobutton(f1, text=_('Here to end'), variable=self.rflType, value='run', width=12, bg=colorBack, highlightthickness=0, anchor='w')
cut = Tkinter.Radiobutton(f1, text=_('This cutpath'), variable=self.rflType, value='cut', width=12, bg=colorBack, highlightthickness=0, anchor='w')
run.pack(anchor='w')
cut.pack(anchor='w')
f1.pack(padx=4, pady=4, anchor='w')
return _('OK'), _('Cancel')
def popup_run_from_line(self, func):
self.leadIn = Tkinter.BooleanVar()
self.leadLength = Tkinter.StringVar()
self.leadAngle = Tkinter.StringVar()
f1 = Tkinter.Frame(self.frm, bg=colorBack)
lbl1 = Tkinter.Label(f1, text=_('Use Leadin:'), fg=colorFore, bg=colorBack, width=12, anchor='e')
lbl1.pack(side='left')
leadinDo = Tkinter.Checkbutton(f1, fg=colorFore, bg=colorBack, variable=self.leadIn, indicatoron=False, width=2, bd=1)
leadinDo.configure(highlightthickness=0, activebackground=colorBack, selectcolor=colorActive, relief='raised', overrelief='raised')
leadinDo.pack(side='left')
f1.pack(padx=4, pady=4, anchor='w')
f2 = Tkinter.Frame(self.frm, bg=colorBack)
lbl2 = Tkinter.Label(f2, text=_('Leadin Length:'), fg=colorFore, bg=colorBack, width=12, anchor='e')
lbl2.pack(side='left')
leadinLength = Tkinter.Spinbox(f2, fg=colorFore, bg=colorBack, textvariable=self.leadLength, width=10)
leadinLength.configure(highlightthickness=0)
leadinLength.pack(side='left')
f2.pack(padx=4, pady=4, anchor='w')
f3 = Tkinter.Frame(self.frm, bg=colorBack)
lbl3 = Tkinter.Label(f3, text=_('Leadin Angle:'), fg=colorFore, bg=colorBack, width=12, anchor='e')
lbl3.pack(side='left')
leadinAngle = Tkinter.Spinbox(f3, fg=colorFore, bg=colorBack, textvariable=self.leadAngle, width=10)
leadinAngle.configure(highlightthickness=0)
leadinAngle.pack(side='left')
f3.pack(padx=4, pady=4, anchor='w')
self.leadIn.set(False)
if s.linear_units == 1:
leadinLength.config(width=10, from_=1, to=25, increment=1, format='%0.0f', wrap=1)
self.leadLength.set(5)
else:
leadinLength.config(width=10, from_=0.05, to=1, increment=0.05, format='%0.2f', wrap=1)
self.leadLength.set(0.2)
leadinAngle.config(width=10, from_=-359, to=359, increment=1, format='%0.0f', wrap=1)
self.leadAngle.set(0)
return _('Load'), _('Cancel')
def popup_single_cut(self, func):
self.xLength = Tkinter.StringVar()
self.yLength = Tkinter.StringVar()
f1 = Tkinter.Frame(self.frm, bg=colorBack)
lbl1 = Tkinter.Label(f1, text=_('X Length:'), fg=colorFore, bg=colorBack, width=12, anchor='e')
lbl1.pack(side='left')
xLength = Tkinter.Entry(f1, fg=colorFore, bg=colorBack, textvariable=self.xLength, width=10)
xLength.configure(highlightthickness=0)
xLength.pack(side='left')
f1.pack(padx=4, pady=4, anchor='w')
f2 = Tkinter.Frame(self.frm, bg=colorBack)
lbl2 = Tkinter.Label(f2, text=_('Y Length:'), fg=colorFore, bg=colorBack, width=12, anchor='e')
lbl2.pack(side='left')
yLength = Tkinter.Entry(f2, fg=colorFore, bg=colorBack, textvariable=self.yLength, width=10)
yLength.configure(highlightthickness=0)
yLength.pack(side='left')
f2.pack(padx=4, pady=4, anchor='w')
self.xLength.set(getPrefs(PREF, 'SINGLE_CUT', 'X length', 0, float))
self.yLength.set(getPrefs(PREF, 'SINGLE_CUT', 'Y length', 0, float))
return _('Load'), _('Cancel')
def popup_entry(self, func, msg, optional):
label = Tkinter.Label(self.frm, text=msg, fg=colorFore, bg=colorBack)
if msg[:3] == '1. ':
label.configure(justify='left')
label.pack(padx=4, pady=4)
if func in ['entry', 'touch']:
self.entry = Tkinter.Entry(self.frm, justify='right', fg=colorFore, bg=colorBack)
self.entry.configure(highlightthickness=0, selectforeground=colorBack, selectbackground=colorFore)
if optional is not None:
self.entry.insert('end', optional)
self.entry.pack(padx=4, pady=4)
self.entry.focus_set()
if func == 'touch':
self.entry.insert('end', '0.0')
opl = Tkinter.Label(self.frm, text=_('Coordinate System'), fg=colorFore, bg=colorBack)
opl.pack(padx=4, pady=4)
self.c = c = StringVar(t)
c.set(optional)
self.opt = Tkinter.OptionMenu(self.frm, c, *all_systems[:])
self.opt.configure(fg=colorFore, bg=colorBack, activebackground=colorBack, highlightthickness=0)
self.opt.children['menu'].configure(fg=colorFore, bg=colorBack, activeforeground=colorBack, activebackground=colorFore)
self.opt.pack(padx=4, pady=4)
if func in ['info', 'error', 'warn']:
return _('OK'), None
elif func in ['yesno']:
return _('Yes'), _('No')
elif func in ['offsets']:
return _('Laser'), _('Scribe'), _('Offset Probe'), _('Cancel')
elif func in ['entry', 'touch']:
return _('OK'), _('Cancel')
def popup_complete(self, value, func):
if func == 'rfl_type':
self.reply = value, self.rflType.get()
elif func == 'rfl':
self.reply = value, self.leadIn.get(), float(self.leadLength.get()), float(self.leadAngle.get())
elif func == 'sc':
self.reply = value, self.xLength.get(), self.yLength.get()
elif func in ['entry']:
self.reply = value, self.entry.get()
elif func in ['touch']:
self.reply = value, self.entry.get(), self.c.get()
else:
self.reply = value
self.destroy()
##############################################################################
# PREFERENCE FUNCTIONS #
##############################################################################
def getPrefs(prefs, section, option, default=False, typ=bool):
m = prefs.types.get(typ)
if prefs.has_section(section):
if prefs.has_option(section, option):
try:
return m(prefs, section, option)
except Exception as err:
msg0 = f"[{section}]{option} is invalid, default of {typ(default)} applied"
notifications.add('error', f"{msg0}\n")
prefs.set(section, option, str(typ(default)))
prefs.write(open(prefs.fn, 'w'))
return typ(default)
else:
prefs.set(section, option, str(typ(default)))
prefs.write(open(prefs.fn, 'w'))
print(f"Preferences file, adding {option} to [{section}] section")
return typ(default)
else:
prefs.add_section(section)
prefs.set(section, option, str(typ(default)))
prefs.write(open(prefs.fn, 'w'))
print(f"Preferences file, adding [{section}] section")
print(f"Preferences file, adding {option} to [{section}] section")
return typ(default)
def putPrefs(prefs, section, option, value, typ=bool):
if prefs.has_section(section):
prefs.set(section, option, str(typ(value)))
prefs.write(open(prefs.fn, 'w'))
else:
prefs.add_section(section)
prefs.set(section.upper(), option, str(typ(value)))
prefs.write(open(prefs.fn, 'w'))
def removePrefsSect(prefs, section):
prefs.remove_section(section)
prefs.write(open(prefs.fn, 'w'))
def sortPrefs(prefs):
prefs._sections = OrderedDict(sorted(prefs._sections.items(), key=lambda t: int(t[0].rsplit('_',1)[1]) ))
prefs.write(open(prefs.fn, 'w'))
##############################################################################
# WINDOW FUNCTIONS #
##############################################################################
def mode_changed():
if pVars.plasmacMode.get() == 2:
rE(f"grid forget {foverride}")
rE(f"grid forget {fplasma}.arcvl")
rE(f"grid forget {fplasma}.arc-voltage")
rE(f"grid forget {fleds}.led-void-locked")
rE(f"grid forget {fleds}.led-void-lockedL")
rE(f"grid forget {fruns}.check.av")
rE(f"grid forget {fruns}.check.avL")
rE(f"grid forget {fparam}.c2.thc.thc-auto")
rE(f"grid forget {fparam}.c2.thc.thc-autoL")
rE(f"grid forget {fparam}.c2.thc.thc-delay")
rE(f"grid forget {fparam}.c2.thc.thc-delayL")
rE(f"grid forget {fparam}.c2.thc.thc-sample-counts")
rE(f"grid forget {fparam}.c2.thc.thc-sample-countsL")
rE(f"grid forget {fparam}.c2.thc.thc-sample-threshold")
rE(f"grid forget {fparam}.c2.thc.thc-sample-thresholdL")
rE(f"grid forget {fparam}.c2.thc.thc-threshold")
rE(f"grid forget {fparam}.c2.thc.thc-thresholdL")
rE(f"grid forget {fparam}.c2.thc.pid-i-gain")
rE(f"grid forget {fparam}.c2.thc.pid-i-gainL")
rE(f"grid forget {fparam}.c2.thc.pid-d-gain")
rE(f"grid forget {fparam}.c2.thc.pid-d-gainL")
rE(f"grid forget {fparam}.c2.thc.voidlock-slope")
rE(f"grid forget {fparam}.c2.thc.voidlock-slopeL")
rE(f"grid forget {fparam}.c3.arc.arc-voltage-scale")
rE(f"grid forget {fparam}.c3.arc.arc-voltage-scaleL")
rE(f"grid forget {fparam}.c3.arc.arc-voltage-offset")
rE(f"grid forget {fparam}.c3.arc.arc-voltage-offsetL")
rE(f"grid forget {fparam}.c3.arc.arc-ok-high")
rE(f"grid forget {fparam}.c3.arc.arc-ok-highL")
rE(f"grid forget {fparam}.c3.arc.arc-ok-low")
rE(f"grid forget {fparam}.c3.arc.arc-ok-lowL")
elif pVars.plasmacMode.get() == 1:
rE(f"grid forget {fparam}.c3.arc.arc-ok-high")
rE(f"grid forget {fparam}.c3.arc.arc-ok-highL")
rE(f"grid forget {fparam}.c3.arc.arc-ok-low")
rE(f"grid forget {fparam}.c3.arc.arc-ok-lowL")
else:
rE(f"grid {fparam}.c3.arc.arc-ok-highL -column 0 -row 5 -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fparam}.c3.arc.arc-ok-high -column 1 -row 5 -sticky e -padx {{0 4}} -pady {{4 0}}")
rE(f"grid {fparam}.c3.arc.arc-ok-lowL -column 0 -row 6 -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fparam}.c3.arc.arc-ok-low -column 1 -row 6 -sticky e -padx {{0 4}} -pady {{4 0}}")
if pVars.plasmacMode.get() < 2:
rE(f"grid {foverride} -column 0 -row 3 -padx 2 -pady {{0 4}} -sticky w")
rE(f"grid {fplasma}.arcvl -column 0 -row 0 -sticky nw")
rE(f"grid {fplasma}.arc-voltage -column 0 -row 1 -sticky se -rowspan 2")
rE(f"grid {fleds}.led-void-locked -column 4 -row 3 -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fleds}.led-void-lockedL -column 5 -row 3 -padx {{0 0}} -pady {{4 0}} -sticky W")
rE(f"grid {fruns}.check.av -column 0 -row 0 -sticky w")
rE(f"grid {fruns}.check.avL -column 1 -row 0 -sticky w")
rE(f"grid {fparam}.c2.thc.thc-autoL -column 0 -row 0 -sticky e")
rE(f"grid {fparam}.c2.thc.thc-auto -column 1 -row 0 -sticky e -padx {{0 4}}")
if pVars.thcAuto.get():
rE(f"grid forget {fparam}.c2.thc.thc-delay")
rE(f"grid forget {fparam}.c2.thc.thc-delayL")
rE(f"grid {fparam}.c2.thc.thc-sample-countsL -column 0 -row 2 -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fparam}.c2.thc.thc-sample-counts -column 1 -row 2 -sticky e -padx {{0 4}} -pady {{4 0}}")
rE(f"grid {fparam}.c2.thc.thc-sample-thresholdL -column 0 -row 3 -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fparam}.c2.thc.thc-sample-threshold -column 1 -row 3 -sticky e -padx {{0 4}} -pady {{4 0}}")
else:
rE(f"grid forget {fparam}.c2.thc.thc-sample-counts")
rE(f"grid forget {fparam}.c2.thc.thc-sample-countsL")
rE(f"grid forget {fparam}.c2.thc.thc-sample-threshold")
rE(f"grid forget {fparam}.c2.thc.thc-sample-thresholdL")
rE(f"grid {fparam}.c2.thc.thc-delayL -column 0 -row 1 -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fparam}.c2.thc.thc-delay -column 1 -row 1 -sticky e -padx {{0 4}} -pady {{4 0}}")
rE(f"grid {fparam}.c2.thc.thc-thresholdL -column 0 -row 4 -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fparam}.c2.thc.thc-threshold -column 1 -row 4 -sticky e -padx {{0 4}} -pady {{4 0}}")
rE(f"grid {fparam}.c2.thc.pid-i-gainL -column 0 -row 6 -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fparam}.c2.thc.pid-i-gain -column 1 -row 6 -sticky e -padx {{0 4}} -pady {{4 0}}")
rE(f"grid {fparam}.c2.thc.pid-d-gainL -column 0 -row 7 -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fparam}.c2.thc.pid-d-gain -column 1 -row 7 -sticky e -padx {{0 4}} -pady {{4 0}}")
rE(f"grid {fparam}.c2.thc.voidlock-slopeL -column 0 -row 9 -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fparam}.c2.thc.voidlock-slope -column 1 -row 9 -sticky e -padx {{0 4}} -pady {{4 0}}")
rE(f"grid {fparam}.c3.arc.arc-voltage-scaleL -column 0 -row 3 -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fparam}.c3.arc.arc-voltage-scale -column 1 -row 3 -sticky e -padx {{0 4}} -pady {{4 0}}")
rE(f"grid {fparam}.c3.arc.arc-voltage-offsetL -column 0 -row 4 -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fparam}.c3.arc.arc-voltage-offset -column 1 -row 4 -sticky e -padx {{0 4}} -pady {{4 0}}")
hal.set_p('plasmac.mode', str(pVars.plasmacMode.get()))
def cone_size_changed():
o.set_cone_basesize(float(rE(f"{fsetup}.l.gui.cone get")))
def font_changed(value=0):
if value:
populate_gcode_fonts()
if 'mono' not in pVars.codeFont.get().lower():
pVars.codeFont.set(pVars.guiFont.get())
global lastFontSize
rE(f"font configure TkDefaultFont -family {{{pVars.guiFont.get()}}} -size {pVars.fontSize.get()}")
rE(f"font configure fontGui -family {{{pVars.guiFont.get()}}} -size {pVars.fontSize.get()}")
rE(f"font configure fontArc -family {{{pVars.guiFont.get()}}} -size {int(pVars.fontSize.get())*3}")
rE(f"font configure fontCode -family {{{pVars.codeFont.get()}}} -size {pVars.fontSize.get()}")
rE(f".info.gcodef.gcodes configure -font fontGui")
rE(f"{fplasma}.arc-voltage configure -font fontArc")
rE(f".pane.bottom.t.text configure -font fontCode")
if s.paused:
txt = _('Cut Recovery')
rE(f"{ftabs} itemconfigure cutrecs -text {txt}")
else:
rE(f"{ftabs} delete manual 0")
rE(f"{ftabs} delete mdi 0")
rE(f"{ftabs} insert end manual")
rE(f"{ftabs} insert end mdi")
rE(f"{ftabs} raise manual")
txt = _('Manual')
rE(f"{ftabs} itemconfigure manual -text {txt}")
txt = _('MDI')
rE(f"{ftabs} itemconfigure mdi -text {txt}")
rE(f"{fright} delete preview 0")
rE(f"{fright} delete numbers 0")
rE(f"{fright} delete stats 0")
rE(f"{fright} insert end preview")
rE(f"{fright} insert end numbers")
rE(f"{fright} insert end stats")
rE(f"{fright} raise preview")
txt = _('Preview')
rE(f"{fright} itemconfigure preview -text {txt}")
txt = _('DRO')
rE(f"{fright} itemconfigure numbers -text {txt}")
txt = _('Statistics')
rE(f"{fright} itemconfigure stats -text {txt}")
for nbook in [ftabs, fright]:
pages = rE(f"{nbook} pages").split()
for page in pages:
rE(f"{nbook} itemconfigure {page} -foreground {colorFore} -background {colorBack}")
rE(f"{fright}.fpreview configure -bd 2")
rE(f"{fright}.fnumbers configure -bd 2")
rE(f"{fright}.fstats configure -bd 2")
for box in spinBoxes:
rE(f"{box} configure -font fontGui")
for box in rSpinBoxes:
rE(f"{box} configure -font fontGui")
ledFrame = int(pVars.fontSize.get()) * 2
ledScale = int(pVars.fontSize.get()) / int(lastFontSize)
lastFontSize = pVars.fontSize.get()
for led in wLeds:
rE(f"{led} configure -width {ledFrame} -height {ledFrame}")
rE(f"{led} scale 1 1 1 {ledScale} {ledScale}")
bSize = int(int(pVars.fontSize.get()) / 10 * 24) if int(pVars.fontSize.get()) > 10 else 24
for w in toolButtons:
rE(f".toolbar.{w} configure -width {bSize} -height {bSize}")
for w in matButtons:
rE(f"{toolmat}.{w} configure -width {bSize} -height {bSize}")
get_coordinate_font(None)
set_window_size()
# set combobox list widths
longest = max(fontsGui, key=len)
l = Label(bd=0, padx=0, pady=0, highlightthickness=0, font='fontGui', text=longest)
ll = int(rE(f"winfo reqwidth {l}")) + 20
rE(f"{fsetup}.l.gui.fgui configure -listboxwidth {ll}")
longest = max(fontsCode, key=len)
l = Label(bd=0, padx=0, pady=0, highlightthickness=0, font='fontCode', text=longest)
ll = int(rE(f"winfo reqwidth {l}")) + 20
rE(f"{fsetup}.l.gui.fcode configure -listboxwidth {ll}")
material_list_width()
def material_list_width():
if rE(f"{fruns}.material.materials cget -values"):
longest = max(tuple_from_tcl_string(matNames), key=len)
l = Label(bd=0, padx=0, pady=0, highlightthickness=0, font='fontGui', text=longest)
ll = int(rE(f"winfo reqwidth {l}")) + 20
rE(f"{fruns}.material.materials configure -listboxwidth {ll}")
def populate_gcode_fonts():
for font in sorted(fontsCode):
rE(f"{fsetup}.l.gui.fcode insert end {{{font}}}")
def tuple_from_tcl_string(str):
multi = False
tmp = ''
tup = ()
for char in str:
if char == '{':
multi = True
elif char == '}':
if tmp and tmp not in tup:
tup += (tmp,)
tmp = ''
multi = False
elif multi:
tmp += char
elif char == ' ':
if tmp and tmp not in tup:
tup += (tmp,)
tmp = ''
else:
tmp += char
if tmp and tmp not in tup:
tup += (tmp,)
return(tup)
def close_window():
rE(f"focus {fbuttons}.torch-enable")
vkb_hide()
if pVars.closeDialog.get():
msgs = ''
if pVars.closeText.get():
customText = getPrefs(PREF, 'GUI_OPTIONS', 'Exit warning text', '', str).split('\\')
if customText:
for t in customText:
msgs += f"{t}\n\n"
text2 = _('Do you really want to close LinuxCNC ?')
msgs += text2
if not plasmacPopUp('yesno', _('CONFIRM CLOSE'), msgs).reply:
return
putPrefs(PREF, 'GUI_OPTIONS', 'Window last', rE(f"winfo geometry {root_window}").split('+', 1)[0], str)
if hal.component_exists('pmx485'):
hal.set_p('pmx485.enable', '0')
root_window.destroy()
def set_window_size(winSize=False):
global wSize, bPane
winSize = rE(f"winfo geometry {root_window}").split('+', 1)[0] if winSize == 'combo' else winSize
size = pVars.winSize.get()
if size not in ['default', 'last', 'fullscreen', 'maximized']:
msg0 = _('Invalid parameters in [GUI_OPTIONS]Window size in preferences file')
notifications.add('error', f"{msg0}\n")
return False
else:
if pVars.orient.get() == 'portrait':
# window height, window height with pmx enabled, window width, bottom pane height
wSize = { '7': [ 720, 756, 644, 92], '8': [ 7588, 796, 644, 102],
'9': [ 814, 856, 662, 108], '10': [ 880, 924, 664, 116],
'11': [ 918, 966, 734, 124], '12': [ 962, 1012, 806, 132],
'13': [1058, 1114, 878, 140], '14': [1102, 1158, 950, 148],
'15': [1146, 1206,1024, 156], '16': [1214, 1278,1094, 164],
'17': [1270, 1336,1166, 172], '18': [1334, 1404,1166, 180],
'19': [1366, 1438,1240, 188], '20': [1442, 1514,1310, 196],
}
offset = 1 if pmPort else 0
height = wSize[pVars.fontSize.get()][offset]
width = wSize[pVars.fontSize.get()][2]
bPane = wSize[pVars.fontSize.get()][3]
else:
# window height, bottom pane height
wSize = { '7': [456, 92], '8': [482, 102],
'9': [516, 108], '10': [562, 116],
'11': [586, 124], '12': [612, 132],
'13': [686, 140], '14': [710, 148],
'15': [736, 156], '16': [784, 164],
'17': [811, 172], '18': [858, 180],
'19': [882, 188], '20': [932, 196],
}
height = wSize[pVars.fontSize.get()][0]
width = int(height * 1.75)
bPane = wSize[pVars.fontSize.get()][1]
rE(f".pane paneconfigure $pane_top -minsize {bPane}")
rE(f".pane paneconfigure $pane_bottom -minsize {bPane}")
rE(f".pane paneconfigure $pane_bottom -height {bPane}")
if size == 'maximized':
root_window.attributes('-fullscreen', False, '-zoomed', True)
elif size == 'fullscreen':
root_window.attributes('-fullscreen', True, '-zoomed', False)
else:
root_window.attributes('-fullscreen', False, '-zoomed', False)
last = winSize if winSize else restoreSetup['winLast']
if size == 'last' and last != 'none':
width = int(last.split('x')[0])
height = int(last.split('x')[1])
xPos = int((root_window.maxsize()[0] - width) / 2)
yPos = int((root_window.maxsize()[1] - height) / 2)
root_window.geometry(f"{width}x{height}+{xPos}+{yPos}")
rE(f"{fsetup}.r.ubuttons.canvas xview moveto 0.0")
rE(f"{fsetup}.r.ubuttons.canvas yview moveto 0.0")
root_window.update_idletasks()
def wcs_rotation(mode):
global currentX, currentY, currentRot
if mode == 'get':
currentX = s.g5x_offset[0]
currentY = s.g5x_offset[1]
currentRot = s.rotation_xy
elif mode == 'set':
ensure_mode(linuxcnc.MODE_MDI)
c.mdi(f"G10 L2 P0 X{currentX} Y{currentY} R{currentRot}")
c.wait_complete()
if currentRot != s.rotation_xy:
reload_file()
def preview_toggle(conv=False):
if pVars.previewLarge.get() or conv:
rE('grid forget .pane.top.tabs')
rE(f"grid forget {ftop}.feedoverride")
rE(f"grid forget {ftop}.rapidoverride")
rE(f"grid forget {ftop}.jogspeed")
rE(f"grid forget {ftop}.ajogspeed")
rE('.pane paneconfigure $pane_bottom -minsize 1')
rE('.pane paneconfigure $pane_bottom -height 1')
if pVars.orient.get() == 'portrait':
rE(f"grid forget {fportrait}")
else:
rE(f"grid forget {fbuttons}")
rE(f"grid forget {toolmat}")
rE(f"grid forget {fruns}")
else:
rE('grid .pane.top.tabs -column 0 -row 1 -sticky nesw -padx 2 -pady 2')
rE('grid .pane.top.feedoverride -column 0 -row 2 -sticky new')
rE('grid .pane.top.rapidoverride -column 0 -row 3 -sticky new')
rE('grid .pane.top.jogspeed -column 0 -row 5 -sticky new')
# test if we need the angular jog slider (56 = 0x38 = 000111000 = ABC)
if s.axis_mask & 56 == 0 and 'ANGULAR' in joint_type:
rE('grid .pane.top.ajogspeed -column 0 -row 6 -sticky new')
rE(f".pane paneconfigure $pane_top -minsize {bPane}")
rE(f".pane paneconfigure $pane_bottom -minsize {bPane}")
rE(f".pane paneconfigure $pane_bottom -height {bPane}")
populate_run_panel()
def setup_toggle(state):
if int( state):
hide_default()
enable_menus(False)
rE('grid .toolsetup -column 0 -row 0 -columnspan 3 -sticky nesw')
rE(f"grid {fsetup} -column 1 -row 1 -rowspan 2 -sticky nsew")
keyboard_bindings(False)
vkbData['required'] = True
vkb_show('keyboard')
else:
rE('grid forget .toolsetup')
rE(f"grid forget {fsetup}")
show_default()
def param_toggle(state):
if int(state):
hide_default()
enable_menus(False)
rE('grid .toolparam -column 0 -row 0 -columnspan 3 -sticky nesw')
rE(f"grid {fparam} -column 1 -row 1 -rowspan 2 -sticky nsew")
keyboard_bindings(False)
vkbData['required'] = True
vkb_show('numpad')
else:
rE('grid forget .toolparam')
rE(f"grid forget {fparam}")
show_default()
def conv_toggle(state, convSent=False):
global CONV, convFirstRun, preConvFile, loaded_file, lastViewType
if int(state):
lastViewType = get_view_type()
preview_toggle(True)
enable_menus(False)
if pVars.orient.get() == 'portrait':
rE('grid .toolconv -column 1 -row 2 -columnspan 1 -sticky nesw')
rE('grid .fconv -column 1 -row 3 -rowspan 1 -sticky nsew')
else:
rE('grid .toolconv -column 0 -row 0 -columnspan 3 -sticky nesw')
rE('grid .fconv -column 0 -row 1 -rowspan 2 -sticky nsew')
rE(f"{fright} itemconfigure numbers -state disabled")
for page in rE(f"{fright} pages").split():
if page != 'preview':
rE(f"{fright} itemconfigure {page} -state disabled")
rE(f"{fright} raise preview")
matIndex = rE(f"{fruns}.material.materials getvalue")
if comp['development']:
reload(conversational)
if convFirstRun or comp['development']:
CONV = conversational.Conv(convFirstRun, root_window, widgets.toolFrame, \
widgets.convFrame, bwidget.ComboBox, imagePath, tmpPath, pVars, \
unitsPerMm, comp, PREF, getPrefs, putPrefs, open_file_guts, \
wcs_rotation, conv_toggle, color_change, plasmacPopUp, o)
convFirstRun = False
# set_conv_preview()
if loaded_file:
COPY(loaded_file, preConvFile)
CONV.start(materialFileDict, matIndex, vars.taskfile.get(), s.g5x_index, commands.set_view_z)
set_conv_preview()
keyboard_bindings(False)
vkbData['required'] = True
vkb_show('numpad')
else:
preview_toggle()
rE('grid forget .toolconv')
rE('grid forget .fconv')
show_default()
for page in rE(f"{fright} pages").split():
rE(f"{fright} itemconfigure {page} -state normal")
reset_conv_preview()
if convSent:
loaded_file = vars.taskfile.get()
else:
if loaded_file:
if 'shape.ngc' in loaded_file:
COPY(preConvFile, loaded_file)
open_file_guts(loaded_file, True, False)
commands.set_view_z()
else:
clear_program()
root_window.update_idletasks()
if lastViewType == 'p':
commands.set_view_p()
elif lastViewType == 't':
commands.set_view_t()
else:
commands.set_view_z()
CONV.oldConvButton = 'line'
def clear_program():
file = os.path.join(tmpPath, 'clear.ngc')
with open(file, 'w') as outFile:
outFile.write('m2')
open_file_guts(file, True, False)
t.configure(state='normal')
t.delete(0.0, 'end')
t.configure(state='disabled')
def set_conv_preview():
''' configure the preview for conversational '''
global convViewOptions, joint_dro_format_old , dro_format_old
convViewOptions['alpha'] = vars.program_alpha.get()
convViewOptions['dtg'] = vars.show_distance_to_go.get()
convViewOptions['extents'] = vars.show_extents.get()
convViewOptions['limits'] = vars.show_machine_limits.get()
convViewOptions['offsets'] = vars.show_offsets.get()
convViewOptions['plot'] = vars.show_live_plot.get()
convViewOptions['rapids'] = vars.show_rapids.get()
convViewOptions['speed'] = vars.show_machine_speed.get()
convViewOptions['tool'] = vars.show_tool.get()
convViewOptions['cone'] = o.cone_basesize
convViewOptions['origin'] = o.show_small_origin
vars.program_alpha.set(False)
vars.show_distance_to_go.set(False)
vars.show_extents.set(True)
vars.show_machine_limits.set(False)
vars.show_offsets.set(False)
vars.show_live_plot.set(False)
vars.show_rapids.set(True)
vars.show_machine_speed.set(False)
vars.show_tool.set(False)
o.cone_basesize = .025
o.show_small_origin = False
o.hide_icons = True
o.set_view_z()
joint_dro_format_old = o.joint_dro_format
o.joint_dro_format = joint_dro_format_new
dro_format_old = o.dro_format
o.dro_format = dro_format_new
def reset_conv_preview():
''' configure the preview for gcode file '''
global convViewOptions
vars.program_alpha.set(convViewOptions['alpha'])
vars.show_distance_to_go.set(convViewOptions['dtg'])
vars.show_extents.set(convViewOptions['extents'])
vars.show_machine_limits.set(convViewOptions['limits'])
vars.show_offsets.set(convViewOptions['offsets'])
vars.show_live_plot.set(convViewOptions['plot'])
vars.show_rapids.set(convViewOptions['rapids'])
vars.show_machine_speed.set(convViewOptions['speed'])
vars.show_tool.set(convViewOptions['tool'])
o.cone_basesize = convViewOptions['cone']
o.show_small_origin = convViewOptions['origin']
o.joint_dro_format = joint_dro_format_old
o.dro_format = dro_format_old
o.hide_icons = False
def joint_dro_format_new(s,spd,num_of_joints,limit, homed):
''' replace dro with selected material '''
text = conv_material_text()
return limit, homed, text, text
def dro_format_new(s,spd,dtg,limit,homed,positions,axisdtg,g5x_offset,g92_offset,tlo_offset):
''' replace dro with selected material '''
text = conv_material_text()
return limit, homed, text, text
def conv_material_text():
''' return a list of material parameters to display '''
res = 3 if s.linear_units != 1 else 2
text = f"Cut Speed={materialFileDict[int(CONV.matCombo.get().split(':')[0])]['cut_speed']:.0f}"
text += f" Cut Height={materialFileDict[int(CONV.matCombo.get().split(':')[0])]['cut_height']:.{res}f}"
text += f" Kerf Width={materialFileDict[int(CONV.matCombo.get().split(':')[0])]['kerf_width']:.{res}f}"
return [text]
def hide_default():
rE('grid forget .pane')
rE('grid forget .fconv')
rE('grid forget .toolbar')
if pVars.orient.get() == 'portrait':
rE(f"grid forget {fportrait}")
else:
rE(f"grid forget {fbuttons}")
rE(f"grid forget {toolmat}")
rE(f"grid forget {fruns}")
def show_default():
rE('grid .toolbar -column 0 -row 0 -columnspan 3 -sticky nesw')
rE('grid .pane -column 1 -row 1 -rowspan 2 -sticky nsew')
rE(f".pane paneconfigure $pane_bottom -height {bPane}")
if not pVars.previewLarge.get():
populate_run_panel()
enable_menus(True)
keyboard_bindings(pVars.kbShortcuts.get())
vkbData['required'] = False
rE('focus .')
def enable_menus(state):
state = 'normal' if state else 'disabled'
menus = ['File', 'Machine', 'View', 'Conversational', 'Parameters', 'Setup', 'Help']
for menu in menus:
rE(f".menu entryconfig {menu} -state {state}")
##############################################################################
# TABLE VIEW FUNCTIONS #
##############################################################################
def set_view_t(event=None, scale=None):
# set button states
widgets.view_z.configure(relief='link')
widgets.view_z2.configure(relief='link')
widgets.view_x.configure(relief='link')
widgets.view_y.configure(relief='link')
widgets.view_y2.configure(relief='link')
widgets.view_t.configure(relief='sunken')
widgets.view_p.configure(relief='link')
vars.view_type.set(5)
# set view
mult = 1 if s.linear_units != 1 else 25.4
zoomScale = scale * 2 if scale else float(rE(f"{fsetup}.l.gui.zoom get")) * 2
xTableLength = machineBounds['xLen'] / mult / zoomScale
yTableLength = machineBounds['yLen'] / mult / zoomScale
xTableCenter = (machineBounds['X-'] + (machineBounds['xLen'] / 2)) / mult
yTableCenter = (machineBounds['Y-'] + (machineBounds['yLen'] / 2)) / mult
o.reset()
o.translate_modelview(-xTableCenter, -yTableCenter, 0)
o.set_eyepoint_from_extents(xTableLength, yTableLength)
o.perspective = False
o.lat = o.lon = 0
glRotateScene(o, 1.0, xTableCenter, yTableCenter, 0, 0, 0, 0, 0)
o._redraw()
def get_view_type():
if str(widgets.view_x['relief']) == 'sunken':
view = 'x'
elif (str(widgets.view_y['relief']) == 'sunken' or
str(widgets.view_y2['relief']) == 'sunken'):
view = 'y'
elif (str(widgets.view_z['relief']) == 'sunken' or
str(widgets.view_z2['relief']) == 'sunken'):
view = 'z'
elif (str(widgets.view_t['relief']) == 'sunken'):
view = 't'
else:
view = 'p'
return view
##############################################################################
# STATISTICS FUNCTIONS #
##############################################################################
def secs_to_hms(s):
m, s = divmod(int(s), 60)
h, m = divmod(m, 60)
return f"{h:02.0f}:{m:02.0f}:{s:02.0f}"
def hms_to_secs(hms):
h, m, s = hms.split(':')
return int(h)*3600 + int(m)*60 + int(s)
def save_total_stats():
if hal.get_value('plasmac.torch-enable'):
val = float(pVars.lengthT.get()[:len(pVars.lengthT.get()) - 1]) * statDivisor
pVars.lengthS.set(val)
putPrefs(PREF, 'STATISTICS', 'Cut length', val, float)
val = pVars.pierceT.get()
pVars.pierceS.set(val)
putPrefs(PREF, 'STATISTICS', 'Pierce count', val, int)
val = hms_to_secs(pVars.rapidT.get())
pVars.rapidS.set(val)
putPrefs(PREF, 'STATISTICS', 'Rapid time', val, int)
val = hms_to_secs(pVars.probeT.get())
pVars.probeS.set(val)
putPrefs(PREF, 'STATISTICS', 'Probe time', val, int)
val = hms_to_secs(pVars.torchT.get())
pVars.torchS.set(val)
putPrefs(PREF, 'STATISTICS', 'Torch on time', val, int)
val = hms_to_secs(pVars.cutT.get())
pVars.cutS.set(val)
putPrefs(PREF, 'STATISTICS', 'Cut time', val, int)
val = hms_to_secs(pVars.pausedT.get())
pVars.pausedS.set(val)
putPrefs(PREF, 'STATISTICS', 'Paused time', val, int)
val = hms_to_secs(pVars.runT.get())
pVars.runS.set(val)
putPrefs(PREF, 'STATISTICS', 'Program run time', val, int)
def clear_job_stats():
pVars.lengthJ.set(f"0.00{statSuffix}")
pVars.pierceJ.set(0)
pVars.rapidJ.set('00:00:00')
pVars.probeJ.set('00:00:00')
pVars.torchJ.set('00:00:00')
pVars.cutJ.set('00:00:00')
pVars.pausedJ.set('00:00:00')
pVars.runJ.set('00:00:00')
def reset_all_stats(stat):
if stat == 'length':
pVars.lengthS.set(0)
pVars.lengthJ.set(f"0.00{statSuffix}")
pVars.lengthT.set(f"0.00{statSuffix}")
putPrefs(PREF, 'STATISTICS', 'Cut length', 0, float)
elif stat == 'pierce':
pVars.pierceS.set(0)
pVars.pierceJ.set('0')
pVars.pierceT.set('0')
putPrefs(PREF, 'STATISTICS', 'Pierce count', 0, int)
elif stat == 'rapid':
pVars.rapidS.set(0)
pVars.rapidJ.set('00:00:00')
pVars.rapidT.set('00:00:00')
putPrefs(PREF, 'STATISTICS', 'Rapid time', 0, int)
elif stat == 'probe':
pVars.probeS.set(0)
pVars.probeJ.set('00:00:00')
pVars.probeT.set('00:00:00')
putPrefs(PREF, 'STATISTICS', 'Probe time', 0, int)
elif stat == 'torch':
pVars.torchS.set(0)
pVars.torchJ.set('00:00:00')
pVars.torchT.set('00:00:00')
putPrefs(PREF, 'STATISTICS', 'Torch on time', 0, int)
elif stat == 'cut':
pVars.cutS.set(0)
pVars.cutJ.set('00:00:00')
pVars.cutT.set('00:00:00')
putPrefs(PREF, 'STATISTICS', 'Cut time', 0, int)
elif stat == 'paused':
pVars.pausedS.set(0)
pVars.pausedJ.set('00:00:00')
pVars.pausedT.set('00:00:00')
putPrefs(PREF, 'STATISTICS', 'Paused time', 0, int)
elif stat == 'run':
pVars.runS.set(0)
pVars.runJ.set('00:00:00')
pVars.runT.set('00:00:00')
putPrefs(PREF, 'STATISTICS', 'Program run time', 0, int)
##############################################################################
# PARAMETER FUNCTIONS #
##############################################################################
# set appropriate hal pin if parameter changed
def param_changed(widget, value):
global widgetValues
item = widget.rsplit('.',1)[1]
if item in ['x-single-cut','y-single-cut']:
return
widgetValues[widget] = value
if item == 'cut-amps':
if pmx485['exists']:
pmx485_current_changed(int(value))
elif item == 'cut-mode':
if pmx485['exists']:
pmx485_mode_changed(int(value))
elif item == 'gas-pressure':
if pmx485['exists']:
pmx485_pressure_changed(float(value))
elif item == 'cut-speed':
hal.set_p('plasmac.cut-feed-rate',f"{value}")
elif item[1:] == '-pierce-offset':
if extHalPins:
comp[item] = value
else:
hal.set_p(f"plasmac.{item}",f"{value}")
def validate_spinbox(widget, spinType, resolution, value, original):
dbPrint = True if (not firstRun and comp['development']) else False
if dbPrint: print(f"\nvalidating {widget}")
if value == '':
if dbPrint: print('blank = still valid')
return True
minval = float(rE(f"{widget} cget -from")) if spinType == 'flt' else int(float(rE(f"{widget} cget -from")))
maxval = float(rE(f"{widget} cget -to")) if spinType == 'flt' else int(float(rE(f"{widget} cget -to")))
# a negative value if range min equals zero is invalid
if value == '-' and minval >= 0:
if dbPrint: print('cannot be less than zero = invalid')
return False
# a decimal point in an int is invalid
if '.' in str(value) and spinType == 'int':
if dbPrint: print('a decimal in an int = invalid')
return False
# more digits than the resolution of a float is invalid
if '.' in str(value) and len(str(value).split('.')[1]) > int(resolution):
# don't error on first run so we can import parameters from qtplasmac
if dbPrint: print('float resolution too large = invalid')
if not firstRun:
return False
# we cannot do math on "-", ".", or "-." but they are valid for a float
if value == '.' or value == '-' or value == '-.':
if dbPrint: print('illegal math = still valid')
return True
# if a float cannot be calculated from value then it is invalid
# we won't change the hal pin yet
try:
v = float(value)
except:
if dbPrint: print('cannot calculate a float = invalid')
return False
# if value is too large it is invalid
if v > maxval:
if dbPrint: print('value out of bounds = invalid')
return False
# if value is too small it may be valid
if v < minval or v > maxval:
if dbPrint: print('value out of bounds = still valid')
return True
# must be a good number...
if widget == f"{fsetup}.l.gui.zoom":
commands.set_view_t(None, v)
elif widget == f"{fsetup}.l.gui.jogspeed":
jog_default_changed(value)
elif widget == f"{fsetup}.l.gui.crspeed":
cut_rec_default_changed(value)
else:
param_changed(widget,value)
if dbPrint: print('valid entry')
return True
def load_param_clicked():
for widget in cpList:
try:
# bring in all parameters as floats to allow reading QtPlasmaC parameters
value = getPrefs(PREF, 'PLASMA_PARAMETERS', str(widget[8]), widget[3], float)
except:
value = 0
title = _('PARAMETER ERROR')
msg0 = _('Invalid parameter for')
plasmacPopUp('error', title, f"{msg0}: {widget[8]}")
continue
# convert to int here if required
value = value if widget[2] > 0 else int(value)
rE(f"{widget[0]}.{widget[1]} set {value}")
widgetValues[f"{widget[0]}.{widget[1]}"] = value
value = getPrefs(PREF, 'ENABLE_OPTIONS', 'THC auto', False, bool)
pVars.thcAuto.set(value)
mode_changed()
def save_param_clicked():
for widget in cpList:
value = rE(f"{widget[0]}.{widget[1]} get")
if widget[2] > 0:
putPrefs(PREF, 'PLASMA_PARAMETERS', str(widget[8]), value, float)
else:
putPrefs(PREF, 'PLASMA_PARAMETERS', str(widget[8]), value, int)
value = pVars.thcAuto.get()
putPrefs(PREF, 'ENABLE_OPTIONS', 'THC auto', value, bool)
def load_setup_clicked():
fontChanged = False
rE(f"{fsetup}.l.gui.jogspeed set {restoreSetup['jogSpeed']}")
jog_default_changed(restoreSetup['jogSpeed'])
if pVars.plasmacMode.get() != restoreSetup['plasmacMode']:
pVars.plasmacMode.set(restoreSetup['plasmacMode'])
mode_changed()
if pVars.winSize.get() != restoreSetup['winSize'] or \
rE(f"winfo geometry {root_window}").split('+', 1)[0] != restoreSetup['winLast']:
pVars.winSize.set(restoreSetup['winSize'])
set_window_size(restoreSetup['winLast'])
if pVars.orient.get() != restoreSetup['orient']:
pVars.orient.set(restoreSetup['orient'])
set_orientation()
if pVars.fontSize.get() != restoreSetup['fontSize']:
pVars.fontSize.set(restoreSetup['fontSize'])
fontChanged = True
if pVars.guiFont.get() != restoreSetup['guiFont']:
pVars.guiFont.set(restoreSetup['guiFont'])
fontChanged = True
if pVars.codeFont.get() != restoreSetup['codeFont']:
pVars.codeFont.set(restoreSetup['codeFont'])
fontChanged = True
if fontChanged:
font_changed()
if pVars.coneSize.get() != restoreSetup['coneSize']:
pVars.coneSize.set(restoreSetup['coneSize'])
cone_size_changed()
if pVars.popLocation.get() != restoreSetup['popLocation']:
pVars.popLocation.set(restoreSetup['popLocation'])
if pVars.matDefault.get() != restoreSetup['matDefault']:
pVars.matDefault.set(restoreSetup['matDefault'])
display_selected_material(restoreSetup['matDefault'])
if pVars.useVirtKB.get() != restoreSetup['useVirtKB']:
pVars.useVirtKB.set(restoreSetup['useVirtKB'])
virtual_kb_changed()
if pVars.useVirtKB.get():
pVars.kbShortcuts.set(0)
keyboard_bindings(0)
else:
if pVars.kbShortcuts.get() != restoreSetup['kbShortcuts']:
pVars.kbShortcuts.set(restoreSetup['kbShortcuts'])
keyboard_bindings(restoreSetup['kbShortcuts'])
if int(rE(f"{fsetup}.l.gui.crspeed get")) == restoreSetup['crPercent']:
rE(f"{fcrspeed}.display.cut-rec-speed set {restoreSetup['crPercent']}")
else:
rE(f"{fsetup}.l.gui.crspeed set {restoreSetup['crPercent']}")
pVars.closeDialog.set(restoreSetup['closeDialog'])
pVars.closeText.set(restoreSetup['closeText'])
rE(f"{fsetup}.l.gui.zoom set {restoreSetup['tableZoom']}")
if not (isPaused or isRunning):
user_button_load()
read_colors()
color_change()
def save_setup_clicked():
if int(rE(f"{fsetup}.l.gui.jogspeed get")) < minJogSpeed:
rE(f"{fsetup}.l.gui.jogspeed set {minJogSpeed}")
restoreSetup['jogSpeed'] = rE(f"{fsetup}.l.gui.jogspeed get")
putPrefs(PREF, 'GUI_OPTIONS', 'Jog speed', restoreSetup['jogSpeed'], int)
restoreSetup['plasmacMode'] = pVars.plasmacMode.get()
putPrefs(PREF, 'GUI_OPTIONS', 'Mode', restoreSetup['plasmacMode'], int)
restoreSetup['closeText'] = pVars.closeText.get()
putPrefs(PREF, 'GUI_OPTIONS', 'Exit warning text', restoreSetup['closeText'], str)
restoreSetup['closeDialog'] = pVars.closeDialog.get()
putPrefs(PREF, 'GUI_OPTIONS', 'Exit warning', restoreSetup['closeDialog'], bool)
restoreSetup['winSize'] = pVars.winSize.get()
putPrefs(PREF, 'GUI_OPTIONS', 'Window size', restoreSetup['winSize'], str)
restoreSetup['winLast'] = rE(f"winfo geometry {root_window}").split('+', 1)[0]
putPrefs(PREF, 'GUI_OPTIONS', 'Window last', restoreSetup['winLast'], str)
restoreSetup['orient'] = pVars.orient.get()
putPrefs(PREF, 'GUI_OPTIONS', 'Orientation', restoreSetup['orient'], str)
restoreSetup['fontSize'] = pVars.fontSize.get()
putPrefs(PREF, 'GUI_OPTIONS', 'Font size', restoreSetup['fontSize'], str)
restoreSetup['guiFont'] = pVars.guiFont.get()
putPrefs(PREF, 'GUI_OPTIONS', 'GUI font', restoreSetup['guiFont'], str)
restoreSetup['codeFont'] = pVars.codeFont.get()
putPrefs(PREF, 'GUI_OPTIONS', 'Gcode font', restoreSetup['codeFont'], str)
restoreSetup['coneSize'] = pVars.coneSize.get()
putPrefs(PREF, 'GUI_OPTIONS', 'Preview cone size', restoreSetup['coneSize'], float)
restoreSetup['popLocation'] = pVars.popLocation.get()
putPrefs(PREF, 'GUI_OPTIONS', 'Popup location', restoreSetup['popLocation'], str)
restoreSetup['tableZoom'] = float(rE(f"{fsetup}.l.gui.zoom get"))
putPrefs(PREF, 'GUI_OPTIONS', 'Table zoom', restoreSetup['tableZoom'], float)
restoreSetup['matDefault'] = pVars.matDefault.get()
putPrefs(PREF, 'GUI_OPTIONS', 'Default material', restoreSetup['matDefault'], int)
restoreSetup['crPercent'] = rE(f"{fsetup}.l.gui.crspeed get")
putPrefs(PREF, 'GUI_OPTIONS', 'Cut recovery speed %', restoreSetup['crPercent'], int)
rE(f"{fcrspeed}.display.cut-rec-speed set {restoreSetup['crPercent']}")
restoreSetup['kbShortcuts'] = pVars.kbShortcuts.get()
putPrefs(PREF, 'GUI_OPTIONS', 'Use keyboard shortcuts', restoreSetup['kbShortcuts'], bool)
restoreSetup['useVirtKB'] = pVars.useVirtKB.get()
putPrefs(PREF, 'GUI_OPTIONS', 'Use soft keyboard', restoreSetup['useVirtKB'], bool)
if not (isPaused or isRunning):
user_button_save()
putPrefs(PREF, 'GUI_OPTIONS', 'Foreground color', colorFore, str)
putPrefs(PREF, 'GUI_OPTIONS', 'Background color', colorBack, str)
putPrefs(PREF, 'GUI_OPTIONS', 'Disabled color', colorDisable, str)
putPrefs(PREF, 'GUI_OPTIONS', 'Active color', colorActive, str)
putPrefs(PREF, 'GUI_OPTIONS', 'Warning color', colorWarn, str)
putPrefs(PREF, 'GUI_OPTIONS', 'Voltage color', colorVolt, str)
putPrefs(PREF, 'GUI_OPTIONS', 'Arc OK color', colorArc, str)
putPrefs(PREF, 'GUI_OPTIONS', 'LED color', colorLed, str)
putPrefs(PREF, 'GUI_OPTIONS', 'Trough color', colorTrough, str)
for button in togglePins:
set_toggle_pins(button)
##############################################################################
# GENERAL FUNCTIONS #
##############################################################################
def thc_enable_toggled():
hal.set_p('plasmac.thc-enable', str(pVars.thcEnable.get()))
putPrefs(PREF, 'ENABLE_OPTIONS','THC enable', pVars.thcEnable.get(), bool)
def corner_enable_toggled():
hal.set_p('plasmac.cornerlock-enable', str(pVars.cornerEnable.get()))
putPrefs(PREF, 'ENABLE_OPTIONS','Corner lock enable', pVars.cornerEnable.get(), bool)
def void_enable_toggled():
hal.set_p('plasmac.voidlock-enable', str(pVars.voidEnable.get()))
putPrefs(PREF, 'ENABLE_OPTIONS','Void lock enable', pVars.voidEnable.get(), bool)
def auto_volts_toggled():
hal.set_p('plasmac.use-auto-volts', str(pVars.autoVolts.get()))
putPrefs(PREF, 'ENABLE_OPTIONS', 'Use auto volts', pVars.autoVolts.get(), bool)
def ohmic_enable_toggled():
hal.set_p('plasmac.ohmic-probe-enable', str(pVars.ohmicEnable.get()))
putPrefs(PREF, 'ENABLE_OPTIONS', 'Ohmic probe enable', pVars.ohmicEnable.get(), bool)
def ignore_arc_ok_toggled():
hal.set_p('plasmac.ignore-arc-ok-1', str(pVars.ignorArcOk.get()))
def laser_button_enable():
if laserOffsets['X'] or laserOffsets['Y']:
rE(f"grid {fjogf}.zerohome.laser -column 3 -row 0")
rE(f"grid {fcrbuttons}.laser -column 0 -row 0")
else:
rE(f"grid forget {fjogf}.zerohome.laser")
rE(f"grid forget {fcrbuttons}.laser")
def offset_probe_setup():
hal.set_p('plasmac.offset-probe-x', f"{probeOffsets['X']}")
hal.set_p('plasmac.offset-probe-y', f"{probeOffsets['Y']}")
hal.set_p('plasmac.offset-probe-delay', f"{probeOffsets['Delay']}")
if probeOffsets['X'] or probeOffsets['Y']:
rE(f"grid {fparam}.c1.probe.offset-feed-rateL -column 0 -row 6 -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fparam}.c1.probe.offset-feed-rate -column 1 -row 6 -sticky e -padx {{0 4}} -pady {{4 0}}")
else:
rE(f"grid forget {fparam}.c1.probe.offset-feed-rateL")
rE(f"grid forget {fparam}.c1.probe.offset-feed-rate")
def set_toggle_pins(button):
togglePins[button]['state'] = hal.get_value(togglePins[button]['pin'])
if togglePins[button]['state']:
rE(f"{fbuttons}.button{button} configure -bg {colorActive}")
rE(f"{fbuttons}.button{button} configure -text {{{togglePins[button]['ontext']}}}")
else:
if togglePins[button]['runcritical']:
rE(f"{fbuttons}.button{button} configure -bg {colorWarn}")
else:
rE(f"{fbuttons}.button{button} configure -bg {colorBack}")
rE(f"{fbuttons}.button{button} configure -text {{{togglePins[button]['offtext']}}}")
def jog_default_changed(value):
set_jog_slider(int(value) / (vars.max_speed.get() * 60))
def set_jog_slider(value):
# jog slider value is from 0 to 1
if not manualCut['state']:
rE(f"set jog_slider_val {value}")
rE(f"update_jog_slider_vel {value}")
def tar_filter(tarinfo):
if os.path.splitext(tarinfo.name)[1] in ['.pyc']:
return None
else:
return tarinfo
def backup_clicked():
n = time.localtime()
d = f"{str(n.tm_year)[-2:]}{n.tm_mon:02d}{n.tm_mday:02d}"
t = f"{n.tm_hour:02d}{n.tm_min:02d}{n.tm_sec:02d}"
configDir = os.path.dirname(vars.emcini.get())
outName = f"{vars.machine.get()}_v{VER}_{d}_{t}.tar.gz"
outFile = os.path.join(os.path.expanduser('~'), outName)
with tarfile.open(outFile, mode='w:gz', ) as archive:
archive.add(configDir, filter=tar_filter)
title = _('BACKUP')
msg0 = _('A compressed backup of the machine configuration has been saved in your home directory')
msg1 = _('The file name is')
msg2 = _('This file may be attached to a post on the LinuxCNC forum to aid in problem solving')
plasmacPopUp('info', title, f"{msg0}\n\n{msg1}: {outName}\n\n{msg2}\n")
def torch_enable():
hal.set_p('plasmac.torch-enable', str(not hal.get_value('plasmac.torch-enable')))
color_torch()
def update_preview(clear=False):
c.wait_complete(.25)
s.poll()
while s.interp_state != linuxcnc.INTERP_IDLE:
o.tkRedraw()
o.redraw_dro()
o.update_idletasks()
root_window.update()
s.poll()
if clear:
live_plotter.clear()
def user_manual():
web = webbrowser.open_new_tab('https://phillc54.github.io/plasmac2-docs/plasmac2.html')
if not web:
msg0 = _('Cannot open user manual web page')
notifications.add('error', f"{msg0}")
##############################################################################
# JOINTS/AXES FUNCTIONS #
##############################################################################
def ja_button_setup(widget, button, text):
rE(f"radiobutton {widget} -value {button} -text {text} -anchor center \
-variable ja_rbutton -command ja_button_activated -padx 10 -pady 6 \
-indicatoron 0 -bd 2 -highlightthickness 0 -selectcolor {colorActive}")
def ja_button_activated():
if vars.ja_rbutton.get() in 'xyzabcuvw':
widget = getattr(widgets, f"axis_{vars.ja_rbutton.get()}")
widget.focus()
rE(f"{fjogf}.zerohome.zero configure -text {vars.ja_rbutton.get().upper()}0")
if not homing_order_defined:
pre = _('Home')
widgets.homebutton.configure(text = f"{pre} {vars.ja_rbutton.get().upper()}")
else:
widget = getattr(widgets, f"joint_{vars.ja_rbutton.get()}")
widget.focus()
commands.axis_activated()
def joint_mode_switch(a, b, c):
global lastMotionMode
if vars.motion_mode.get() == linuxcnc.TRAJ_MODE_FREE and s.kinematics_type != linuxcnc.KINEMATICS_IDENTITY:
rE(f"grid forget {fmanual}.axes")
rE(f"grid {fmanual}.joints -column 0 -row 0 -padx 2 -sticky w")
widget = getattr(widgets, 'joint_0')
widget.focus()
vars.ja_rbutton.set(0)
elif lastMotionMode == linuxcnc.TRAJ_MODE_FREE or not lastMotionMode:
rE(f"grid forget {fmanual}.joints")
rE(f"grid {fmanual}.axes -column 0 -row 0 -padx 2 -sticky w")
widget = getattr(widgets, f"axis_{first_axis}")
widget.focus()
vars.ja_rbutton.set(first_axis)
lastMotionMode = vars.motion_mode.get()
##############################################################################
# MANUAL CUT #
##############################################################################
def manual_cut(event):
global manualCut
if manual_ok():
if not hal.get_value('spindle.0.on'):
if not critical_button_check():
return
msg = bounds_check_probe(True)
if msg:
notifications.add('error', msg)
return
c.spindle(linuxcnc.SPINDLE_FORWARD, 1)
manualCut['feed'] = vars.jog_speed.get()
vars.jog_speed.set(int(rE(f"{fruns}.material.cut-feed-rate get")))
manualCut['state'] = True
rE('.pane.top.jogspeed.s configure -state disabled')
clear_job_stats()
else:
manualCut['state'] = False
c.spindle(linuxcnc.SPINDLE_OFF)
vars.jog_speed.set(manualCut['feed'])
rE('.pane.top.jogspeed.s configure -state normal')
save_total_stats()
##############################################################################
# SINGLE CUT #
##############################################################################
def single_cut():
global result, singleCut
result = False
def cut_file(event):
global result
result = True
sc.quit()
def cancel_file(event):
sc.quit()
title = _('SINGLE CUT')
valid, xLength, yLength = plasmacPopUp('sc', title, None, 'numpad').reply
vkbData['required'] = False
if not valid:
return
try:
xLength = float(xLength)
yLength = float(yLength)
except:
title = _('ENTRY ERROR')
msg = _('Lengths need to be floating point values')
plasmacPopUp('error', title, msg)
return
singleCut['state'] = True
singleCut['G91'] = True if 910 in s.gcodes else False
putPrefs(PREF, 'SINGLE_CUT', 'X length', xLength, float)
putPrefs(PREF, 'SINGLE_CUT', 'Y length', yLength, float)
xEnd = s.position[0] + float(xLength)
yEnd = s.position[1] + float(yLength)
scFile = os.path.join(tmpPath, 'single_cut.ngc')
with open(scFile, 'w') as f:
f.write('G90\n')
f.write('F#<_hal[plasmac.cut-feed-rate]>\n')
f.write(f"G53 G0 X{s.position[0]:0.6f} Y{s.position[1]:0.6f}\n")
f.write('M3 $0 S1\n')
f.write(f"G53 G1 X{xEnd:0.6f} Y{yEnd:0.6f}\n")
f.write('M5 $0\n')
f.write('M2\n')
if loaded_file != scFile:
pVars.preRflFile.set(loaded_file)
open_file_guts(scFile, False, False)
commands.set_view_z()
##############################################################################
# ALIGNMENT FUNCTIONS #
##############################################################################
def touch_off_xy(state, x, y):
if state == '0' and isIdleHomed:
if manual_ok():
save_task_mode = s.task_mode
ensure_mode(linuxcnc.MODE_MDI)
c.mdi(f"G10 L20 P0 X{x} Y{y}")
c.wait_complete()
s.poll()
o.tkRedraw()
reload_file(False)
ensure_mode(save_task_mode)
set_motion_teleop(1)
o.redraw_dro()
def laser_button_toggled(state, button):
global laserTimer, laserButtonState, laserText, laserOffsets
global machineBounds, isIdleHomed
if state == '1': # button pressed
if s.paused and not hal.get_value('plasmac.laser-recovery-state'):
xPos = s.position[0] + laserOffsets['X']
yPos = s.position[1] + laserOffsets['Y']
if xPos < machineBounds['X-'] or xPos > machineBounds['X+'] or yPos < machineBounds['Y-'] or yPos > machineBounds['Y+']:
msg0 = _('Torch cannot move outside the machine boundary')
notifications.add('error', f"{msg0}")
return
hal.set_p('plasmac.laser-x-offset', f"{str(int(laserOffsets['X'] / hal.get_value('plasmac.offset-scale')))}")
hal.set_p('plasmac.laser-y-offset', f"{str(int(laserOffsets['Y'] / hal.get_value('plasmac.offset-scale')))}")
hal.set_p('plasmac.laser-recovery-start', '1')
hal.set_p('plasmac.cut-recovery', '1')
comp['laser-on'] = 1
return
# long press timer
elif isIdleHomed:
laserTimer = 700
root_window.after(100, laser_long_press_timer)
else: # button released
if laserButtonState == 'reset':
laserButtonState = 'laser'
return
laserTimer = 0
if not isIdleHomed:
return
xPos = s.position[0] - laserOffsets['X']
yPos = s.position[1] - laserOffsets['Y']
if xPos < machineBounds['X-'] or xPos > machineBounds['X+'] or yPos < machineBounds['Y-'] or yPos > machineBounds['Y+']:
msg0 = _('Laser is outside the machine boundary')
notifications.add('error', f"{msg0}")
return
if laserButtonState == 'laser':
pVars.laserText.set(_('Mark'))
laserButtonState = 'markedge'
comp['laser-on'] = 1
return
elif laserButtonState == 'setorigin':
comp['laser-on'] = 0
laserButtonState = sheet_align('laser', laserButtonState, laserOffsets['X'], laserOffsets['Y'])
def laser_long_press_timer():
global laserTimer, laserButtonState
if laserTimer > 0:
laserTimer -= 100
if laserTimer <= 0:
comp['laser-on'] = 0
laserButtonState = 'reset'
pVars.laserText.set(_('Laser'))
else:
root_window.after(100, laser_long_press_timer)
def sheet_align(mode, buttonState, offsetX, offsetY):
global relPos, startAlignPos
msgList = []
if buttonState == 'markedge':
#FIXME: NO CAMERA - IS A CAMERA WORTHWHILE ON A PLASMA TABLE?
# zAngle = self.w.camview.rotation = 0
ensure_mode(linuxcnc.MODE_MDI)
c.mdi('G10 L2 P0 R0')
c.wait_complete()
live_plotter.clear()
#FIXME: NO CAMERA - IS A CAMERA WORTHWHILE ON A PLASMA TABLE?
# self.w.cam_goto.setEnabled(False)
pVars.laserText.set(_('Origin'))
buttonState = 'setorigin'
startAlignPos['X'] = relPos['X']
startAlignPos['Y'] = relPos['Y']
else:
if mode == 'laser':
pVars.laserText.set(_('Laser'))
buttonState = 'laser'
else:
pVars.laserText.set(_('Mark'))
buttonState = 'markedge'
xDiff = relPos['X'] - startAlignPos['X']
yDiff = relPos['Y'] - startAlignPos['Y']
if xDiff and yDiff:
zAngle = math.degrees(math.atan(yDiff / xDiff))
if xDiff > 0:
zAngle += 180
elif yDiff > 0:
zAngle += 360
if abs(xDiff) < abs(yDiff):
zAngle -= 90
elif xDiff:
if xDiff > 0:
zAngle = 180
else:
zAngle = 0
elif yDiff:
if yDiff > 0:
zAngle = 180
else:
zAngle = 0
else:
zAngle = 0
#FIXME: NO CAMERA - IS A CAMERA WORTHWHILE ON A PLASMA TABLE?
# self.w.camview.rotation = zAngle
ensure_mode(linuxcnc.MODE_MDI)
c.mdi(f"G10 L20 P0 X{offsetX} Y{offsetY}")
c.wait_complete()
c.mdi(f"G10 L2 P0 R{zAngle}")
c.wait_complete()
if loaded_file:
commands.reload_file(False)
if 1:
ensure_mode(linuxcnc.MODE_MDI)
c.mdi('G0 X0 Y0')
c.wait_complete()
#FIXME: NO CAMERA - IS A CAMERA WORTHWHILE ON A PLASMA TABLE?
# self.w.cam_goto.setEnabled(True)
live_plotter.clear()
return buttonState
##############################################################################
# FRAMING FUNCTIONS #
##############################################################################
def frame_error(torch, msgList):
title = _('AXIS LIMIT ERROR')
if not torch:
msgList += '\n'
msgList += _('Do you want to try with the torch?')
reply = plasmacPopUp('yesno', title, msgList).reply
else:
msgList += '\n'
msgList += _('Framing cannot proceed')
reply = plasmacPopUp('error', title, msgList).reply
return reply
def frame_job(feed, height):
global framingState, activeFunction
if o.canon:
msgList, frame_points = bounds_check_framing(laserOffsets['X'], laserOffsets['Y'], True)
if msgList:
reply = frame_error(False, msgList)
if not reply:
return
msgList, frame_points = bounds_check_framing()
if msgList:
reply = frame_error(True, msgList)
return
if s.interp_state == linuxcnc.INTERP_IDLE:
activeFunction = True
if not msgList:
comp['laser-on'] = 1
if not feed:
feed = widgetValues[f"{fruns}.material.cut-feed-rate"]
ensure_mode(linuxcnc.MODE_MDI)
previousMode = None
# metric machine imperial units
if s.linear_units == 1 and s.program_units == 1:
previousMode = 'G20'
c.mdi('G21')
# imperial machine metric units
elif s.linear_units != 1 and s.program_units == 2:
previousMode = 'G21'
c.mdi('G20')
c.wait_complete()
if not height:
height = machineBounds['Z+'] - (hal.get_value('plasmac.max-offset') * unitsPerMm)
c.mdi(f"G53 G0 Z{height:0.4f}")
c.mdi(f"G53 G0 X{frame_points[1][0]:0.2f} Y{frame_points[1][1]:0.2f}")
c.mdi(f"G53 G1 X{frame_points[2][0]:0.2f} Y{frame_points[2][1]:0.2f} F{feed:0.0f}")
c.mdi(f"G53 G1 X{frame_points[3][0]:0.2f} Y{frame_points[3][1]:0.2f}")
c.mdi(f"G53 G1 X{frame_points[4][0]:0.2f} Y{frame_points[4][1]:0.2f}")
c.mdi(f"G53 G1 X{frame_points[1][0]:0.2f} Y{frame_points[1][1]:0.2f}")
c.mdi('G0 X0 Y0')
if previousMode:
c.mdi(previousMode)
framingState = True
def rotate_frame(coordinates):
angle = math.radians(s.rotation_xy)
cos = math.cos(angle)
sin = math.sin(angle)
frame_points = [coordinates[0]]
ox = frame_points[0][0]
oy = frame_points[0][1]
for x, y in coordinates[1:]:
tox = x - ox
toy = y - oy
rx = (tox * cos) - (toy * sin) + ox
ry = (tox * sin) + (toy * cos) + oy
frame_points.append([rx, ry])
xMin = min(frame_points[1:])[0]
xMax = max(frame_points[1:])[0]
yMin = min(frame_points[1:])[1]
yMax = max(frame_points[1:])[1]
return frame_points, xMin, yMin, xMax, yMax
##############################################################################
# BOUNDS CHECKS #
##############################################################################
def bounds_check_file():
msg = _('G-code')
# canon units are always returned as imperial
# ensure returned units are in machine units
unitsMultiplier = 25.4 if s.linear_units == 1 else 1
xMin = (o.canon.min_extents[0] * unitsMultiplier)
xMax = (o.canon.max_extents[0] * unitsMultiplier)
yMin = (o.canon.min_extents[1] * unitsMultiplier)
yMax = (o.canon.max_extents[1] * unitsMultiplier)
errMsg = bounds_compare(xMin, xMax, yMin, yMax, msg)
return errMsg
def bounds_check_probe(local):
msg = _('Move to probe offset')
xPierceOffset = yPierceOffset = xMinP = xMaxP = yMinP = yMaxP = 0
if local:
xMinP = xMaxP = s.position[0] + probeOffsets['X']
yMinP = yMaxP = s.position[1] + probeOffsets['Y']
elif any((comp['x_min_pierce_extent'], comp['x_max_pierce_extent'], \
comp['y_min_pierce_extent'], comp['y_max_pierce_extent'])):
if comp['cut-type']:
#TODO PHILL - Do you want this from the GUI or HAL??
xPierceOffset = comp['x-pierce-offset']
yPierceOffset = comp['y-pierce-offset']
if s.linear_units == 1 and not vars.metric.get():
unitsMultiplier = 25.4
elif s.linear_units != 1 and vars.metric.get():
unitsMultiplier = 1 / 25.4
else:
unitsMultiplier = 1
xMinP = comp['x_min_pierce_extent'] * unitsMultiplier + probeOffsets['X'] + s.g5x_offset[0] + xPierceOffset
xMaxP = comp['x_max_pierce_extent'] * unitsMultiplier + probeOffsets['X'] + s.g5x_offset[0] + xPierceOffset
yMinP = comp['y_min_pierce_extent'] * unitsMultiplier + probeOffsets['Y'] + s.g5x_offset[1] + yPierceOffset
yMaxP = comp['y_max_pierce_extent'] * unitsMultiplier + probeOffsets['Y'] + s.g5x_offset[1] + yPierceOffset
errMsg = bounds_compare(xMinP, xMaxP, yMinP, yMaxP, msg)
return errMsg
def bounds_check_framing(xOffset=0, yOffset=0, laser=False):
msg = _('Framing move')
if laser:
msg1 = _('due to laser offset')
else:
msg1 = ''
# canon units are always returned as imperial
# ensure returned units are in machine units
unitsMultiplier = 25.4 if s.linear_units == 1 else 1
xMin = (o.canon.min_extents_zero_rxy[0] * unitsMultiplier + xOffset)
xMax = (o.canon.max_extents_zero_rxy[0] * unitsMultiplier + xOffset)
yMin = (o.canon.min_extents_zero_rxy[1] * unitsMultiplier + yOffset)
yMax = (o.canon.max_extents_zero_rxy[1] * unitsMultiplier + yOffset)
coordinates = [[s.g5x_offset[0], s.g5x_offset[1]], [xMin, yMin], [xMin, yMax], [xMax, yMax], [xMax, yMin]]
frame_points, xMin, yMin, xMax, yMax = rotate_frame(coordinates)
errMsg = bounds_compare(xMin, xMax, yMin, yMax, msg, msg1)
return (errMsg, frame_points)
def bounds_compare(xMin, xMax, yMin, yMax, msg, msg1=''):
global machineBounds
errMsg = ''
epsilon = 1e-4
txtxMin = _('exceeds the X minimum limit by')
txtxMax = _('exceeds the X maximum limit by')
txtyMin = _('exceeds the Y minimum limit by')
txtyMax = _('exceeds the Y maximum limit by')
lessThan = _('less than')
if xMin < machineBounds['X-']:
errMsg += f'{msg} {txtxMin} {lessThan} {epsilon} {unitSuffix} {msg1}\n' if (machineBounds['X-'] - xMin) < epsilon \
else f'{msg} {txtxMin} {machineBounds["X-"] - xMin:0.4f} {unitSuffix} {msg1}\n'
if xMax > machineBounds['X+']:
errMsg += f'{msg} {txtxMax} {lessThan} {epsilon} {unitSuffix} {msg1}\n' if (xMax - machineBounds['X+']) < epsilon \
else f'{msg} {txtxMax} {xMax - machineBounds["X+"]:0.4f} {unitSuffix} {msg1}\n'
if yMin < machineBounds['Y-']:
errMsg += f'{msg} {txtyMin} {lessThan} {epsilon} {unitSuffix} {msg1}\n' if (machineBounds['Y-'] - yMin) < epsilon \
else f'{msg} {txtyMin} {machineBounds["Y-"] - yMin:0.4f} {unitSuffix} {msg1}\n'
if yMax > machineBounds['Y+']:
errMsg += f'{msg} {txtyMax} {lessThan} {epsilon} {unitSuffix} {msg1}\n' if (yMax - machineBounds['Y+']) < epsilon \
else f'{msg} {txtyMax} {yMax - machineBounds["Y+"]:0.4f} {unitSuffix} {msg1}\n'
return errMsg
##############################################################################
# PERIPHERAL OFFSET FUNCTIONS #
##############################################################################
def offsets_text():
text = []
text.append(_('1. Jog until the peripheral is centered on the mark'))
text.append(_('2. Click the Yes button to get the offsets'))
return text
def set_peripheral_offsets():
toolFile = os.path.realpath(tooltable)
setup_toggle(False)
while 1:
reply = offsets_show(toolFile)
if not reply:
break
def offsets_show(toolFile):
text = []
text.append(_('1. Place some scrap material under the torch'))
text.append(_('2. Touchoff the torch to X0Y0'))
text.append(_('3. Mark the material with a torch pulse'))
text.append(_('4. Click the appropriate button to activate the peripheral'))
reply = plasmacPopUp('offsets', _('SET PERIPHERAL OFFSETS'), ' \n'.join(text)).reply
if reply == 'laser':
comp['laser-on'] = True
offsets_laser_clicked()
elif reply == 'scribe':
comp['offset-set-scribe'] = True
offsets_scribe_clicked(toolFile)
elif reply == 'probe':
comp['offset-set-probe'] = True
offsets_probe_clicked()
else:
comp['laser-on'] = False
comp['offset-set-scribe'] = False
comp['offset-set-probe'] = False
return False
return True
def offsets_laser_clicked():
reply = plasmacPopUp('yesno', _('SET LASER OFFSETS'), ' \n'.join(offsets_text())).reply
if not reply:
comp['laser-on'] = False
return
newOffsets = {'X':round(s.position[0] - s.g5x_offset[0] - s.g92_offset[0], 4) + 0, \
'Y':round(s.position[1] - s.g5x_offset[1] - s.g92_offset[1], 4) + 0}
if offsets_prompt(_('CHANGE LASER OFFSETS'), laserOffsets, newOffsets):
laserOffsets['X'] = newOffsets['X']
laserOffsets['Y'] = newOffsets['Y']
putPrefs(PREF, 'LASER_OFFSET', 'X axis', laserOffsets['X'], float)
putPrefs(PREF, 'LASER_OFFSET', 'Y axis', laserOffsets['Y'], float)
laser_button_enable()
comp['laser-on'] = False
title = _('LASER OFFSETS')
msg = _('Laser offsets have been saved')
plasmacPopUp('info', title, msg)
comp['laser-on'] = False
def offsets_scribe_clicked(toolFile):
try:
scribeOffsets = {'X': 0, 'Y': 0}
tool = []
with open(toolFile, 'r') as inFile:
for line in inFile:
if line.startswith('T1'):
tool = line.split()
inFile.close()
break
if tool:
for item in tool:
if item.startswith('X'):
scribeOffsets['X'] = float(item.replace('X','').replace('+',''))
elif item.startswith('Y'):
scribeOffsets['Y'] = float(item.replace('Y','').replace('+',''))
except:
title = _('TOOL FILE ERROR')
msg = _('Could not get current scribe offsets from tooltable')
plasmacPopUp('info', title, msg)
return
reply = plasmacPopUp('yesno', _('SET SCRIBE OFFSETS'), ' \n'.join(offsets_text())).reply
if not reply:
comp['offset-set-scribe'] = False
return
newOffsets = {'X':round(s.position[0] - s.g5x_offset[0] - s.g92_offset[0], 4) + 0, \
'Y':round(s.position[1] - s.g5x_offset[1] - s.g92_offset[1], 4) + 0}
if offsets_prompt(_('CHANGE SCRIBE OFFSETS'), scribeOffsets, newOffsets):
scribeOffsets['X'] = newOffsets['X']
scribeOffsets['Y'] = newOffsets['Y']
offsets_write_scribe(toolFile, scribeOffsets)
c.load_tool_table()
comp['offset-set-scribe'] = False
title = _('SCRIBE OFFSETS')
msg = _('Scribe offsets have been saved')
plasmacPopUp('info', title, msg)
comp['offset-set-scribe'] = False
def offsets_probe_clicked():
reply = plasmacPopUp('yesno', _('SET PROBE OFFSETS'), ' \n'.join(offsets_text())).reply
if not reply:
comp['offset-set-probe'] = False
return
title = _('OFFSET PROBE DELAY')
prompt = _('Delay (Seconds)')
while 1:
valid, delay = plasmacPopUp('entry', title, prompt, True, probeOffsets['Delay']).reply
if not valid:
comp['offset-set-probe'] = False
return
if not delay:
delay = 0
try:
delay = float(delay)
break
except:
msg = ('Delay entry is invalid')
plasmacPopUp('info', title, msg)
newOffsets = {'X':round(s.position[0] - s.g5x_offset[0] - s.g92_offset[0], 4) + 0, \
'Y':round(s.position[1] - s.g5x_offset[1] - s.g92_offset[1], 4) + 0, \
'Delay':delay}
if offsets_prompt(_('CHANGE PROBE OFFSETS'), probeOffsets, newOffsets, True):
probeOffsets['X'] = newOffsets['X']
probeOffsets['Y'] = newOffsets['Y']
probeOffsets['Delay'] = newOffsets['Delay']
putPrefs(PREF, 'OFFSET_PROBING', 'X axis', probeOffsets['X'], float)
putPrefs(PREF, 'OFFSET_PROBING', 'Y axis', probeOffsets['Y'], float)
putPrefs(PREF, 'OFFSET_PROBING', 'Delay', probeOffsets['Delay'], float)
offset_probe_setup()
comp['offset-set-probe'] = False
title = _('PROBE OFFSETS')
msg = _('Probe offsets have been saved')
plasmacPopUp('info', title, msg)
comp['offset-set-probe'] = False
def offsets_prompt(title, oldOffsets, newOffsets, probe=False):
dly = _('Delay')
prompt = _('From')
prompt += f":\nX:{oldOffsets['X']:0.3f} Y:{oldOffsets['Y']:0.3f}"
if probe:
prompt += f" {dly}:{oldOffsets['Delay']:0.2f}"
prompt += '\n\n'
prompt += _('To')
prompt += f":\nX:{newOffsets['X']:0.3f} Y:{newOffsets['Y']:0.3f}"
if probe:
prompt += f" {dly}:{newOffsets['Delay']:0.2f}"
prompt += '\n'
return plasmacPopUp('yesno', title, prompt, True).reply
def offsets_write_scribe(toolFile, offsets):
written = False
COPY(toolFile, f"{toolFile}~")
with open(f"{toolFile}~", 'r') as inFile:
with open(toolFile, 'w') as outFile:
for line in inFile:
if line.startswith('T1'):
outFile.write(f"T1 P1 X{offsets['X']:0.3f} Y{offsets['Y']:0.3f} ;scribe\n")
written = True
else:
outFile.write(line)
if not written:
outFile.write(f"T1 P1 X{offsets['X']:0.3f} Y{offsets['Y']:0.3f} ;scribe\n")
os.remove(f"{toolFile}~")
##############################################################################
# HEIGHT OVERRIDE #
##############################################################################
def height_lower():
if rE(f"{foverride}.lower cget -state") == 'disabled':
return
global torchHeight
torchHeight -= 0.1
height_set(f"{torchHeight:.1f}")
def height_raise():
if rE(f"{foverride}.raise cget -state") == 'disabled':
return
global torchHeight
torchHeight += 0.1
height_set(f"{torchHeight:.1f}")
def height_reset():
if rE(f"{foverride}.reset cget -state") == 'disabled':
return
global torchHeight
torchHeight = 0
height_set(f"{torchHeight:.1f}")
def height_set(height):
rE(f"{foverride}.height-override configure -text {height}V")
hal.set_p('plasmac.height-override', height)
##############################################################################
# CUT RECOVERY FUNCTIONS #
##############################################################################
def cut_rec_default_changed(percent):
rE(f"{fcrspeed}.display.cut-rec-speed set {percent}")
def cut_rec_slider_changed(percent):
pVars.crSpeed.set(f"{int(widgetValues[f'{fruns}.material.cut-feed-rate']) * int(percent) * 0.01:0.0f}")
def cut_rec_motion(direction):
if int(direction):
speed = float(rE(f"{fcrspeed}.display.cut-rec-speed get")) * 0.01
hal.set_p('plasmac.paused-motion-speed',f"{speed * int(direction):.2f}")
else:
hal.set_p('plasmac.paused-motion-speed','0')
def cut_rec_move(x, y):
if not s.paused:
return
maxMove = 10
if s.linear_units != 1:
maxMove = 0.4
laser = hal.get_value('plasmac.laser-recovery-state') > 0
distX = float(pVars.kerfWidth.get()) * int(x)
distY = float(pVars.kerfWidth.get()) * int(y)
xNew = hal.get_value('plasmac.axis-x-position') + hal.get_value('axis.x.eoffset') - (laserOffsets['X'] * laser) + distX
yNew = hal.get_value('plasmac.axis-y-position') + hal.get_value('axis.y.eoffset') - (laserOffsets['Y'] * laser) + distY
if xNew < machineBounds['X-'] or xNew > machineBounds['X+'] or yNew < machineBounds['Y-'] or yNew > machineBounds['Y+']:
return
xTotal = hal.get_value('axis.x.eoffset') - (laserOffsets['X'] * laser) + distX
yTotal = hal.get_value('axis.y.eoffset') - (laserOffsets['Y'] * laser) + distY
if xTotal > maxMove or xTotal < -maxMove or yTotal > maxMove or yTotal < -maxMove:
return
moveX = int(distX / hal.get_value('plasmac.offset-scale'))
moveY = int(distY / hal.get_value('plasmac.offset-scale'))
hal.set_p('plasmac.x-offset', f"{str(hal.get_value('plasmac.x-offset') + moveX)}")
hal.set_p('plasmac.y-offset', f"{str(hal.get_value('plasmac.y-offset') + moveY)}")
hal.set_p('plasmac.cut-recovery', '1')
def cut_rec_cancel():
hal.set_p('plasmac.cut-recovery', '0')
hal.set_p('plasmac.laser-recovery-start', '0')
hal.set_p('plasmac.x-offset', '0')
hal.set_p('plasmac.y-offset', '0')
comp['laser-on'] = 0
##############################################################################
# MONKEYPATCHED FUNCTIONS FROM AXIS.PY #
##############################################################################
def get_view():
''' add view t '''
x,y,z,p = 0,1,2,3
if str(widgets.view_x['relief']) == 'sunken':
view = x
elif (str(widgets.view_y['relief']) == 'sunken' or
str(widgets.view_y2['relief']) == 'sunken'):
view = y
elif (str(widgets.view_z['relief']) == 'sunken' or
str(widgets.view_z2['relief']) == 'sunken' or
str(widgets.view_t['relief']) == 'sunken'):
view = z
else:
view = p
return view
def filter_program_new(program_filter, infilename, outfilename):
''' remove temporary materials before calling original filter_program function '''
mats = [m for m in materialNumList if m >= 1000000]
if mats:
for mat in mats:
materialFileDict.pop(mat)
materialNumList.remove(mat)
if get_displayed_material() >= 1000000:
change_default_material()
insert_materials()
exitcode, stderr = filter_program_old(program_filter, infilename, outfilename)
return exitcode, stderr
##############################################################################
# MONKEYPATCHED FUNCTIONS FROM GLCANON.PY #
##############################################################################
def draw_grid():
''' do not return if view = P '''
x,y,z,p = 0,1,2,3
view = get_view()
rotation = math.radians(s.rotation_xy % 90)
permutations = [
lambda x_y_z: (x_y_z[2], x_y_z[1], x_y_z[0]), # YZ X
lambda x_y_z1: (x_y_z1[2], x_y_z1[0], x_y_z1[1]), # ZX Y
lambda x_y_z2: (x_y_z2[0], x_y_z2[1], x_y_z2[2])] # XY Z
inverse_permutations = [
lambda z_y_x: (z_y_x[2], z_y_x[1], z_y_x[0]), # YZ X
lambda z_x_y: (z_x_y[1], z_x_y[2], z_x_y[0]), # ZX Y
lambda x_y_z3: (x_y_z3[0], x_y_z3[1], x_y_z3[2])] # XY Z
o.draw_grid_permuted(rotation, permutations[2],
inverse_permutations[2])
def posstrs():
''' show height above material probed height '''
s = o.stat
limit = list(s.limit[:])
homed = list(s.homed[:])
spd = o.to_internal_linear_unit(s.current_vel)
if not o.get_joints_mode() or o.no_joint_display:
if o.get_show_commanded():
positions = s.position
else:
positions = s.actual_position
if o.get_show_relative():
positions = [(i-j) for i, j in zip(positions, s.tool_offset)]
positions = [(i-j) for i, j in zip(positions, s.g5x_offset)]
t = -s.rotation_xy
t = math.radians(t)
x = positions[0]
y = positions[1]
positions[0] = x * math.cos(t) - y * math.sin(t)
positions[1] = x * math.sin(t) + y * math.cos(t)
positions = [(i-j) for i, j in zip(positions, s.g92_offset)]
else:
positions = list(positions)
if o.get_a_axis_wrapped():
positions[3] = math.fmod(positions[3], 360.0)
if positions[3] < 0: positions[3] += 360.0
if o.get_b_axis_wrapped():
positions[4] = math.fmod(positions[4], 360.0)
if positions[4] < 0: positions[4] += 360.0
if o.get_c_axis_wrapped():
positions[5] = math.fmod(positions[5], 360.0)
if positions[5] < 0: positions[5] += 360.0
positions = o.to_internal_units(positions)
axisdtg = o.to_internal_units(s.dtg)
g5x_offset = o.to_internal_units(s.g5x_offset)
g92_offset = o.to_internal_units(s.g92_offset)
tlo_offset = o.to_internal_units(s.tool_offset)
dtg = o.to_internal_linear_unit(s.distance_to_go)
if o.get_show_metric():
positions = o.from_internal_units(positions, 1)
axisdtg = o.from_internal_units(axisdtg, 1)
g5x_offset = o.from_internal_units(g5x_offset, 1)
g92_offset = o.from_internal_units(g92_offset, 1)
tlo_offset = o.from_internal_units(tlo_offset, 1)
dtg *= 25.4
spd = spd * 25.4
spd = spd * 60
# show height above material probed height
if s.linear_units == 1 and not vars.metric.get():
scale = 1 / 25.4
elif s.linear_units != 1 and vars.metric.get():
scale = 25.4
else:
scale = 1
if hal.get_value('plasmac.offsets-active') or isPaused:
positions[2] = hal.get_value('plasmac.z-height') * scale
return o.dro_format(o.stat,spd,dtg,limit,homed,positions,
axisdtg,g5x_offset,g92_offset,tlo_offset)
else:
return o.joint_dro_format(s,spd,o.get_num_joints(),limit, homed)
##############################################################################
# TCL CALLED PYTHON FUNCTIONS HIJACKED FROM AXIS.PY #
##############################################################################
def reload_file(refilter=True):
''' file to load depends on type of file
set view to Z
clear the plot '''
if running(): return
s.poll()
if not loaded_file and not pVars.preRflFile.get():
root_window.tk.call('set_mode_from_tab')
return
line = vars.highlight_line.get()
o.set_highlight_line(None)
if pVars.preRflFile.get():
f = pVars.preRflFile.get()
elif '/shape.ngc' in loaded_file:
f = os.path.join(tmpPath, 'shape.ngc')
else:
f = loaded_file
if refilter or not get_filter(loaded_file):
# we copy the file to a temporary file so that even if it subsequently
# changes on disk, LinuxCNC is parsing the file contents from the time
# the user opened the file
tempfile = os.path.join(tempdir, os.path.basename(loaded_file))
if loaded_file != tempfile:
shutil.copyfile(loaded_file, tempfile)
open_file_guts(tempfile, False, False)
else:
tempfile = os.path.join(tempdir, "filtered-" + os.path.basename(loaded_file))
open_file_guts(tempfile, True, False)
msg = bounds_check_file()
msg += bounds_check_probe(False)
if msg:
notifications.add('error', msg)
commands.set_view_z()
if line:
o.set_highlight_line(line)
live_plotter.clear()
def task_run(*event):
global dryRun
if event and event[0]== 'dry-run' and loaded_file:
if laserOffsets['X'] or laserOffsets['Y']:
framingError = bounds_check_framing(laserOffsets['X'], laserOffsets['Y'], True)[0]
if framingError:
title = _('AXIS LIMIT ERROR')
plasmacPopUp('warn', title, framingError)
return
dryRun = [s.g5x_offset[0], s.g5x_offset[1]]
newX = s.actual_position[0] - s.g5x_offset[0] - laserOffsets['X']
newY = s.actual_position[1] - s.g5x_offset[1] - laserOffsets['Y']
units = '21' if vars.metric.get() else '20'
if s.linear_units == 1 and units == '20':
unitsMultiplier = 1 / 25.4
elif s.linear_units != 1 and units == '20':
unitsMultiplier = 25.4
else:
unitsMultiplier = 1
ensure_mode(linuxcnc.MODE_MDI)
c.mdi(f'G{units} G10 L20 P0 X{newX * unitsMultiplier} Y{newY * unitsMultiplier}')
c.wait_complete()
comp['laser-on'] = 1
hal.set_p('plasmac.dry-run', '1')
if run_warn() or not critical_button_check():
return
global program_start_line, program_start_line_last
program_start_line_last = program_start_line;
ensure_mode(linuxcnc.MODE_AUTO)
c.auto(linuxcnc.AUTO_RUN, program_start_line)
program_start_line = 0
t.tag_remove("ignored", "0.0", "end")
o.set_highlight_line(None)
def task_run_line():
''' get leadin if required
preprocess gcode up to selected line '''
if vars.highlight_line.get() == 0:
return
pVars.startLine.set(vars.highlight_line.get() - 1)
if loaded_file != os.path.join(tmpPath, 'rfl.ngc'):
rflIn = os.path.join(tmpPath, 'filtered_bkp.ngc')
else:
rflIn = loaded_file
if comp['development']:
reload(RFL)
# get rfl type
rfl = {}
title = _('RUN FROM LINE')
valid, type_ = plasmacPopUp('rfl_type', title, None, 'numpad').reply
vkbData['required'] = False
if not valid:
pVars.rflActive = False
pVars.startLine.set(0)
return
lastLine = 0
# get start and end of cutpath
if type_ == 'cut':
count = 0
start = 0
with open(rflIn, 'r') as inFile:
for line in inFile:
line = line.strip()
if line[:3] == 'G00':
start = count
if line[:3] == 'M05' and count > pVars.startLine.get():
break
count += 1
lastLine = count
pVars.startLine.set(start)
rfl['do'] = False
rfl['length'] = 0.0
rfl['angle'] = 0.0
# get leadin parameters
else:
valid, rfl['do'], rfl['length'], rfl['angle'] = plasmacPopUp('rfl', title, None, 'numpad').reply
vkbData['required'] = False
# cancel rfl
if not valid:
pVars.rflActive = False
pVars.startLine.set(0)
return
# get the old data
setup = RFL.run_from_line_get(rflIn, pVars.startLine.get(), lastLine)
if setup['error']:
# cannot do run from line within a subroutine or if using cutter compensation
if setup['compError']:
msg0 = _('Cannot run from line while cutter compensation is active')
notifications.add('error', f"{msg0}\n")
if setup['subError']:
msg0 = _('Cannot do run from line inside a subroutine')
msg1 = ''
for sub in setup['subError']:
msg1 += f" {sub}"
notifications.add('error', f"{msg0} {msg1}\n")
pVars.rflActive = False
pVars.startLine.set(0)
else:
# set the new data
rflFile = os.path.join(tmpPath, 'rfl.ngc')
result = RFL.run_from_line_set(rflFile, setup, rfl, unitsPerMm)
# if leadin cannot be used
if result['error']:
msg0 = _('Unable to calculate a leadin for this cut')
msg1 = _('Program will run from selected line with no leadin applied')
notifications.add('error', f"{msg0}\n{msg1}\n")
# load rfl file
if loaded_file != os.path.join(tmpPath, 'rfl.ngc'):
pVars.preRflFile.set(loaded_file)
open_file_guts(os.path.join(tmpPath, 'rfl.ngc'), False, False)
commands.set_view_z()
def open_file_name(f): # from axis.py
''' set view to Z '''
global loaded_file
loaded_file = f
open_file_guts(f)
commands.set_view_z()
if o.canon is not None:
x = (o.canon.min_extents[0] + o.canon.max_extents[0])/2
y = (o.canon.min_extents[1] + o.canon.max_extents[1])/2
z = (o.canon.min_extents[2] + o.canon.max_extents[2])/2
o.set_centerpoint(x, y, z)
live_plotter.clear()
msg = bounds_check_file()
msg += bounds_check_probe(False)
if msg:
notifications.add('error', msg)
def set_view_p(event=None): # from axis.py
''' add view t '''
widgets.view_z.configure(relief='link')
widgets.view_z2.configure(relief='link')
widgets.view_x.configure(relief='link')
widgets.view_y.configure(relief='link')
widgets.view_y2.configure(relief='link')
widgets.view_t.configure(relief='link')
widgets.view_p.configure(relief='sunken')
vars.view_type.set(5)
o.set_view_p()
def set_view_z(event=None): # from axis.py
''' add view t '''
widgets.view_z.configure(relief='sunken')
widgets.view_z2.configure(relief='link')
widgets.view_x.configure(relief='link')
widgets.view_y.configure(relief='link')
widgets.view_y2.configure(relief='link')
widgets.view_t.configure(relief='link')
widgets.view_p.configure(relief='link')
vars.view_type.set(1)
o.set_view_z()
def get_jog_speed(a):
''' convert speeds to suit gcode units'''
if vars.teleop_mode.get():
if axis_type[a] == "LINEAR" :
return vars.jog_speed.get() / 60 * pVars.jogMultiplier.get()
else:
return vars.jog_aspeed.get() / 60 * pVars.jogMultiplier.get()
else:
if joint_type[a] == 'LINEAR':
return vars.jog_speed.get() / 60 * pVars.jogMultiplier.get()
else:
return vars.jog_aspeed.get() / 60 * pVars.jogMultiplier.get()
def send_mdi_command(command):
''' reload file if G10 in gcode '''
''' disallow M3, M4, M5, and G92 in MDI '''
global mdi_history_index, mdi_history_save_filename
if command != "":
command= command.lstrip().rstrip()
test = command.lower().replace(' ','')
if 'g92' in test and not 'g92.1' in test:
msg0 = _('G92 offsets are not allowed')
notifications.add('error', f"{msg0}\n")
return
if 'm3' in test:
msg0 = _('M3 commands are not allowed in MDI mode')
notifications.add('error', f"{msg0}\n")
return
elif 'm4' in test:
msg0 = _('M4 commands are not allowed in MDI mode')
notifications.add('error', f"{msg0}\n")
return
elif 'm5' in test:
msg0 = _('M5 commands are not allowed in MDI mode')
notifications.add('error', f"{msg0}\n")
return
vars.mdi_command.set("")
ensure_mode(linuxcnc.MODE_MDI)
widgets.mdi_history.selection_clear(0, "end")
## check if input is already in list. If so, then delete old element
#idx = 0
#for ele in widgets.mdi_history.get(0, "end"):
# if ele == command:
# widgets.mdi_history.delete(idx)
# break
# idx += 1
history_size = widgets.mdi_history.size()
new_entry = 1
if history_size > 1 and widgets.mdi_history.get(history_size - 2) == command:
new_entry = 0
if new_entry != 0:
# if command is already at end of list, don't add it again
widgets.mdi_history.insert(history_size - 1, f"{command}")
history_size += 1
widgets.mdi_history.see(history_size - 1)
if history_size > (mdi_history_max_entries + 1):
widgets.mdi_history.delete(0, 0)
history_size= (mdi_history_max_entries + 1)
# pdb.set_trace()
mdi_history_index = widgets.mdi_history.index("end") - 1
c.mdi(command)
o.tkRedraw()
commands.mdi_history_write_to_file(mdi_history_save_filename, history_size)
if 'G10' in command.upper().replace(' ',''):
reload_file()
##############################################################################
# TCL FUNCTIONS HIJACKED FROM AXIS.TCL #
##############################################################################
def update_title(*args):
''' add plasmac to the title '''
if vars.taskfile.get() == '':
file = 'no file loaded'
name = 'AXIS'
else:
file = name = os.path.basename(vars.taskfile.get())
base = f"{vars.machine.get()} plasmac2 v{VER} + AXIS {linuxcnc.version}"
rE(f"wm title {root_window} {{{base} ({file})}}")
rE(f"wm iconname {root_window} {{{name}}}")
def update_jog_slider_vel(value):
''' inhibit slider update if manual cut is active
use a linear response for jog slider '''
if not manualCut['state']:
rE(f"set jog_speed {int(float(value) * vars.max_speed.get() * 60)}")
def set_pane_minsize():
'''we set our own min sizes dependent on font size '''
pass
##############################################################################
# PYTHON FUNCTIONS HIJACKED FROM AXIS.PY #
##############################################################################
def vupdate(var, val):
''' pretend no file is loaded if program is cleared '''
if var == vars.taskfile and 'clear.ngc' in val:
val = ''
try:
if var.get() == val: return
except ValueError:
pass
var.set(val)
def get_coordinate_font(large):
''' remove selectable large font'''
global coordinate_font
global coordinate_linespace
global coordinate_charwidth
global fontbase
coordinate_font = f"monospace {pVars.fontSize.get()}"
if coordinate_font not in font_cache:
font_cache[coordinate_font] = \
glnav.use_pango_font(coordinate_font, 0, 128)
fontbase, coordinate_charwidth, coordinate_linespace = \
font_cache[coordinate_font]
def install_help(app):
''' we use install_kb_text and install_kp_text instead
so we don't need to do anything here '''
return
def prompt_touchoff(title, text, default, tool_only, system=None):
''' use our own prompt
allow use of virtual keyboard'''
title = _('TOUCH OFF')
text = text.replace(':', '') % _('workpiece')
valid, value, system = plasmacPopUp('touch', title, text, 'numpad', system).reply
vkbData['required'] = False
try:
v = float(value)
except:
msg0 = _('Touch off entry is invalid')
plasmacPopUp('error', title, f"{msg0[:16]}{value} {msg0[16:]}")
value = 0.0
if valid:
return(value, system)
else:
return(None, None)
##############################################################################
# USER BUTTON FUNCTIONS #
##############################################################################
def critical_button_check():
valid = True
if hal.get_value('plasmac.torch-enable'):
buttons = ''
btnText = _('button is')
for button in criticalButtons:
if togglePins[str(button)]['runcritical'] and not togglePins[str(button)]['state']:
if buttons:
buttons += ', '
btnText = _('buttons are')
buttons += rE(f"{fbuttons}.button{str(button)} cget -text")
valid = False
if not valid:
msg0 = _('not activated')
msg1 = _('Continue the cut?')
valid = plasmacPopUp('yesno', _('CRITICAL BUTTON'), f'{buttons} {btnText} {msg0}\n\n{msg1}').reply
return valid
def validate_hal_pin(halpin, button, usage):
valid = pBit = False
for pin in halPinList:
if halpin in pin['NAME']:
pBit = isinstance(pin['VALUE'], bool)
valid = True
break
if not valid:
msg0 = _('does not exist for user button')
notifications.add('error', f"{halpin} {msg0} #{button}")
elif not pBit:
msg0 = _('must be a bit pin for user button')
notifications.add('error', f"{usage} {msg0} #{button}")
valid = False
return valid
def get_parameter_value(code, button):
param, start, mid, end = '', '', '', ''
error = False
while '{' in code or '#<_ini[' in code:
if '{' in code:
if not '}' in code:
msg0 = _('is missing for user button')
notifications.add('error', f"'}}' {msg0} #{button}")
code = ''
error = True
break
param = code.split('{', 1)[1].split('}', 1)[0]
start = '{'
mid = ' '
end = '}'
else:
if not '>' in code:
msg0 = _('is missing for user button')
notifications.add('error', f"'>' {msg0} #{button}")
code = ''
error = True
break
param = code.split('#<_ini[', 1)[1].split('>', 1)[0].replace(']', ' ')
start = '#<_ini['
mid = ']'
end = '>'
if param:
section, option = param.split(' ', 1)
if PREF.has_option(section, option):
value = getPrefs(PREF, section.upper(), option, '', str)
elif inifile.find(section.upper(), option.upper()):
value = inifile.find(section.upper(), option.upper())
else:
msg0 = _('is invalid parameter for user button')
notifications.add('error', f"'{section} {option}' {msg0} #{button}")
code = ''
error = True
break
code = code.replace(f"{start}{section}{mid}{option}{end}", value)
param, start, mid, end = '', '', '', ''
return(error, code)
def button_action(button, pressed):
if int(pressed):
user_button_pressed(button, buttonCodes[int(button)])
else:
user_button_released(button, buttonCodes[int(button)])
def user_button_setup():
global buttonNames, buttonCodes, togglePins, pulsePins, machineBounds, criticalButtons
global probeButton, probeText, torchButton, torchText, cChangeButton, invalidButtons
singleCodes = ['ohmic-test','cut-type','single-cut','manual-cut','probe-test', \
'torch-pulse','change-consumables','framing','latest-file']
buttonNames = {0:{'name':None}}
buttonCodes = {0:{'code':None}}
invalidButtons = []
criticalButtons = []
row = 1
for n in range(1, maxUserButtons + 1):
bLabel = None
bName = getPrefs(PREF, 'BUTTONS', f"{n} Name", '', str)
bCode = getPrefs(PREF, 'BUTTONS', f"{n} Code", '', str)
outCode = {'code':None}
parmError = False
if '{' in bCode or '#<_ini[' in bCode:
parmError, bCode = get_parameter_value(bCode, n)
if bCode.strip() == 'ohmic-test' and not 'ohmic-test' in [(v['code']) for k, v in buttonCodes.items()]:
outCode['code'] = 'ohmic-test'
elif bCode.strip() == 'cut-type' and not 'cut-type' in buttonCodes:
bName = bName.split(',')
if len(bName) == 1:
text = _('Pierce\Only') if '\\' in bName[0] else _('Pierce Only')
bName.append(text)
outCode = {'code':'cut-type', 'text':bName}
elif bCode.strip() == 'single-cut' and not 'single-cut' in buttonCodes:
outCode['code'] = 'single-cut'
elif bCode.strip() == 'manual-cut' and not 'manual-cut' in buttonCodes:
outCode['code'] = 'manual-cut'
elif bCode.startswith('probe-test') and not 'probe-test' in [(v['code']) for k, v in buttonCodes.items()]:
codes = bCode.lower().strip().replace(' ','').replace('probe-test','probe-test ').split()
if len(codes) < 3:
outCode = {'code':'probe-test', 'time':10}
probeButton = str(n)
probeText = bName.replace('\\', '\n')
if len(codes) == 2:
try:
value = int(float(codes[1]))
outCode['time'] = value
except:
outCode['code'] = None
elif bCode.startswith('torch-pulse') and not 'torch-pulse' in [(v['code']) for k, v in buttonCodes.items()]:
codes = bCode.lower().strip().replace(' ','').replace('torch-pulse','torch-pulse ').split()
if len(codes) < 3:
outCode = {'code':'torch-pulse', 'time':1.0}
torchButton = str(n)
torchText = bName.replace('\\', '\n')
if len(codes) == 2:
try:
value = round(float(codes[1]), 1)
outCode['time'] = value
except:
outCode['code'] = None
elif bCode.startswith('change-consumables') and not 'change-consumables' in [(v['code']) for k, v in buttonCodes.items()]:
codes = bCode.lower().strip().replace('x',' x').replace('y',' y').replace('f',' f').split()
if len(codes) > 1 and len(codes) < 5:
outCode = {'code':'change-consumables', 'X':None, 'Y':None, 'F':None}
for l in 'xyf':
for c in range(1, len(codes)):
if codes[c].startswith(l):
try:
value = round(float(codes[c].replace(l,'')), 3)
outCode['XYF'['xyf'.index(l)]] = value
except:
outCode['code'] = None
if outCode['X'] is None and outCode['Y'] is None:
outCode['code'] = None
else:
buff = 10 * unitsPerMm # keep 10mm away from machine limits
for axis in 'XY':
if outCode[axis]:
if outCode[f"{axis}"] < machineBounds[f"{axis}-"] + buff:
outCode[f"{axis}"] = machineBounds[f"{axis}-"] + buff
elif outCode[f"{axis}"] > machineBounds[f"{axis}+"] - buff:
outCode[f"{axis}"] = machineBounds[f"{axis}+"] - buff
if outCode['code']:
cChangeButton = str(n)
elif bCode.startswith('framing') and not 'framing' in [(v['code']) for k, v in buttonCodes.items()]:
codes = bCode.lower().strip().replace(' ','').replace('fra','ra').replace('f',' f') \
.replace('us',' us').replace('ra','fra').split()
if codes[0] == 'framing' and len(codes) < 4:
outCode = {'code':'framing', 'F':False, 'Z':False}
for c in range(1, len(codes)):
if codes[c].startswith('f'):
try:
value = round(float(codes[c].replace('f','')), 3)
outCode['F'] = value
except:
outCode['code'] = None
elif codes[c] == 'usecurrentzheight':
outCode['Z'] = True
else:
outCode['code'] = None
elif bCode.startswith('load'):
codes = bCode.strip().replace(' ','').replace('load','load ').split()
if len(codes) > 1 and len(codes) < 3:
if os.path.isfile(os.path.join(open_directory, codes[1])):
outCode = {'code':'load', 'file':os.path.join(open_directory, codes[1])}
elif bCode.startswith('latest-file') and not 'latest-file' in [(v['code']) for k, v in buttonCodes.items()]:
codes = bCode.strip().replace(' ','').replace('latest-file','latest-file ').split()
if len(codes) < 3:
outCode = {'code':'latest-file', 'dir':None}
if len(codes) == 1:
outCode['dir'] = open_directory
elif len(codes) == 2 and os.path.isdir(codes[1]):
outCode['dir'] = codes[1]
else:
outCode['code'] = None
elif bCode.startswith('pulse-halpin '):
if len(bCode.split()) > 1 and len(bCode.split()) < 4:
codes = bCode.strip().split()
if validate_hal_pin(codes[1], n, 'pulse-halpin'):
try:
value = round(float(codes[2]), 1)
outCode = {'code':'pulse-halpin', 'pin':codes[1], 'time':value}
pulsePins[str(n)] = {'pin':codes[1], 'text':None, 'pulse':0, 'state':False}
except:
outCode = {'code':None}
else:
parmError = True
elif bCode.startswith('toggle-halpin '):
if len(bCode.split()) > 1:
ontext = bName
if ';;' in bCode:
ontext = bCode.split(';;')[1] if ';;' in bCode else ''
bCode = bCode[:bCode.index(';;')].strip()
codes = bCode.strip().split()
if validate_hal_pin(codes[1], n, 'toggle-halpin'):
outCode = {'code':'toggle-halpin', 'pin':codes[1], 'critical':False, 'ontext':ontext}
outCode['pin'] = codes[1]
if len(codes) == 3 and (codes[2] == 'runcritical' or codes[2] == 'cutcritical'):
outCode['critical'] = True
criticalButtons.append(n)
togglePins[str(n)] = {'pin':outCode['pin'], 'state':hal.get_value(outCode['pin']), \
'runcritical':outCode['critical'], 'ontext':outCode['ontext'], \
'offtext':bName}
else:
parmError = True
elif bCode and bCode not in singleCodes:
if 'dual-code' in bCode:
# incoming code is: "dual-code" ;; code1 ;; label1 ;; code2 ;; checked (optional = true)
data = bCode.split(';;')
if len(data) not in [4, 5]:
outCode = {'code':None}
continue
else:
if len(data) == 5 and data[4].strip().lower() == 'true':
checked = True
else:
checked = False
dualCodes[str(n)] = {'ontext':data[2].strip(), 'offtext':bName.strip(), 'checked':checked}
codes = [code for code in data[1].split('\\') + ['dual-code'] + data[3].split('\\')]
else:
codes = bCode.strip().split('\\')
codes = [x.strip() for x in codes]
outCode['code'] = []
for cn in range(len(codes)):
if codes[cn][0] == '%':
if codes[cn][-3:] == '.py':
if '/' not in codes[cn]:
codes[cn] = f"%./{codes[cn].replace('%','')}"
if os.path.exists(os.path.expanduser(codes[cn][1:])):
outCode['code'].append(['python3', os.path.expanduser(codes[cn][1:])])
else:
outCode = {'code': None}
elif WHICH(codes[cn].split()[0][1:]) is not None:
outCode['code'].append(['shell', codes[cn][1:]])
else:
outCode = {'code': None}
elif codes[cn][:2].lower() == 'o<':
outCode['code'].append(['ocode', codes[cn]])
elif codes[cn][0].lower() in 'gm':
outCode['code'].append(['gcode', codes[cn]])
elif codes[cn] == 'dual-code':
outCode['code'].append(codes[cn])
else:
outCode = {'code': None}
break
if 'dual-code' in bCode:
dualCodes[str(n)]['oncode'] = outCode['code'][:outCode['code'].index('dual-code')]
dualCodes[str(n)]['offcode'] = outCode['code'][outCode['code'].index('dual-code')+1:]
outCode['code'] = 'dual-code'
else:
outCode = {'code':None}
if rE(f"winfo exists {fbuttons}.button{n}") == '0':
rE(f"button {fbuttons}.button{n} -takefocus 0 -width 10")
if bName and outCode['code']:
bHeight = 1
if type(bName) == list:
bHeight = max(len(bName[0].split('\\')), len(bName[1].split('\\')))
bName = bName[0]
else:
bHeight = len(bName.split('\\'))
bLabel = bName.replace('\\', '\n')
rE(f"{fbuttons}.button{n} configure -text {{{bLabel}}} -height {bHeight} -bg {colorBack}")
rE(f"bind {fbuttons}.button{n} <ButtonPress-1> {{button_action {n} 1}}")
rE(f"bind {fbuttons}.button{n} <ButtonRelease-1> {{button_action {n} 0}}")
row += 1
elif bName or bCode:
if not parmError:
msg0 = _('is invalid code for user button')
notifications.add('error', f"'{bCode}' {msg0} #{n}")
bName = None
outCode = {'code':None}
invalidButtons.append(n)
buttonNames[n] = {'name':bName}
buttonCodes[n] = outCode
user_button_load()
populate_user_buttons()
def user_button_pressed(button, code):
global colorBack, activeFunction
global probePressed, probeTimer, probeButton
global torchPressed, torchTimer, torchButton
if rE(f"{fbuttons}.button{button} cget -state") == 'disabled' or not code:
return
if code['code'] == 'ohmic-test':
hal.set_p('plasmac.ohmic-test','1')
# print window sizes in development mode
if comp['development']:
print(f"Width = {rE('winfo width .')} Height={rE('winfo height .')}")
elif code['code'] == 'cut-type':
pass # actioned from button_release
elif code['code'] == 'single-cut':
pass # actioned from button_release
elif code['code'] == 'manual-cut':
manual_cut(None)
elif code['code'] == 'probe-test' and not hal.get_value('halui.program.is-running'):
if probeTimer:
probeTimer = 0
probe_test_timer()
elif not hal.get_value('plasmac.z-offset-counts'):
msg = bounds_check_probe(True)
if msg:
notifications.add('error', msg)
return
activeFunction = True
probePressed = True
probeTimer = code['time'] * 1000
hal.set_p('plasmac.probe-test','1')
rE(f"{fbuttons}.button{probeButton} configure -text {str(int(probeTimer))}")
rE(f"{fbuttons}.button{probeButton} configure -bg {colorActive}")
root_window.after(100, probe_test_timer)
elif code['code'] == 'torch-pulse':
if torchTimer:
torchTimer = 0
torch_pulse_timer()
elif not hal.get_value('plasmac.z-offset-counts'):
torchPressed = True
torchTimer = code['time'] * 1000
hal.set_p('plasmac.torch-pulse-time',f"{torchTimer}")
hal.set_p('plasmac.torch-pulse-start','1')
rE(f"{fbuttons}.button{torchButton} configure -text {str(int(torchTimer))}")
rE(f"{fbuttons}.button{torchButton} configure -bg {colorActive}")
root_window.after(100, torch_pulse_timer)
elif code['code'] == 'change-consumables' and not hal.get_value('plasmac.breakaway'):
if hal.get_value('axis.x.eoffset-counts') or hal.get_value('axis.y.eoffset-counts'):
hal.set_p('plasmac.consumable-change', '0')
hal.set_p('plasmac.x-offset', '0')
hal.set_p('plasmac.y-offset', '0')
rE(f"{fbuttons}.button{button} configure -bg {colorBack}")
activeFunction = False
else:
activeFunction = True
xPos = s.position[0] if code['X'] is None else code['X']
yPos = s.position[1] if code['Y'] is None else code['Y']
if not code['F']:
code['F'] = rE(f"{fruns}.material.cut-feed-rate get")
hal.set_p('plasmac.xy-feed-rate', str(code['F']))
hal.set_p('plasmac.x-offset', f"{(xPos - s.position[0]) / hal.get_value('plasmac.offset-scale'):.0f}")
hal.set_p('plasmac.y-offset', f"{(yPos - s.position[1]) / hal.get_value('plasmac.offset-scale'):.0f}")
hal.set_p('plasmac.consumable-change', '1')
rE(f"{fbuttons}.button{button} configure -bg {colorActive}")
elif code['code'] == 'framing':
pass # actioned from button_release
elif code['code'] == 'load':
pass # actioned from button_release
elif code['code'] == 'latest-file':
pass # actioned from button_release
#FIXME - pin not turning off when button pressed and pin is on
elif code['code'] == 'pulse-halpin' and hal.get_value('halui.machine.is-on'):
if code['pin'].startswith('axisui.ext.out_'):
comp[code['pin'].replace('axisui.','')] = not hal.get_value(code['pin'])
else:
hal.set_p(code['pin'], str(not hal.get_value(code['pin'])))
if pulsePins[button]['pulse']:
pulsePins[button]['pulse'] = 0
rE(f"{fbuttons}.button{button} configure -text {{{pulsePins[button]['text']}}}")
else:
pulsePins[button]['pulse'] = code['time'] * 1000
pulsePins[button]['text'] = rE(f"{fbuttons}.button{button} cget -text")
root_window.after(100, pulse_halpin_off, button)
elif code['code'] == 'toggle-halpin' and hal.get_value('halui.machine.is-on'):
if code['pin'].startswith('axisui.ext.out_'):
comp[code['pin'].replace('axisui.','')] = not hal.get_value(code['pin'])
else:
hal.set_p(code['pin'], str(not hal.get_value(code['pin'])))
else:
if button in dualCodes:
if rE(f"{fbuttons}.button{button} cget -text") == dualCodes[button]['offtext']:
rE(f"{fbuttons}.button{button} configure -text {{{dualCodes[button]['ontext']}}}")
code['code'] = dualCodes[button]['oncode']
if dualCodes[button]['checked']:
rE(f"{fbuttons}.button{button} configure -bg {colorActive}")
else:
rE(f"{fbuttons}.button{button} configure -text {{{dualCodes[button]['offtext']}}}")
code['code'] = dualCodes[button]['offcode']
rE(f"{fbuttons}.button{button} configure -bg {colorBack}")
for n in range(len(code['code'])):
if code['code'][n][0] == 'python3':
cmd = f"python3 {code['code'][n][1]}"
Popen(cmd, stdin = None, stdout=PIPE, stderr=PIPE, shell=True)
elif code['code'][n][0] == 'shell':
Popen(code['code'][n][1], stdout=PIPE, stderr=PIPE, shell=True)
elif code['code'][n][0] in ['gcode', 'ocode']:
if manual_ok():
ensure_mode(linuxcnc.MODE_MDI)
c.mdi(code['code'][n][1])
update_preview()
def user_button_released(button, code):
global cutType, probePressed, torchPressed
if rE(f"{fbuttons}.button{button} cget -state") == 'disabled' or not code: return
if code['code'] == 'ohmic-test':
hal.set_p('plasmac.ohmic-test','0')
elif code['code'] == 'cut-type':
if not hal.get_value('halui.program.is-running'):
cutType ^= 1
if cutType:
comp['cut-type'] = 1
text = code['text'][1].replace('\\', '\n')
color = colorActive
else:
comp['cut-type'] = 0
text = code['text'][0].replace('\\', '\n')
color = colorBack
rE(f"{fbuttons}.button{button} configure -bg {color} -text {{{text}}}")
reload_file()
elif code['code'] == 'single-cut':
single_cut()
elif code['code'] == 'manual-cut':
pass
elif code['code'] == 'probe-test':
probePressed = False
elif code['code'] == 'torch-pulse':
torchPressed = False
elif code['code'] == 'change-consumables':
pass
elif code['code'] == 'framing':
if not code['F']:
code['F'] = int(rE(f"{fruns}.material.cut-feed-rate get"))
frame_job(code['F'], code['Z'])
elif code['code'] == 'load':
commands.open_file_name(code['file'])
elif code['code'] == 'latest-file':
files = GLOB(f"{code['dir']}/*.ngc")
latest = max(files, key=os.path.getctime)
commands.open_file_name(latest)
elif code['code'] == 'pulse-halpin':
pass
elif code['code'] == 'toggle-halpin':
pass
else:
pass
def user_button_load():
rE(f"{fsetup}.r.torch.enabled delete 0 end")
rE(f"{fsetup}.r.torch.disabled delete 0 end")
rE(f"{fsetup}.r.torch.enabled insert end {{{getPrefs(PREF, 'BUTTONS', 'Torch enabled', 'Torch Enabled', str)}}}")
rE(f"{fsetup}.r.torch.disabled insert end {{{getPrefs(PREF, 'BUTTONS','Torch disabled', 'Torch Disabled', str)}}}")
for n in range(1, maxUserButtons + 1):
rE(f"{fsetup}.r.ubuttons.canvas.frame.name{n} delete 0 end")
rE(f"{fsetup}.r.ubuttons.canvas.frame.code{n} delete 0 end")
if getPrefs(PREF, 'BUTTONS', f"{n} Name", '', str) or getPrefs(PREF, 'BUTTONS', f"{n} Code", '', str):
rE(f"{fsetup}.r.ubuttons.canvas.frame.name{n} insert end {{{getPrefs(PREF, 'BUTTONS', f'{n} Name', '', str)}}}")
rE(f"{fsetup}.r.ubuttons.canvas.frame.code{n} insert end {{{getPrefs(PREF, 'BUTTONS', f'{n} Code', '', str)}}}")
color_user_buttons()
def user_button_save():
global torchEnable
putPrefs(PREF, 'BUTTONS', 'Torch enabled', rE(f"{fsetup}.r.torch.enabled get"), str)
putPrefs(PREF, 'BUTTONS', 'Torch disabled', rE(f"{fsetup}.r.torch.disabled get"), str)
torchEnable['enabled'] = getPrefs(PREF, 'BUTTONS', 'Torch enabled', 'Torch Enabled', str)
torchEnable['disabled'] = getPrefs(PREF, 'BUTTONS','Torch disabled', 'Torch Disabled', str)
if '\\' in torchEnable['enabled'] or '\\' in torchEnable['disabled']:
rE(f"{fbuttons}.torch-enable configure -height 2")
else:
rE(f"{fbuttons}.torch-enable configure -height 1")
color_torch()
for n in range(1, maxUserButtons + 1):
putPrefs(PREF, 'BUTTONS', f"{n} Name", rE(f"{fsetup}.r.ubuttons.canvas.frame.name{n} get"), str)
putPrefs(PREF, 'BUTTONS', f"{n} Code", rE(f"{fsetup}.r.ubuttons.canvas.frame.code{n} get"), str)
rE(f"{fsetup}.r.ubuttons.canvas.frame.name{n} delete 0 end")
rE(f"{fsetup}.r.ubuttons.canvas.frame.code{n} delete 0 end")
user_button_setup()
def probe_test_timer():
global probeTimer, activeFunction
if probeTimer > 0:
probeTimer -= 100
if hal.get_value('plasmac.probe-test-error') and not probePressed:
probeTimer = 0
if probeTimer > 0:
rE(f"{fbuttons}.button{probeButton} configure -text {{{round(probeTimer / 1000, 1)}}}")
root_window.after(100, probe_test_timer)
if probeTimer <= 0:
if probePressed:
root_window.after(100, probe_test_timer)
else:
hal.set_p('plasmac.probe-test','0')
rE(f"{fbuttons}.button{probeButton} configure -text {{{probeText}}}")
rE(f"{fbuttons}.button{probeButton} configure -bg {colorBack}")
probeTimer = 0
activeFunction = False
def torch_pulse_timer():
global torchTimer
if torchTimer > 0:
torchTimer -= 100
if torchTimer > 0:
rE(f"{fbuttons}.button{torchButton} configure -text {{{round(torchTimer / 1000, 1)}}}")
root_window.after(100, torch_pulse_timer)
if torchTimer <= 0:
if torchPressed:
root_window.after(100, torch_pulse_timer)
else:
hal.set_p('plasmac.torch-pulse-start','0')
hal.set_p('plasmac.torch-pulse-time', '0')
rE(f"{fbuttons}.button{torchButton} configure -text {{{torchText}}}")
rE(f"{fbuttons}.button{torchButton} configure -bg {colorBack}")
torchTimer = 0
def pulse_halpin_off(button):
if pulsePins[button]['pulse'] > 0:
pulsePins[button]['pulse'] -= 100
if pulsePins[button]['pulse'] > 0:
rE(f"{fbuttons}.button{button} configure -text {round(pulsePins[button]['pulse'] / 1000, 1)}")
root_window.after(100, pulse_halpin_off, button)
if pulsePins[button]['pulse'] <= 0:
rE(f"{fbuttons}.button{button} configure -text {{{pulsePins[button]['text']}}}")
if pulsePins[button]['pin'].startswith('axisui.ext.out_'):
comp[pulsePins[button]['pin'].replace('axisui.','')] = not hal.get_value(pulsePins[button]['pin'])
else:
hal.set_p(pulsePins[button]['pin'], str(not hal.get_value(pulsePins[button]['pin'])))
pulsePins[button]['pulse'] = 0
##############################################################################
# MATERIAL HANDLING FUNCTIONS #
##############################################################################
def get_displayed_material():
try:
return int(rE(f"{fruns}.material.materials get").split(":")[0])
except:
return -1
def save_material_clicked():
matNum = get_displayed_material()
save_one_material(matNum)
load_materials(matNum, True)
def reload_material_clicked():
MATS.clear()
MATS.read(MATS.fn)
load_materials(get_displayed_material(), True)
def new_material_clicked():
global materialNumList
for n in range(1, 1000000):
if n not in materialNumList:
new = n
break
title = _('ADD MATERIAL')
msg1 = _('Enter New Material Number')
msgs = msg1
while(1):
valid, num = plasmacPopUp('entry', title, f"{msgs}:", 'numpad', new).reply
if not valid:
return
if not num:
msg0 = _('A material number is required')
msgs = f"{msg0}.\n\n{msg1}:"
continue
try:
num = int(num)
except:
msg0 = _('is not a valid number')
msgs = f"{num} {msg0}.\n\n{msg1}:"
continue
if num == 0 or num in materialNumList:
msg0 = _('Material')
msg2 = _('is in use')
msgs = f"{msg0} #{num} {msg2}.\n\n{msg1}:"
elif num >= 1000000:
msg0 = _('Material numbers need to be less than 1000000')
msgs = f"{msg0}.\n\n{msg1}:"
else:
break
msg1 = _('Enter New Material Name')
msgs = msg1
while(1):
valid, nam = plasmacPopUp('entry', title, f"{msgs}:", 'keyboard').reply
if not valid:
return
if not nam:
msg0 = _('Material name is required')
msgs = f"{msg0}.\n\n{msg1}:"
else:
break
putPrefs(MATS,f"MATERIAL_NUMBER_{num}",'NAME', nam, str)
save_one_material(num)
sortPrefs(MATS)
load_materials(num, True)
def delete_material_clicked():
title = _('DELETE MATERIAL')
msg0 = _('Default material cannot be deleted')
material = int(rE(f"{fruns}.material.materials get").split(':')[0])
if material == getPrefs(PREF, 'GUI_OPTIONS', 'Default material', 0, int):
reply = plasmacPopUp('warn', title, msg0).reply
return
msg0 = _('Do you wish to delete material')
reply = plasmacPopUp('yesno', title, f"{msg0} #{material}?").reply
if not reply:
return
removePrefsSect(MATS,f"MATERIAL_NUMBER_{get_displayed_material()}")
load_materials(-1, True)
def material_changed():
global getMaterialBusy, materialAutoChange
if rE(f"{fruns}.material.materials get"):
if getMaterialBusy:
comp['material-change'] = 0
materialAutoChange = False
return
matnum = int(rE(f"{fruns}.material.materials get").split(': ', 1)[0])
if matnum >= 1000000:
rE(f"{toolmat}.save configure -state disabled")
else:
rE(f"{toolmat}.save configure -state normal")
if materialAutoChange:
hal.set_p('motion.digital-in-03','0')
display_selected_material(matnum)
comp['material-change'] = 2
hal.set_p('motion.digital-in-03','1')
else:
display_selected_material(matnum)
materialAutoChange = False
def material_change_pin_changed(halpin):
if halpin == 0:
hal.set_p('motion.digital-in-03','0')
elif halpin == 3:
hal.set_p('motion.digital-in-03','1')
comp['material-change'] = 0
def material_change_number_pin_changed(halpin):
global getMaterialBusy, materialAutoChange
if getMaterialBusy:
return
if comp['material-change'] == 1:
materialAutoChange = True
if halpin == -1:
halpin = pVars.matDefault.get()
if not material_exists(halpin):
materialAutoChange = False
return
index = f"@{materialNumList.index(halpin)}"
rE(f"{fruns}.material.materials setvalue {index}")
display_selected_material(halpin)
def material_change_timeout_pin_changed(halpin):
if halpin:
material = rE(f"{fruns}.material.materials get").split(': ', 1)[0]
msg0 = _('Material change timeout occurred for material')
notifications.add('error', f"{msg0} #{material}\n")
comp['material-change-number'] = material
comp['material-change-timeout'] = 0
hal.set_p('motion.digital-in-03','0')
def material_temp_pin_changed(halpin):
if halpin:
TEMP.read(TEMP.fn)
material = get_one_material(TEMP, f"MATERIAL_NUMBER_{halpin}", halpin)
if not material['valid']:
msg0 = _('Temporary material')
msg1 = _('is invalid')
notifications.add('error', f"{msg0} #{halpin} {msg1}\n")
return
materialFileDict[halpin] = { \
'name': material['name'], \
'kerf_width': material['kerf_width'], \
'pierce_height': material['pierce_height'], \
'pierce_delay': material['pierce_delay'], \
'puddle_jump_height': material['puddle_jump_height'], \
'puddle_jump_delay': material['puddle_jump_delay'], \
'cut_height': material['cut_height'], \
'cut_speed': material['cut_speed'], \
'cut_amps': material['cut_amps'], \
'cut_volts': material['cut_volts'], \
'pause_at_end': material['pause_at_end'], \
'gas_pressure': material['gas_pressure'], \
'cut_mode': material['cut_mode']}
insert_materials()
comp['material-temp'] = 0
def insert_materials():
global materialNumList, materialFileDict, matNames
mats = ''
matDefaults = ''
materialNumList = []
for matnum in sorted(materialFileDict):
mats += f" {{{matnum}: {materialFileDict[matnum]['name']}}}"
materialNumList.append(matnum)
if matnum < 1000000:
matDefaults += f" {{{matnum}}}"
rE(f"{fruns}.material.materials configure -values {{{mats}}}")
rE(f"{fsetup}.l.gui.matdefault configure -values {{{matDefaults}}}")
matNames = mats
def display_selected_material(matnum, reload=False):
rE(f"{fruns}.material.kerf-width set {materialFileDict[matnum]['kerf_width']}")
rE(f"{fruns}.material.pierce-height set {materialFileDict[matnum]['pierce_height']}")
rE(f"{fruns}.material.pierce-delay set {materialFileDict[matnum]['pierce_delay']}")
rE(f"{fruns}.material.puddle-jump-height set {materialFileDict[matnum]['puddle_jump_height']}")
rE(f"{fruns}.material.puddle-jump-delay set {materialFileDict[matnum]['puddle_jump_delay']}")
rE(f"{fruns}.material.cut-height set {materialFileDict[matnum]['cut_height']}")
rE(f"{fruns}.material.cut-feed-rate set {materialFileDict[matnum]['cut_speed']}")
rE(f"{fruns}.material.cut-amps set {materialFileDict[matnum]['cut_amps']}")
rE(f"{fruns}.material.cut-volts set {materialFileDict[matnum]['cut_volts']}")
rE(f"{fruns}.material.pause-at-end set {materialFileDict[matnum]['pause_at_end']}")
rE(f"{fruns}.material.gas-pressure set {materialFileDict[matnum]['gas_pressure']}")
rE(f"{fruns}.material.cut-mode set {materialFileDict[matnum]['cut_mode']}")
if not reload:
comp['material-change-number'] = matnum
def save_one_material(num):
section = f"MATERIAL_NUMBER_{num}"
putPrefs(MATS,section, 'KERF_WIDTH', rE(f"{fruns}.material.kerf-width get"), float)
putPrefs(MATS,section, 'PIERCE_HEIGHT', rE(f"{fruns}.material.pierce-height get"), float)
putPrefs(MATS,section, 'PIERCE_DELAY', rE(f"{fruns}.material.pierce-delay get"), float)
putPrefs(MATS,section, 'PUDDLE_JUMP_HEIGHT', rE(f"{fruns}.material.puddle-jump-height get"), int)
putPrefs(MATS,section, 'PUDDLE_JUMP_DELAY',rE(f"{fruns}.material.puddle-jump-delay get"), float)
putPrefs(MATS,section, 'CUT_HEIGHT', rE(f"{fruns}.material.cut-height get"), float)
putPrefs(MATS,section, 'CUT_SPEED', rE(f"{fruns}.material.cut-feed-rate get"), int)
putPrefs(MATS,section, 'CUT_AMPS', rE(f"{fruns}.material.cut-amps get"), int)
putPrefs(MATS,section, 'CUT_VOLTS', rE(f"{fruns}.material.cut-volts get"), int)
putPrefs(MATS,section, 'PAUSE_AT_END', rE(f"{fruns}.material.pause-at-end get"), float)
putPrefs(MATS,section, 'GAS_PRESSURE', rE(f"{fruns}.material.gas-pressure get"), float)
putPrefs(MATS,section, 'CUT_MODE', rE(f"{fruns}.material.cut-mode get"), int)
def set_saved_material():
matnum = int(rE(f"{fruns}.material.materials get").split(':')[0])
materialFileDict[matnum] = { \
'name': rE(f"{fruns}.material.materials get").split(':')[1], \
'kerf_width': rE(f"{fruns}.material.kerf-width get"), \
'pierce_height': rE(f"{fruns}.material.pierce-height get"), \
'pierce_delay': rE(f"{fruns}.material.pierce-delay get"), \
'puddle_jump_height': rE(f"{fruns}.material.puddle-jump-height get"), \
'puddle_jump_delay': rE(f"{fruns}.material.puddle-jump-delay get"), \
'cut_height': rE(f"{fruns}.material.cut-height get"), \
'cut_speed': rE(f"{fruns}.material.cut-feed-rate get"), \
'cut_amps': rE(f"{fruns}.material.cut-amps get"), \
'cut_volts': rE(f"{fruns}.material.cut-volts get"), \
'pause_at_end': rE(f"{fruns}.material.pause-at-end get"), \
'gas_pressure': rE(f"{fruns}.material.gas-pressure get"), \
'cut_mode': rE(f"{fruns}.material.cut-mode get")}
def get_one_material(prefs, section, matnum):
global notifications
material = {}
# we bring in some ints as floats so we can read QtPlasmaC material files
material['name'] = getPrefs(prefs, section, 'NAME', 'Material', str)
material['kerf_width'] = getPrefs(prefs, section, 'KERF_WIDTH', round(1 * unitsPerMm, 2), float)
material['pierce_height'] = getPrefs(prefs, section, 'PIERCE_HEIGHT', round(3.0 * unitsPerMm, 2), float)
material['pierce_delay'] = getPrefs(prefs, section, 'PIERCE_DELAY', 0, float)
material['puddle_jump_height'] = int(getPrefs(prefs, section, 'PUDDLE_JUMP_HEIGHT', 0, float))
material['puddle_jump_delay'] = getPrefs(prefs, section, 'PUDDLE_JUMP_DELAY', 0, float)
material['cut_height'] = getPrefs(prefs, section, 'CUT_HEIGHT', round(1.0 * unitsPerMm, 2), float)
material['cut_speed'] = int(getPrefs(prefs, section, 'CUT_SPEED', round(2540 * unitsPerMm, 0), float))
material['cut_amps'] = int(getPrefs(prefs, section, 'CUT_AMPS', 45, float))
material['cut_volts'] = int(getPrefs(prefs, section, 'CUT_VOLTS', 100, float))
material['pause_at_end'] = getPrefs(prefs, section, 'PAUSE_AT_END', 0, float)
material['gas_pressure'] = getPrefs(prefs, section, 'GAS_PRESSURE', 0, float)
material['cut_mode'] = int(getPrefs(prefs, section, 'CUT_MODE', 1, float))
material['valid'] = True
required = {'pierce_height':material['pierce_height'],'cut_height':material['cut_height'],'cut_speed':material['cut_speed']}
for item in required:
if not required[item]:
material['valid'] = False
msg0 = _('is missing from Material')
notifications.add('error',f"{item} {msg0} #{matnum}\n")
return material
def set_material_dict(materialFileDict, matnum, material):
materialFileDict[matnum] = { \
'name': material['name'], \
'kerf_width': material['kerf_width'], \
'pierce_height': material['pierce_height'], \
'pierce_delay': material['pierce_delay'], \
'puddle_jump_height': material['puddle_jump_height'], \
'puddle_jump_delay': material['puddle_jump_delay'], \
'cut_height': material['cut_height'], \
'cut_speed': material['cut_speed'], \
'cut_amps': material['cut_amps'], \
'cut_volts': material['cut_volts'], \
'pause_at_end': material['pause_at_end'], \
'gas_pressure': material['gas_pressure'], \
'cut_mode': material['cut_mode']}
def load_materials(display, keepTemp):
global getMaterialBusy, materialFileDict
if keepTemp:
pop = [key for key in materialFileDict if key < 1000000]
for e in pop:
materialFileDict.pop(e)
else:
materialFileDict = {}
getMaterialBusy = True
if not MATS.sections():
msg0 = _('Creating new materials file')
notifications.add('error', f"{msg0}\n")
material = get_one_material(MATS, 'MATERIAL_NUMBER_0', 0)
set_material_dict(materialFileDict, 0, material)
else:
for section in MATS.sections():
matnum = int(section.rsplit('_', 1)[1].strip().strip(']'))
if matnum >= 1000000:
msg0 = _('Material number')
msg1 = _('is invalid')
msg2 = _('Material numbers need to be less than 1000000')
notifications.add('error', f"{msg0} {matnum} {msg1}\n{msg2}\n")
continue
material = get_one_material(MATS, section, matnum)
if not material['valid']:
continue
set_material_dict(materialFileDict, matnum, material)
if not materialFileDict:
msg0 = _('Materials file is empty or corrupt')
notifications.add('error', f"{msg0}\n")
return
insert_materials()
value = getPrefs(PREF, 'GUI_OPTIONS', 'Default material', materialNumList[0], int)
pVars.matDefault.set(value)
restoreSetup['matDefault'] = value
if pVars.matDefault.get() not in materialNumList:
mat = sorted(materialNumList)[0]
msg0 = _('Default material')
msg1 = _('was not found')
msg2 = _('Changing default material to')
notifications.add('info', f"{msg0} #{pVars.matDefault.get()} {msg1}\n{msg2} #{mat}\n")
pVars.matDefault.set(mat)
putPrefs(PREF, 'GUI_OPTIONS', 'Default material', pVars.matDefault.get(), int)
if display == -1 or get_displayed_material() not in materialNumList:
display = pVars.matDefault.get()
index = f"@{materialNumList.index(display)}"
rE(f"{fruns}.material.materials setvalue {index}")
display_selected_material(display)
material_list_width()
getMaterialBusy = False
def change_default_material():
display_selected_material(pVars.matDefault.get())
def material_exists(material):
global materialNumList, materialAutoChange
if int(material) in materialNumList:
return True
else:
if materialAutoChange:
comp['material-change'] = -1
comp['material-change-number'] = rE(f"{fruns}.material.materials get").split(':')[0]
msg0 = _('Material')
msg1 = _('not in material list')
notifications.add('error', f"{msg0} #{int(material)} {msg1}\n")
return False
##############################################################################
# POWERMAX COMMUNICATIONS FUNCTIONS #
##############################################################################
def pmx485_check(port, periodic=False):
if not comPorts:
return False
msg1 = _('Powermax communications is disabled')
ports = []
for p in comPorts.comports():
ports.append(p[0])
if port in ports:
try:
sPort = serial.Serial(port, 19200)
sPort.close()
except Exception as err:
if not periodic:
notifications.add('error', f"{err}\n{msg1}")
return False
else:
if not periodic:
msg0 = _('cannot be found')
notifications.add('error', f"{port} {msg0}\n{msg1}")
return False
return True
def pmx485_startup(port):
global pmx485
if pmx485_load(port):
return
pmx485['exists'] = True
pVars.pmx485Enable.set(True)
pmx485_enable_toggled()
def pmx485_load(port):
import subprocess
msg0 = _('PMX485 component is not able to be loaded,')
msg1 = _('Powermax communications are not available')
count = 0
while not hal.component_exists('pmx485'):
if count >= 3:
notifications.add('error', f"{msg0}\n{msg1}\n")
rE(f"{fruns}.pmx.info configure -text {{{_('Comms Error')}}} -bg red")
return 1
subprocess.run(['halcmd', 'loadusr', '-Wn', 'pmx485', 'pmx485', f"{port}"])
count += 1
return 0
def pmx485_enable_toggled():
global pmx485
if pVars.pmx485Enable.get():
pmx485['commsError'] = False
pmx485['retryTimer'] = 0
if pmx485_load(pmPort):
return
# ensure valid parameters before trying to connect
if pVars.cutMode.get() == 0 or pVars.cutAmps.get() == 0:
msg0 = _('Invalid Cut Mode or Cut Amps')
msg1 = _('cannot connect to Powermax')
notifications.add('error', f"{msg0}\n{msg1}\n")
pVars.pmx485Enable.set(False)
return
# good to go
else:
hal.set_p('pmx485.pressure_set', str(pVars.gasPressure.get()))
hal.set_p('pmx485.current_set', str(pVars.cutAmps.get()))
hal.set_p('pmx485.mode_set', str(pVars.cutMode.get()))
hal.set_p('pmx485.enable', '1')
rE(f"{fruns}.pmx.info configure -text {{{_('Connecting')}}} -bg {colorBack}")
pmx485['commsTimer'] = 3
else:
if hal.component_exists('pmx485'):
hal.set_p('pmx485.enable', '0')
pmx485['connected'] = False
pmx485['commsError'] = False
pmx485['commsTimer'] == 0
pmx485['retryTimer'] == 0
blank = ''
rE(f"{fruns}.pmx.info configure -text {blank} -bg {colorBack}")
def pmx485_current_changed(value):
hal.set_p('pmx485.current_set', str(value))
def pmx485_mode_changed(value):
pVars.gasPressure.set(0)
hal.set_p('pmx485.pressure_set', '0')
hal.set_p('pmx485.mode_set', str(value))
def pmx485_pressure_changed(value):
if value > 0 and value < int(hal.get_value('pmx485.pressure_min') / 2):
pVars.gasPressure.set(int(hal.get_value('pmx485.pressure_min')))
elif value > int(hal.get_value('pmx485.pressure_min') / 2) and value < hal.get_value('pmx485.pressure_min'):
pVars.gasPressure.set(0)
else:
pVars.gasPressure.set(float(value))
hal.set_p('pmx485.pressure_set', str(pVars.gasPressure.get()))
def pmx485_min_current_changed():
rE(f"{fruns}.material.cut-amps configure -from hal.get_value('pmx485.current_min')")
def pmx485_max_current_changed():
rE(f"{fruns}.material.cut-amps configure -to hal.get_value('pmx485.current_max')")
def pmx485_max_pressure_changed():
rE(f"{fruns}.material.gas-pressure configure -to hal.get_value('pmx485.pressure_max')")
if float(hal.get_value('pmx485.pressure_max')) > 15:
rE(f"{fruns}.material.gas-pressureL configure -text {{{_('Gas Pressure psi')}}}")
rE(f"{fruns}.material.gas-pressure configure -increment 1 -format %0.0f")
else:
rE(f"{fruns}.material.gas-pressureL configure -text _('Gas Pressure bar')")
rE(f"{fruns}.material.gas-pressure configure -increment 0.1 -format %0.1f")
def pmx485_status_changed(state):
global pmx485
if state != pmx485['connected']:
if state:
pmx485['commsError'] = False
rE(f"{fruns}.pmx.info configure -text {{{_('Connected')}}} -bg green")
pmx485['connected'] = True
# probably not required as we change the text directly in periodic
#if pmx485['compArcTime']:
# pmx485_arc_time_changed(pmx485['compArcTime'])
# probably not required as we check the fault in periodic
#if hal.get_value('pmx485.fault'):
# pmx485_fault_changed(hal.get_value('pmx485.fault'))
pmx485['commsTimer'] = 0
pmx485['retryTimer'] = 0
else:
rE(f"{fruns}.pmx.info configure -text {{{_('Comms Error')}}} -bg red")
pmx485['commsError'] = True
pmx485['connected'] = False
pmx485['retryTimer'] = 3
def pmx485_fault_changed(fault):
global pmx485
if pmx485['connected']:
faultRaw = f"{fault:04.0f}"
pmx485['compFaultCode'] = f"{faultRaw[0]}-{faultRaw[1:3]}-{faultRaw[3]}"
code = _('Fault Code')
if faultRaw == '0000':
rE(f"{fruns}.pmx.info configure -text {{{_('Connected')}}} -bg green")
elif faultRaw in pmx485FaultName.keys():
if faultRaw == '0210' and hal.get_value('pmx485.current_max') > 110:
faultMsg = pmx485FaultName[faultRaw][1]
elif faultRaw == '0210':
faultMsg = pmx485FaultName[faultRaw][0]
else:
faultMsg = pmx485FaultName[faultRaw]
rE(f"{fruns}.pmx.info configure -text {{{code}: {pmx485['compFaultCode']}}} -bg red")
msg0 = _('Code')
notifications.add('error', f"{msg0}: {pmx485['compFaultCode']}\n{faultMsg}\n")
else:
rE(f"{fruns}.pmx.info configure -text {{{code}: {pmx485['compFaultCode']}}} -bg red")
pmx485['labelState'] = None
msg0 = _('Unknown Powermax fault code')
notifications.add('error', f"{msg0}: {faultRaw}\n")
def pmx485_mesh_enable_toggled():
global pmx485
if pVars.meshEnable.get() and not pmx485['meshMode']:
pmx485['oldMode'] = pVars.cutMode.get()
pVars.cutMode.set(2)
rE(f"{fruns}.material.cut-mode configure -state disabled")
pmx485['meshMode'] = True
hal.set_p('plasmac.mesh-enable', '1')
elif hal.get_value('plasmac.mesh-enable') and pmx485['meshMode']:
pVars.cutMode.set(pmx485['oldMode'])
rE(f"{fruns}.material.cut-mode configure -state normal")
pmx485['meshMode'] = False
hal.set_p('plasmac.mesh-enable', '0')
def pmx485_comms_timeout():
global pmx485
rE(f"{fruns}.pmx.info configure -text {{{_('Comms Error')}}} -bg red")
pmx485['commsError'] = True
pmx485['connected'] = False
pmx485['retryTimer'] = 3
def pmx485_retry_timeout():
pmx485_enable_toggled()
pmx485FaultName = {
'0110': _('Remote controller mode invalid'),
'0111': _('Remote controller current invalid'),
'0112': _('Remote controller pressure invalid'),
'0120': _('Low input gas pressure'),
'0121': _('Output gas pressure low'),
'0122': _('Output gas pressure high'),
'0123': _('Output gas pressure unstable'),
'0130': _('AC input power unstable'),
'0199': _('Power board hardware protection'),
'0200': _('Low gas pressure'),
'0210': (_('Gas flow lost while cutting'), _('Excessive arc voltage')),
'0220': _('No gas input'),
'0300': _('Torch stuck open'),
'0301': _('Torch stuck closed'),
'0320': _('End of consumable life'),
'0400': _('PFC/Boost IGBT module under temperature'),
'0401': _('PFC/Boost IGBT module over temperature'),
'0402': _('Inverter IGBT module under temperature'),
'0403': _('Inverter IGBT module over temperature'),
'0500': _('Retaining cap off'),
'0510': _('Start/trigger signal on at power up'),
'0520': _('Torch not connected'),
'0600': _('AC input voltage phase loss'),
'0601': _('AC input voltage too low'),
'0602': _('AC input voltage too high'),
'0610': _('AC input unstable'),
'0980': _('Internal communication failure'),
'0990': _('System hardware fault'),
'1000': _('Digital signal processor fault'),
'1100': _('A/D converter fault'),
'1200': _('I/O fault'),
'2000': _('A/D converter value out of range'),
'2010': _('Auxiliary switch disconnected'),
'2100': _('Inverter module temp sensor open'),
'2101': _('Inverter module temp sensor shorted'),
'2110': _('Pressure sensor is open'),
'2111': _('Pressure sensor is shorted'),
'2200': _('DSP does not recognize the torch'),
'3000': _('Bus voltage fault'),
'3100': _('Fan speed fault'),
'3101': _('Fan fault'),
'3110': _('PFC module temperature sensor open'),
'3111': _('PFC module temperature sensor shorted'),
'3112': _('PFC module temperature sensor circuit fault'),
'3200': _('Fill valve'),
'3201': _('Dump valve'),
'3201': _('Valve ID'),
'3203': _('Electronic regulator is disconnected'),
'3410': _('Drive fault'),
'3420': _('5 or 24 VDC fault'),
'3421': _('18 VDC fault'),
'3430': _('Inverter capacitors unbalanced'),
'3441': _('PFC over current'),
'3511': _('Inverter saturation fault'),
'3520': _('Inverter shoot-through fault'),
'3600': _('Power board fault'),
'3700': _('Internal serial communications fault'),
}
##############################################################################
# EXTERNAL HAL PINS #
##############################################################################
# called during setup
def ext_hal_create():
global extHalPins
for pin in ['abort', 'power', 'run', 'pause', 'run-pause', 'touchoff',
'probe','pulse', 'frame-job']:
comp.newpin(f"ext.{pin}", hal.HAL_BIT, hal.HAL_IN)
extHalPins[pin] = {'state': False, 'last': False}
# external pins for user button toggle and pulse
for pin in range(3):
comp.newpin(f"ext.out_{pin}", hal.HAL_BIT, hal.HAL_OUT)
# pins for pierce only offsets
for pin in 'xy':
comp.newpin(f"{pin}-pierce-offset", hal.HAL_FLOAT, hal.HAL_OUT)
# pins for pierce coordinate extents
for pin in ['x_min', 'y_min', 'x_max', 'y_max']:
comp.newpin(f"{pin}_pierce_extent", hal.HAL_FLOAT, hal.HAL_IN)
# called every cycle by user_live_update
def ext_hal_watch():
global extHalPins, isIdle, isIdleHomed, isPaused, isRunning, probePressed, torchPressed
for pin in extHalPins:
state = comp[f"ext.{pin}"]
if state != extHalPins[pin]['last']:
extHalPins[pin]['last'] = state
# pressed commands
if state:
if pin == 'abort':
commands.task_stop()
elif pin == 'power':
commands.onoff_clicked()
elif pin == 'run' and isIdleHomed:
commands.task_run()
elif pin == 'pause':
if isRunning:
commands.task_pause()
elif isPaused:
commands.task_resume()
elif pin == 'run-pause':
if isIdleHomed:
commands.task_run()
elif isRunning:
commands.task_pause()
elif isPaused:
commands.task_resume()
elif pin == 'touchoff':
touch_off_xy('1', '0', '0')
elif pin == 'probe' and probeButton:
user_button_pressed(probeButton, buttonCodes[int(probeButton)])
elif pin == 'pulse' and torchButton:
user_button_pressed(torchButton, buttonCodes[int(torchButton)])
# released commands
else:
if pin == 'touchoff':
touch_off_xy('0', '0', '0')
elif pin == 'probe' and probeButton:
user_button_released(probeButton, buttonCodes[int(probeButton)])
elif pin == 'pulse' and torchButton:
user_button_released(torchButton, buttonCodes[int(torchButton)])
elif pin == 'frame-job':
num = get_button_num('framing')
if num:
user_button_released(str(num), buttonCodes[num])
def get_button_num(name):
num = None
for num in buttonCodes:
if buttonCodes[num]['code'] == name:
break
return num
##############################################################################
# HELP TEXT #
##############################################################################
def help_text():
global kb_text_1, kb_text_2, kp_text_1
kb_text_1 = [
('ESC', _('Abort')),
('', ''),
('F1', _('Emergency stop')),
('F2', _('Machine on/off')),
('F3', _('Manual tab')),
('F4', _('Cycle through right side tabs')),
('F5', _('MDI tab')),
('F9', _('Manual cut start/stop')),
('', ''),
('X', _('Activate first axis')),
('Y', _('Activate second axis')),
('Z', _('Activate third axis')),
('A', _('Activate fourth axis')),
('B', _('Activate fifth axis')),
('', ''),
('C', _('Select continuous jog')),
(_('I or Shift-I'), _('Select jog increment')),
(_('Left or Right'), _('Jog first axis or joint')),
(_('Up or Down'), _('Jog second axis or joint')),
(_('PgUp or PgDn'), _('Jog third axis or joint')),
('[ or ]', _('Jog fourth axis or joint')),
('; or \'', _('Jog fifth axis or joint')),
(_('Shift+ above jog'), _('Jog at traverse speed')),
]
kb_text_2 = [
(_('Ctrl-Home'), _('Emulate GUI home button')),
(_('Shift-Home'), _('Home active joint')),
(_('End'), _('Touchoff X and Y to zero')),
(_('Ctrl-End'), _('Touchoff active axis to zero')),
(_('Shift-End'), _('Set touchoff for current axis')),
(_('Del'), _('Emulate GUI laser button')),
('', ''),
('O', _('Open program')),
('R', _('Run program')),
('T', _('Step program')),
('P or Space', _('Pause program')),
('S', _('Resume program')),
(_('Ctrl-R'), _('Reload program')),
(_('Ctrl-S'), _('Save G-code as')),
('', ''),
(_('Ctrl-K'), _('Clear live plot')),
(_('Ctrl-Space'), _('Clear notifications')),
('', ''),
('World Mode:', ''),
('` or 1~0', _('Feed override from 0% to 100%')),
('Ctrl+ ` or 1~0', _('Rapid Override from 0% to 100%')),
('Alt+ ` or 1~0', _('Jog speed from 0% to 100%')),
('Joint Mode:', ''),
('` or 0~8', _('Activate joints 1 thru 9')),
]
kp_text_1 = [
(_('Left'), _('Jog first axis or joint negative')),
(_('Right'), _('Jog first axis or joint positive')),
(_('Up'), _('Jog second axis or joint positive')),
(_('Down'), _('Jog second axis or joint negative')),
(_('PgUp'), _('Jog third axis or joint positive')),
(_('PgDn'), _('Jog third axis or joint negative')),
('', ''),
(_('Minus+ above jog'), _('Jog speed x 0.1')),
(_('Plus+ above jog'), _('Jog speed x 10')),
('', ''),
(_('Home'), _('Emulate GUI home button')),
(_('End'), _('Touchoff X and Y to zero')),
(_('Del'), _('Emulate GUI laser button')),
]
def install_kb_text(app):
keys = nf.makewidget(app, Frame, '.keys.text')
fixed = app.tk.call("linuxcnc::standard_fixed_font")
for i in range(len(kb_text_1)):
a, b = kb_text_1[i]
Label(keys, text=a, font=fixed, padx=4, pady=0, highlightthickness=0).grid(row=i, column=0, sticky="w")
Label(keys, text=b, padx=4, pady=0, highlightthickness=0).grid(row=i, column=1, sticky="w")
for i in range(len(kb_text_2)):
a, b = kb_text_2[i]
Label(keys, text=a, font=fixed, padx=4, pady=0, highlightthickness=0).grid(row=i, column=3, sticky="w")
Label(keys, text=b, padx=4, pady=0, highlightthickness=0).grid(row=i, column=4, sticky="w")
Label(keys, text=" ").grid(row=0, column=2)
def install_kp_text(app):
keyp = nf.makewidget(app, Frame, '.keyp.text')
fixed = app.tk.call("linuxcnc::standard_fixed_font")
for i in range(len(kp_text_1)):
a, b = kp_text_1[i]
Label(keyp, text=a, font=fixed, padx=4, pady=0, highlightthickness=0).grid(row=i, column=0, sticky="w")
Label(keyp, text=b, padx=4, pady=0, highlightthickness=0).grid(row=i, column=1, sticky="w")
##############################################################################
# KEYBOARD BINDINGS #
##############################################################################
def key_pressed(key):
global keyDelay
if key not in keyDelay:
keyDelay[key] = None
if keyDelay[key]:
root_window.after_cancel(keyDelay[key])
keyDelay[key] = None
return
if key == 'Delete':
laser_button_toggled('1', 1)
if key == 'End':
touch_off_xy('1', 0, 0)
if key == 'Home':
if homing_order_defined:
commands.home_all_joints()
else:
commands.home_joint()
elif key == 'jogfast':
pVars.jogMultiplier.set(10)
elif key == 'jogslow':
pVars.jogMultiplier.set(0.1)
def key_off(key):
global keyDelay
keyDelay[key] = None
if key == 'Delete':
laser_button_toggled('0', 1)
if key == 'End':
touch_off_xy('0', 0, 0)
elif key in ['jogfast', 'jogslow']:
pVars.jogMultiplier.set(1)
def key_released(key):
global keyDelay
if key not in keyDelay:
keyDelay[key] = None
if keyDelay[key]:
return
keyDelay[key] = root_window.after_idle(lambda: key_off(key))
def kb_shortcuts_changed():
keyboard_bindings(pVars.kbShortcuts.get())
def virtual_kb_changed():
if pVars.useVirtKB.get():
if not WHICH('onboard'):
rE(f"{fsetup}.l.gui.useVirtKB deselect")
msg0 = _('a valid "onboard" virtual keyboard is not installed')
notifications.add('error', f"{msg0}\n")
return
pVars.kbShortcuts.set(0)
rE(f"{fsetup}.l.gui.kbShortcutsL configure -state disabled")
rE(f"{fsetup}.l.gui.kbShortcuts configure -state disabled")
if rE('winfo ismapped .fconv'):
vkb_show('numpad')
elif rE(f"winfo ismapped {fparam}"):
vkb_show('numpad')
elif rE(f"winfo ismapped {fsetup}"):
vkb_show('keyboard')
else:
rE(f"{fsetup}.l.gui.kbShortcutsL configure -state normal")
rE(f"{fsetup}.l.gui.kbShortcuts configure -state normal")
vkb_hide()
def make_lambda(func, value, state=None):
if state is not None:
return lambda event:func(value, state)
else:
return lambda event:func(value)
def keyboard_bindings(state):
if firstRun:
help_text()
# delete kb shortcuts from help menu
rE('.menu.help delete 3 5')
# remove current bindings
for key in root_window.bind():
root_window.unbind(key)
# create new key bindings if enabled
if state:
# Escape
root_window.bind('<Escape>', commands.task_stop)
# F keys
root_window.bind_class('all', '<Key-F1>', commands.estop_clicked)
root_window.bind('<Key-F2>', commands.onoff_clicked)
root_window.bind('<Key-F3>', f"{pane_top}.tabs raise manual")
root_window.bind('<Key-F4>', commands.next_tab)
root_window.bind('<Key-F5>', f"{pane_top}.tabs raise mdi")
#FIXME - ???
# root_window.bind('<Key-F5>', f"+{tabs_mdi}.command selection range 0 end")
root_window.bind('<Key-F9>', commands.manual_cut)
# axis selection
root_window.bind('x', lambda event: activate_ja_widget('x'))
root_window.bind('y', lambda event: activate_ja_widget('y'))
root_window.bind('z', lambda event: activate_ja_widget('z'))
root_window.bind('a', lambda event: activate_ja_widget('a'))
root_window.bind('b', lambda event: activate_ja_widget('b'))
#jogging
root_window.bind('c', lambda event: jogspeed_continuous())
root_window.bind('i', lambda event: jogspeed_incremental())
root_window.bind('I', lambda event: jogspeed_incremental(-1))
bind_axis('Left', 'Right', 0)
bind_axis('Down', 'Up', 1)
bind_axis('Next', 'Prior', 2)
bind_axis('KP_Left', 'KP_Right', 0)
bind_axis('KP_Down', 'KP_Up', 1)
bind_axis('KP_Next', 'KP_Prior', 2)
# bind_axis('KP_4', 'KP_6', 0)
# bind_axis('KP_2', 'KP_8', 1)
# bind_axis('KP_3', 'KP_9', 2)
bind_axis('bracketleft', 'bracketright', 3)
bind_axis('semicolon', 'quoteright', 4)
root_window.bind('<Key-KP_Add>', lambda event: commands.key_pressed('jogfast'))
root_window.bind('<KeyRelease-KP_Add>', lambda event: commands.key_released('jogfast'))
root_window.bind('<Key-KP_Subtract>', lambda event: commands.key_pressed('jogslow'))
root_window.bind('<KeyRelease-KP_Subtract>', lambda event: commands.key_released('jogslow'))
# sliders
root_window.bind('`', lambda event: activate_ja_widget_or_set_feedrate(0))
root_window.bind('<Control-Key-quoteleft>',lambda event: set_rapidrate(0))
root_window.bind('<Alt-Key-quoteleft>',lambda event: set_jog_slider(0.0))
for n in range(10):
rate = n if n > 0 else 10
root_window.bind(f"{n}", make_lambda(activate_ja_widget_or_set_feedrate, rate))
root_window.bind(f"<Control-Key-{n}>", make_lambda(set_rapidrate, rate * 10))
root_window.bind(f"<Alt-Key-{n}>", make_lambda(set_jog_slider, rate * 0.1))
# homing
if homing_order_defined:
root_window.bind('<Control-Home>', commands.home_all_joints)
else:
root_window.bind('<Control-Home>', commands.home_joint)
root_window.bind('<Shift-Home>', commands.home_joint)
# touchoff
root_window.bind('<Key-End>', lambda event: commands.touch_off_xy('1', 0, 0))
root_window.bind('<KeyRelease-End>', lambda event: commands.touch_off_xy('0', 0, 0))
root_window.bind('<Control-End>', commands.set_axis_offset)
root_window.bind('<Shift-End>', commands.touch_off_system)
root_window.bind('<Key-Delete>', lambda event: commands.key_pressed('Delete'))
root_window.bind('<KeyRelease-Delete>', lambda event: commands.key_released('Delete'))
# program
root_window.bind('o', commands.open_file)
root_window.bind('r', commands.task_run)
root_window.bind('p', commands.task_pause)
root_window.bind('<space>', commands.task_pause)
root_window.bind('s', commands.task_resume)
root_window.bind('<Control-r>', commands.reload_file)
root_window.bind('<Control-s>', commands.save_gcode)
# clearing
root_window.bind('<Control-k>', commands.clear_live_plot)
root_window.bind('<Control-space>', lambda event: notifications.clear())
# whacky keypad stuff
keys1 = ['Decimal', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
# Down, Next, Left, Right, Up, and Prior are done in the jogging section above
keys2 = ['Delete', 'Insert', 'End', None, None, None, 'Begin', None, 'Home', None, None]
for k in range(len(keys2)):
if keys2[k]:
root_window.bind(f"<Key-KP_{keys2[k]}>", make_lambda(key_pressed, keys2[k]))
root_window.bind(f"<KeyRelease-KP_{keys2[k]}>", make_lambda(key_released, keys2[k]))
root_window.bind(f"<Mod2-KeyRelease-KP_{keys2[k]}>", make_lambda(key_released, keys1[k]))
# add kb shortcuts to help menu
rE('.menu.help add separator')
rE('.menu.help add command -command {wm transient .keys .;wm deiconify .keys; focus .keys.ok}')
rE(f"setup_menu_accel .menu.help end {{{_('Keyboard Shortcuts')}}}")
rE('.menu.help add command -command {wm transient .keyp .;wm deiconify .keyp; focus .keyp.ok}')
rE(f"setup_menu_accel .menu.help end {{{_('Keypad Shortcuts')}}}")
# use arrow keys for spinboxes
for item in rpList + cpList:
widget = f"{item[0]}.{item[1]}"
rE(f"bind {widget} <Up> {{%W invoke buttonup}}")
rE(f"bind {widget} <Down> {{%W invoke buttondown}}")
rE(f"bind {widget} <Left> {{::tk::EntrySetCursor %W [expr [%W index insert] - 1]}}")
rE(f"bind {widget} <Right> {{::tk::EntrySetCursor %W [expr [%W index insert] + 1]}}")
##############################################################################
# ONBOARD VIRTUAL KEYBOARD #
##############################################################################
def vkb_validate():
global vkbData
vkbData = {'valid':False, 'visible':False, 'required':False, 'layout':None, 'width':0, 'height':0}
if WHICH('onboard'):
try:
cmd = 'gsettings get org.onboard layout'
vkbData['layout'] = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True).communicate()[0].decode().strip()
cmd = 'gsettings get org.onboard.window.landscape width'
vkbData['width'] = int(Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True).communicate()[0].decode().strip())
cmd = 'gsettings get org.onboard.window.landscape height'
vkbData['height'] = int(Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True).communicate()[0].decode().strip())
vkbData['valid'] = True
except:
pass
def vkb_show(layout):
global vkbData
if firstRun: return
if not pVars.useVirtKB.get(): return
w = 240 if layout == 'numpad' else 740
vkbData['visible'] = True
vkb_settings(os.path.join(libPath, layout), w, 240)
time.sleep(0.1)
cmd = 'dbus-send'
cmd += ' --type=method_call'
cmd += ' --dest=org.onboard.Onboard'
cmd += ' /org/onboard/Onboard/Keyboard'
cmd += ' org.onboard.Onboard.Keyboard.Show'
Popen(cmd, stdout=PIPE, shell=True)
def vkb_hide(custom=False):
vkbData['visible'] = False
cmd = 'dbus-send'
cmd += ' --type=method_call'
cmd += ' --dest=org.onboard.Onboard'
cmd += ' /org/onboard/Onboard/Keyboard'
cmd += ' org.onboard.Onboard.Keyboard.Hide'
Popen(cmd, stdout=PIPE, shell=True)
vkb_settings(vkbData['layout'], vkbData['width'], vkbData['height'])
def vkb_settings(layout, width, height):
Popen(f"gsettings set org.onboard layout {layout}", stdout=PIPE, shell=True)
Popen(f"gsettings set org.onboard.window.landscape width {width - 1}", stdout=PIPE, shell=True)
Popen(f"gsettings set org.onboard.window.landscape height {height - 1}", stdout=PIPE, shell=True)
time.sleep(0.1)
Popen(f"gsettings set org.onboard.window.landscape width {width}", stdout=PIPE, shell=True)
Popen(f"gsettings set org.onboard.window.landscape height {height}", stdout=PIPE, shell=True)
##############################################################################
# COLOR CHANGE #
##############################################################################
def read_colors():
global colorFore, colorBack, colorDisable, colorActive, colorWarn
global colorVolt, colorArc, colorLed, colorTrough
colorFore = getPrefs(PREF, 'GUI_OPTIONS','Foreground color', '#000000', str)
colorBack = getPrefs(PREF, 'GUI_OPTIONS','Background color', '#808080', str)
colorDisable = getPrefs(PREF, 'GUI_OPTIONS','Disabled color', '#a0a0a0', str)
colorActive = getPrefs(PREF, 'GUI_OPTIONS','Active color', '#00cc00', str)
colorWarn = getPrefs(PREF, 'GUI_OPTIONS','Warning color', '#dd0000', str)
colorVolt = getPrefs(PREF, 'GUI_OPTIONS','Voltage color', '#ff8800', str)
colorArc = getPrefs(PREF, 'GUI_OPTIONS','Arc OK color', '#ff8800', str)
colorLed = getPrefs(PREF, 'GUI_OPTIONS','LED color', '#eeff00', str)
colorTrough = getPrefs(PREF, 'GUI_OPTIONS','Trough color', '#505050', str)
def color_user_buttons(fgc='#000000',bgc='#d9d9d9'):
for b in criticalButtons:
rE(f"{fbuttons}.button{b} configure -bg {colorWarn}")
# user button entries in setup frame
for n in range(1, maxUserButtons + 1):
if n in invalidButtons:
rE(f"{fsetup}.r.ubuttons.canvas.frame.name{n} configure -fg {colorWarn}")
rE(f"{fsetup}.r.ubuttons.canvas.frame.code{n} configure -fg {colorWarn}")
else:
rE(f"{fsetup}.r.ubuttons.canvas.frame.name{n} configure -fg {colorFore}")
rE(f"{fsetup}.r.ubuttons.canvas.frame.code{n} configure -fg {colorFore}")
def color_torch():
if hal.get_value('plasmac.torch-enable'):
txt = torchEnable['enabled'].replace('\\','\n')
rE(f"{fbuttons}.torch-enable configure -fg {colorFore} -bg {colorActive} \
-activebackground {colorActive} -text {{{txt}}}")
else:
txt = torchEnable['disabled'].replace('\\','\n')
rE(f"{fbuttons}.torch-enable configure -fg {colorFore} -bg {colorWarn} \
-activebackground {colorWarn} -text {{{txt}}}")
def get_all_children(parent):
raw = rE(f"winfo children {parent}")
children = raw.split()
for item in children:
if rE(f"winfo children {item}"):
raw = (rE(f"winfo children {item}"))
for item in raw.split():
children.append(item)
return(children)
def color_change():
# menu colors
rE(f"option add *Menu.Background {colorBack}")
rE(f"option add *Menu.Foreground {colorFore}")
widgetTypes = []
for child in get_all_children('.'):
if child in [f"{fbuttons}.torch-enable"]:
continue
w = rE(f"winfo class {child}")
# root window
rE(f". configure -bg {colorBack}")
# all widgets
try:
rE(f"{child} configure -fg {colorFore}")
except:
pass
try:
rE(f"{child} configure -bg {colorBack}")
except:
pass
try:
rE(f"{child} configure -disabledforeground {colorDisable}")
except:
pass
try:
rE(f"{child} configure -activebackground {colorBack}")
except:
pass
try:
rE(f"{child} configure -buttonbackground {colorBack}")
except:
pass
try:
rE(f"{child} configure -highlightthickness 0")
except:
pass
try:
rE(f"{child} configure -highlightbackground {colorBack}")
except:
pass
try:
if w == 'Menu':
rE(f"{child} configure -selectcolor {colorFore}")
else:
rE(f"{child} configure -selectcolor {colorActive}")
except:
pass
try:
# color the trough of override scales and user button scrollbars
rE(f"{child} configure -troughcolor {colorTrough}")
# color the trough of combobox lists
rE(f"option add *Scrollbar.troughColor {colorTrough}")
rE(f"option add *Scrollbar.background {colorBack}")
rE(f"option add *Scrollbar.activeBackground {colorBack}")
except:
pass
# all comboboxes except for the jog increment Combobox
if w == 'ComboBox':
rE(f"{child} configure -background {colorBack}")
rE(f"{child} configure -selectbackground {colorBack}")
rE(f"{child} configure -selectforeground {colorFore}")
# lose the arrow
rE(f"pack forget {child}.a")
# the entry of the jog increment combobox
elif '.jogincr' in child and w == 'Entry':
rE(f"{child} configure -disabledforeground {colorFore}")
# the listbox of the jog increment combobox
elif '.jogincr' in child and w == 'Listbox':
rE(f"{child} configure -selectforeground {colorFore}")
rE(f"{child} configure -selectbackground {colorBack}")
# all checkbuttons
elif w in ['Checkbutton']:
rE(f"{child} configure -relief raised -overrelief raised -bd 1")
# notebook tabs - cutrecs is also done each time it is raised
for nbook in [ftabs, fright]:
pages = rE(f"{nbook} pages").split()
for page in pages:
rE(f"{nbook} itemconfigure {page} -foreground {colorFore} -background {colorBack}")
# leds
for led in ['arc-ok','torch','breakaway','thc-enabled','thc-active','ohmic','float','up','down','corner-locked','void-locked']:
rE(f"{fleds}.led-{led} itemconfigure all -disabledfill {colorBack}")
for led in ['breakaway','void-locked','corner-locked']:
rE(f"{fleds}.led-{led} itemconfigure all -fill {colorWarn}")
for led in ['thc-enabled','thc-active']:
rE(f"{fleds}.led-{led} itemconfigure all -fill {colorActive}")
for led in ['ohmic','float','up','down']:
rE(f"{fleds}.led-{led} itemconfigure all -fill {colorLed}")
rE(f"{fleds}.led-arc-ok itemconfigure all -fill {colorArc}")
rE(f"{fleds}.led-torch itemconfigure all -fill {colorLed}")
# arc voltage
rE(f"{fplasma}.arc-voltage configure -fg {colorVolt}")
color_user_buttons()
color_torch()
# the color setup buttons
rE(f"{fsetup}.m.colors.fore configure -bg {colorFore} -activebackground {colorFore}")
rE(f"{fsetup}.m.colors.back configure -bg {colorBack} -activebackground {colorBack}")
rE(f"{fsetup}.m.colors.disable configure -bg {colorDisable} -activebackground {colorDisable}")
rE(f"{fsetup}.m.colors.active configure -bg {colorActive} -activebackground {colorActive}")
rE(f"{fsetup}.m.colors.warn configure -bg {colorWarn} -activebackground {colorWarn}")
rE(f"{fsetup}.m.colors.volt configure -bg {colorVolt} -activebackground {colorVolt}")
rE(f"{fsetup}.m.colors.arc configure -bg {colorArc} -activebackground {colorArc}")
rE(f"{fsetup}.m.colors.led configure -bg {colorLed} -activebackground {colorLed}")
rE(f"{fsetup}.m.colors.trough configure -bg {colorTrough} -activebackground {colorTrough}")
# notifications
rE(f"option add *!notification2.Frame.Background {colorBack}")
rE(f"option add *!notification2.Frame.Label.Foreground {colorFore}")
rE(f"option add *!notification2.Frame.Label.Background {colorBack}")
rE(f"option add *!notification2.Frame.Button.Background {colorBack}")
rE(f"option add *!notification2.Frame.Button.activeBackground {colorBack}")
rE(f"option add *!notification2.Frame.Button.highlightThickness 0")
# tags in gcode text
t.tag_configure('ignored', background='#ffffff', foreground='#808080')
t.tag_configure('lineno', foreground=colorTrough)
t.tag_configure('executing', background=colorTrough)
t.tag_configure('sel', background='red', foreground='blue')
def color_set(option):
global colorFore, colorBack, colorDisable, colorActive, colorWarn, colorVolt, colorArc, colorLed, colorTrough
if option == 'fore':
color = colorFore
elif option == 'back':
color = colorBack
elif option == 'disable':
color = colorDisable
elif option == 'active':
color = colorActive
elif option == 'warn':
color = colorWarn
elif option == 'volt':
color = colorVolt
elif option == 'arc':
color = colorArc
elif option == 'led':
color = colorLed
elif option == 'trough':
color = colorTrough
else:
color = '#000000'
colors = askcolor(color, title=_('plasmac2 Color Selector'))
if colors[1]:
if option == 'fore':
colorFore = colors[1]
elif option == 'back':
colorBack = colors[1]
elif option == 'disable':
colorDisable = colors[1]
elif option == 'active':
colorActive = colors[1]
elif option == 'warn':
colorWarn = colors[1]
elif option == 'volt':
colorVolt = colors[1]
elif option == 'arc':
colorArc = colors[1]
elif option == 'led':
colorLed = colors[1]
elif option == 'trough':
colorTrough = colors[1]
color_change()
##############################################################################
# ORIENTATION FUNCTIONS #
##############################################################################
def set_orientation():
global orientStart
orientStart = True
matNum = int(rE(f"{fruns}.material.materials get").split(': ', 1)[0])
pmxText = rE(f"{fruns}.pmx.info cget -text")
set_orient_frames()
recreate_widget_list()
user_button_setup()
populate_settings_frame()
for box in rSpinBoxes:
rE(f"{box} configure -font fontGui")
color_change()
set_window_size()
load_materials(get_displayed_material(), keepTemp=True)
index = f"@{materialNumList.index(matNum)}"
rE(f"{fruns}.material.materials setvalue {index}")
display_selected_material(matNum)
rE(f"{fruns}.pmx.info configure -text {{{pmxText}}}")
orientStart = False
def set_orient_frames():
global fportrait, fruns, fbuttons, toolmat
if not firstRun:
rE(f"destroy {toolmat}")
rE(f"destroy {fruns}")
rE(f"destroy {fbuttons}")
rE(f"destroy {fportrait}")
rE(f"destroy {fruns}.material.materials")
fportrait = '.fportrait'
if pVars.orient.get() == 'portrait':
fruns = '.fportrait.fruns'
fbuttons = '.fportrait.fbuttons'
toolmat = f"{fruns}.toolmat"
rE(f"frame {fportrait} -relief raised -borderwidth 2")
else:
fruns = '.fruns'
fbuttons = '.fbuttons'
toolmat = '.toolmat'
rE(f"frame {fbuttons} -relief flat")
rE(f"frame {fruns} -relief flat")
make_torch_button()
make_dry_run_button()
make_toolmat_frame()
make_run_panel()
def make_torch_button():
rE(f"button {fbuttons}.torch-enable -takefocus 0 -width 10 -highlightthickness 0")
if '\\' in torchEnable['enabled'] or '\\' in torchEnable['disabled']:
rE(f"{fbuttons}.torch-enable configure -height 2")
else:
rE(f"{fbuttons}.torch-enable configure -height 1")
rE(f"grid {fbuttons}.torch-enable -column 0 -row 0 -sticky new -padx {{2 0}} -pady {{2 0}}")
rE(f"bind {fbuttons}.torch-enable <ButtonPress-1> torch_enable")
def make_dry_run_button():
if pVars.orient.get() == 'portrait':
col = 1
row = 0
else:
col = 0
row = 1
rE(f"button {fbuttons}.dry-run -takefocus 0 -width 10 -highlightthickness 0 -text {{Dry Run}} -command {{task_run dry-run}}")
rE(f"grid {fbuttons}.dry-run -column {col} -row {row} -sticky new -padx {{2 0}} -pady {{2 0}}")
def make_toolmat_frame():
rE(f"frame {toolmat} -borderwidth 1 -relief raised")
rE(f"Button {toolmat}.delete -command {{delete_material_clicked}} -relief link -takefocus 0")
rE(f"Button {toolmat}.new -command {{new_material_clicked}} -relief link -takefocus 0")
rE(f"Button {toolmat}.reload -command {{reload_material_clicked}} -relief link -takefocus 0")
rE(f"Button {toolmat}.save -command {{save_material_clicked}} -relief link -takefocus 0")
rE(f"{toolmat}.delete configure -image {rE(f'load_image {imageAxis}/tool_zoomout')}")
rE(f"{toolmat}.new configure -image {rE(f'load_image {imageAxis}/tool_zoomin')}")
rE(f"{toolmat}.reload configure -image {rE(f'load_image {imageAxis}/tool_reload')}")
rE(f"{toolmat}.save configure -image {rE(f'load_image {imagePath}/save')}")
rE(f"pack {toolmat}.save -side left -padx {{0 4}}")
rE(f"pack {toolmat}.reload -side left")
rE(f"pack {toolmat}.delete -side right -padx {{4 0}}")
rE(f"pack {toolmat}.new -side right")
def make_run_panel():
global rpList, rSpinBoxes
rSpinBoxes = []
rE(f"labelframe {fruns}.material -text {{{_('Material')}}} -relief groove")
rE(f"ComboBox {fruns}.material.materials -modifycmd {{material_changed}} -textvariable matCurrent -width 10 -editable 0")
rE(f"grid {fruns}.material.materials -column 0 -row 0 -columnspan 2 -sticky ew -padx 2 -pady 2")
# spinboxes [parent, name, decimals, value, min, max, increment, label text]
rpList = [[f"{fruns}.material",'kerf-width',2,0.5,0,5,0.01,'Kerf Width'], \
[f"{fruns}.material",'pierce-height',2,4,0,25,0.01,'Pierce Height'], \
[f"{fruns}.material",'pierce-delay',1,0.0,0,10,0.1,'Pierce Delay'], \
[f"{fruns}.material",'cut-height',2,1,0,25,0.01,'Cut Height'], \
[f"{fruns}.material",'cut-feed-rate',0,4000,0,19999,1,'Feed Rate'], \
[f"{fruns}.material",'cut-amps',0,45,0,999,1,'Cut Amps'], \
[f"{fruns}.material",'cut-volts',0,122,50,300,1,'Cut Volts'], \
[f"{fruns}.material",'puddle-jump-height',0,0,0,200,1,'P-Jump Height'], \
[f"{fruns}.material",'puddle-jump-delay',2,0,0,9,0.01,'P-Jump Delay'], \
[f"{fruns}.material",'pause-at-end',1,0,0,9,0.1,'Pause At End'], \
[f"{fruns}.material",'cut-mode',0,1,1,3,1,'Cut Mode'], \
[f"{fruns}.material",'gas-pressure',1,0,0,150,0.1,'Gas Pressure'], \
]
rpRow = 1
for rpItem in rpList:
if s.linear_units != 1:
if rpItem[1] == 'kerf-width':
rpItem[2:7] = [4,0.02,0,1,0.0001]
elif rpItem[1] == 'pierce-height':
rpItem[2:7] = [3,0.16,0,1,0.001]
elif rpItem[1] == 'cut-height':
rpItem[2:7] = [3,0.04,0,1,0.001]
elif rpItem[1] == 'cut-feed-rate':
rpItem[2:7] = [0,160,0,999,0.1]
rpName = f"{rpItem[0]}.{rpItem[1]}"
rpType = 'flt' if rpItem[2] > 0 else 'int'
rE(f"spinbox {rpName} -width 9 -justify right -wrap true")
rSpinBoxes.append(rpName)
rE(f"{rpName} configure -from {rpItem[4]} -to {rpItem[5]}")
if rpItem[1] == 'kerf-width':
rE(f"{rpName} configure -textvariable kerfWidth")
elif rpItem[1] == 'cut-amps':
rE(f"{rpName} configure -textvariable cutAmps")
elif rpItem[1] == 'cut-mode':
rE(f"{rpName} configure -textvariable cutMode")
elif rpItem[1] == 'gas-pressure':
rE(f"{rpName} configure -textvariable gasPressure")
rE(f"{rpName} configure -increment {rpItem[6]} -format %0.{rpItem[2]}f")
rE(f"{rpName} configure -validate key -vcmd {{{valspin} %W {rpType} {rpItem[2]} %P %s}}")
rE(f"label {rpName}L -text {{{rpItem[7]}}}")
if rpItem[1] not in ['cut-mode', 'gas-pressure']:
rE(f"grid {rpName} -column 0 -row {rpRow} -padx {{2 0}} -pady {{0 2}}")
rE(f"grid {rpName}L -column 1 -row {rpRow} -sticky W -pady {{0 2}}")
rpRow += 1
# checkbox frame
rE(f"labelframe {fruns}.check -text {{{_('Cut')}}} -relief groove")
rE(f"checkbutton {fruns}.check.av -variable autoVolts -command auto_volts_toggled -width 2 -anchor w -indicatoron 0")
rE(f"label {fruns}.check.avL -text {{{_('Use Auto Volts')}}}")
rE(f"checkbutton {fruns}.check.ohmic -variable ohmicEnable -command ohmic_enable_toggled -width 2 -anchor w -indicatoron 0")
rE(f"label {fruns}.check.ohmicL -text {{{_('Ohmic Probe Enable')}}}")
rE(f"checkbutton {fruns}.check.mesh -variable meshEnable -command pmx485_mesh_enable_toggled -width 2 -anchor w -indicatoron 0")
rE(f"label {fruns}.check.meshL -text {{{_('Mesh Mode')}}}")
rE(f"checkbutton {fruns}.check.ignoreok -variable ignorArcOk -command ignore_arc_ok_toggled -width 2 -anchor w -indicatoron 0")
rE(f"label {fruns}.check.ignoreokL -text {{{_('Ignore Arc OK')}}}")
rE(f"grid {fruns}.check.av -column 0 -row 0 -sticky w -padx {{2 0}} -pady {{0 2}}")
rE(f"grid {fruns}.check.avL -column 1 -row 0 -sticky w -pady {{0 2}}")
rE(f"grid {fruns}.check.ohmic -column 0 -row 1 -sticky w -padx {{2 0}} -pady {{0 2}}")
rE(f"grid {fruns}.check.ohmicL -column 1 -row 1 -sticky w -pady {{0 2}}")
rE(f"grid {fruns}.check.mesh -column 0 -row 2 -sticky w -padx {{2 0}} -pady {{0 2}}")
rE(f"grid {fruns}.check.meshL -column 1 -row 2 -sticky w -pady {{0 2}}")
rE(f"grid {fruns}.check.ignoreok -column 0 -row 3 -sticky w -padx {{2 0}} -pady {{0 2}}")
rE(f"grid {fruns}.check.ignoreokL -column 1 -row 3 -sticky w -pady {{0 2}}")
# pmx frame
rE(f"labelframe {fruns}.pmx -text {{{_('Powermax')}}} -relief groove")
rE(f"checkbutton {fruns}.pmx.enable -text {{{_('Powermax Comms')}}} -variable pmx485Enable -command pmx485_enable_toggled -width 2 -anchor w -indicatoron 0")
rE(f"label {fruns}.pmx.info -anchor w")
rE(f"pack {fruns}.pmx.enable -expand 1 -fill x")
rE(f"pack {fruns}.pmx.info -expand 1 -fill x")
def populate_run_panel():
if pVars.orient.get() == 'portrait':
rE(f"grid {fportrait} -column 1 -row 3 -sticky nsew -padx 2 -pady {{0 2}}")
rE(f"grid {fbuttons} -column 0 -row 0 -sticky nsew -padx 0 -pady 0")
rE(f"grid {fruns} -column 2 -row 0 -sticky nsew -padx 1 -pady 1")
rE(f"grid {fruns}.check -column 0 -row 1 -sticky new -padx 2 -pady {{0 2}}")
if pmPort:
rE(f"grid {fruns}.pmx -column 0 -row 2 -sticky new -padx 2 -pady {{0 2}}")
rE(f"grid toolmat -column 2 -row 0 -sticky new -padx 2 -pady 2")
rE(f"grid {fruns}.material -column 2 -row 1 -rowspan 99 -sticky new -padx 2 -pady {{0 2}}")
rE(f"grid columnconfigure {fruns} 1 -weight 1")
rE(f"grid columnconfigure {fportrait} 1 -weight 1")
rE(f"grid columnconfigure {fportrait} 2 -weight 1")
else:
rE(f"grid {fbuttons} -column 0 -row 1 -rowspan 2 -sticky nsew -padx 0 -pady 0")
rE(f"grid {toolmat} -column 3 -row 0 -sticky nesw")
rE(f"grid {fruns}.material -column 0 -row 1 -sticky new -padx 2 -pady {{0 2}}")
rE(f"grid {fruns}.check -column 0 -row 2 -sticky new -padx 2 -pady {{0 2}}")
if pmPort:
rE(f"grid {fruns}.pmx -column 0 -row 3 -sticky new -padx 2 -pady {{0 2}}")
rE(f"grid {fruns} -column 3 -row 1 -rowspan 2 -sticky nsew -padx 1 -pady 1")
if pmPort:
rE(f"grid {fruns}.material.cut-mode -column 0 -row 20 -padx {{2 0}} -pady {{0 2}}")
rE(f"grid {fruns}.material.cut-modeL -column 1 -row 20 -sticky W -pady {{0 2}}")
rE(f"grid {fruns}.material.gas-pressure -column 0 -row 21 -padx {{2 0}} -pady {{0 2}}")
rE(f"grid {fruns}.material.gas-pressureL -column 1 -row 21 -sticky W -pady {{0 2}}")
def populate_settings_frame():
rE(f"grid forget {fsetup}.l")
rE(f"grid forget {fsetup}.m")
rE(f"grid forget {fsetup}.r")
if pVars.orient.get() == 'portrait':
rE(f"grid {fsetup}.l -column 0 -row 0 -sticky nw -padx {{4 0}} -pady {{4 4}}")
rE(f"grid {fsetup}.m -column 2 -row 0 -sticky nw -padx {{4 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.r -column 0 -row 1 -columnspan 3 -sticky nsew -padx {{0 4}} -pady {{4 4}}")
rE(f"grid columnconfigure {fsetup} 0 -weight 0")
rE(f"grid columnconfigure {fsetup} 1 -weight 1")
rE(f"grid columnconfigure {fsetup} 2 -weight 0")
rE(f"grid columnconfigure {fsetup} 3 -weight 0")
rE(f"grid columnconfigure {fsetup} 4 -weight 0")
rE(f"grid rowconfigure {fsetup} 0 -weight 0")
rE(f"grid rowconfigure {fsetup} 1 -weight 1")
else:
rE(f"grid {fsetup}.l -column 0 -row 0 -sticky nw -padx {{4 0}} -pady {{4 4}}")
rE(f"grid {fsetup}.m -column 2 -row 0 -sticky nw -padx {{4 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.r -column 4 -row 0 -sticky nsew -padx {{0 4}} -pady {{4 4}}")
rE(f"grid columnconfigure {fsetup} 0 -weight 0")
rE(f"grid columnconfigure {fsetup} 1 -weight 1")
rE(f"grid columnconfigure {fsetup} 2 -weight 0")
rE(f"grid columnconfigure {fsetup} 3 -weight 1")
rE(f"grid columnconfigure {fsetup} 4 -weight 9")
rE(f"grid rowconfigure {fsetup} 0 -weight 1")
rE(f"grid rowconfigure {fsetup} 1 -weight 0")
def populate_user_buttons():
if pVars.orient.get() == 'portrait':
col = 0
row = 1
else:
col = 0
row = 2
width = 0
for n in range(1, maxUserButtons + 1):
rE(f"grid forget {fbuttons}.button{n}")
if buttonNames[n]['name'] or buttonCodes[n]['code']:
rE(f"grid {fbuttons}.button{n} -column {col} -row {row} -sticky new -padx {{2 0}} -pady {{2 0}}")
if pVars.orient.get() == 'portrait':
col += 1
if col == 2:
col = 0
row += 1
else:
row += 1
if len(buttonNames[n]['name']) > width:
width = len(buttonNames[n]['name'])
width = width if width > 10 else 10
for n in range(1, maxUserButtons + 1):
rE(f"{fbuttons}.button{n} configure -width {int(width)}")
rE(f"{fsetup}.r.ubuttons.canvas.frame.name{n} configure -width {width + 2}")
def recreate_widget_list():
global widgets
# recreate widget list to move active gcodes and add new widgets
widget_list_new = []
for l in widget_list:
if '.gcodes' in l[2]:
l = ('code_text', Text, '.info.gcodef.gcodes')
widget_list_new.append(l)
widget_list_new.append(('buttonFrame', Frame, fbuttons))
widget_list_new.append(('convFrame', Frame, '.fconv'))
widget_list_new.append(('toolFrame', Frame, '.toolconv'))
widget_list_new.append(('materials', bwidget.ComboBox, f"{fruns}.material.materials"))
widget_list_new.append(('kerfWidth', Spinbox, f"{fruns}.material.kerf-width"))
widget_list_new.append(('view_t', Button, '.toolbar.view_t'))
widgets = nf.Widgets(root_window, *widget_list_new)
##############################################################################
# SETUP #
##############################################################################
firstRun = 'valid'
orientStart = False
configPath = os.getcwd()
p2Path = os.path.join(configPath, 'plasmac2')
if os.path.isdir(os.path.join(p2Path, 'lib')):
extHalPins = {}
import sys
libPath = os.path.join(p2Path, 'lib')
sys.path.append(libPath)
import tarfile
from tkinter.colorchooser import askcolor
from collections import OrderedDict
from glob import glob as GLOB
from shutil import copy as COPY
from shutil import which as WHICH
from subprocess import Popen, PIPE
from importlib import reload
from plasmac import run_from_line as RFL
''' comment after next line:
suppresses unresolved module error in VSCode '''
import conversational # type: ignore
import webbrowser
imagePath = os.path.join(libPath, 'images') # our own images
imageAxis = os.path.join(BASE, 'share', 'axis', 'images') # images pinched from Axis
if not os.path.isdir('/tmp/plasmac'):
os.mkdir('/tmp/plasmac')
tmpPath = '/tmp/plasmac'
installType = 'pkg' if os.path.dirname(__file__) == '/bin' else 'rip'
PREF = plasmacPreferences()
MATS = plasmacMaterials()
TEMP = plasmacTempMaterial()
# for calling tcl code
rE = root_window.tk.eval
# set the app icon
icon = PhotoImage(file=f"{imagePath}/chips_plasma.png")
rE(f"wm iconphoto . {icon}")
# set the version
try:
with open(os.path.join(p2Path, 'versions.html'), 'r') as inFile:
for line in inFile:
if 'v2.' in line:
VER = line.split('v')[1].split('<')[0]
break
except:
VER = '0'
putPrefs(PREF, 'GUI_OPTIONS', 'Version', VER, str)
# tk widget variables
pVars = nf.Variables(root_window,
('plasmatool', StringVar),('thcAuto', BooleanVar), ('thcEnable', BooleanVar),
('cornerEnable', BooleanVar), ('voidEnable', BooleanVar), ('autoVolts', BooleanVar),
('ohmicEnable', BooleanVar), ('meshEnable', BooleanVar), ('ignorArcOk', BooleanVar),
('closeText', StringVar), ('closeDialog', BooleanVar), ('kbShortcuts', BooleanVar),
('useVirtKB', BooleanVar), ('winSize', StringVar), ('orient', StringVar),
('startLine', IntVar), ('rflActive', BooleanVar), ('preRflFile', StringVar),
('laserText', StringVar), ('crSpeed', IntVar), ('previewLarge', BooleanVar),
('jogSpeed', DoubleVar), ('plasmacMode', IntVar), ('fontSize', StringVar),
('guiFont', StringVar), ('codeFont', StringVar), ('coneSize', DoubleVar),
('popLocation', StringVar), ('kerfWidth', DoubleVar), ('cutAmps', IntVar),
('cutMode', IntVar), ('gasPressure', DoubleVar), ('pmx485Enable', BooleanVar),
('preConvFile', IntVar),('matDefault', IntVar), ('jogInhibitOvr', IntVar),
('lengthJ', StringVar), ('lengthT', StringVar), ('lengthS', DoubleVar), ('runJ', StringVar),
('pierceJ', StringVar), ('pierceT', StringVar), ('pierceS', IntVar), ('cutS', IntVar),
('rapidJ', StringVar), ('rapidT', StringVar), ('rapidS', IntVar), ('probeJ', StringVar),
('probeT', StringVar),('probeS', IntVar), ('torchJ', StringVar), ('torchT', StringVar),
('torchS', IntVar), ('cutJ', StringVar),('cutT', StringVar), ('pausedJ', StringVar),
('pausedT', StringVar), ('pausedS', IntVar), ('runT', StringVar), ('runS', IntVar),
('probeJ', StringVar), ('probeT', StringVar), ('probeS', IntVar), ('arcT', StringVar),
('jogMultiplier', DoubleVar))
restoreSetup = {}
pVars.plasmacMode.set(getPrefs(PREF, 'GUI_OPTIONS', 'Mode', 0, int))
restoreSetup['plasmacMode'] = pVars.plasmacMode.get()
pVars.winSize.set(getPrefs(PREF, 'GUI_OPTIONS', 'Window size', 'default', str).lower().replace(' ',''))
restoreSetup['winSize'] = pVars.winSize.get()
restoreSetup['winLast'] = getPrefs(PREF, 'GUI_OPTIONS', 'Window last', '932x562', str).split('+', 1)[0]
pVars.orient.set(getPrefs(PREF, 'GUI_OPTIONS', 'Orientation', 'landscape', str).lower().replace(' ',''))
restoreSetup['orient'] = pVars.orient.get()
pVars.fontSize.set(getPrefs(PREF, 'GUI_OPTIONS','Font size', '10', str))
restoreSetup['fontSize'] = pVars.fontSize.get()
pVars.guiFont.set(getPrefs(PREF, 'GUI_OPTIONS','GUI font', 'Sans', str))
restoreSetup['guiFont'] = pVars.guiFont.get()
pVars.codeFont.set(getPrefs(PREF, 'GUI_OPTIONS','Gcode font', 'Sans', str))
restoreSetup['codeFont'] = pVars.codeFont.get()
# make some widget names to save typing
ftop = '.pane.top'
ftabs = f"{ftop}.tabs"
fright = f"{ftop}.right"
stats = f"{fright}.fstats.statistics"
fmanual = f"{ftabs}.fmanual"
faxes = f"{fmanual}.axes"
fjoints = f"{fmanual}.joints"
fjogf = f"{fmanual}.jogf"
foverride = f"{fmanual}.override"
flimitovr = f"{fmanual}.jogf.limitovr"
fjogiovr = f"{fmanual}.jogf.inhibitovr"
fplasma = f"{fmanual}.plasma"
farcv = f"{fplasma}.arcv"
fthc = f"{fplasma}.thc"
fmdi = f"{ftabs}.fmdi"
ft = '.pane.bottom.t'
fleds = '.pane.bottom.leds'
fcutrecs = f"{ftabs}.fcutrecs"
fcrspeed = f"{fcutrecs}.pm"
fcrbuttons = f"{fcutrecs}.buttons"
fcrleadin = f"{fcutrecs}.leadins"
fparam = '.param'
fsetup = '.setup'
# spinbox validator
valspin = root_window.register(validate_spinbox)
torchEnable = {}
torchEnable['enabled'] = getPrefs(PREF, 'BUTTONS', 'Torch enabled', 'Torch Enabled', str)
torchEnable['disabled'] = getPrefs(PREF, 'BUTTONS','Torch disabled', 'Torch Disabled', str)
pmPort = getPrefs(PREF, 'POWERMAX', 'Port', '', str)
set_orient_frames()
recreate_widget_list()
wLeds = {f"{fleds}.led-arc-ok": [_('Arc OK'), 8], \
f"{fleds}.led-torch": [_('Torch On'), 8], \
f"{fleds}.led-breakaway": [_('Break'), 8], \
f"{fleds}.led-thc-enabled": [_('THC Enabled'), 11], \
f"{fleds}.led-thc-active": [_('THC Active'), 11], \
f"{fleds}.led-ohmic": [_('Ohmic Probe'), 11], \
f"{fleds}.led-float": [_('Float Switch'), 11], \
f"{fleds}.led-up": [_('Up'), 8], \
f"{fleds}.led-down": [_('Down'), 8], \
f"{fleds}.led-corner-locked": [_('Vel Lock'), 8], \
f"{fleds}.led-void-locked": [_('Void Lock'), 8], \
}
read_colors()
thcFeedRate = round((float(inifile.find('AXIS_Z', 'MAX_VELOCITY')) * float(inifile.find('AXIS_Z', 'OFFSET_AV_RATIO'))) * 60, 3)
offsetFeedRate = min(float(inifile.find('AXIS_X', 'MAX_VELOCITY')) * 30, \
float(inifile.find('AXIS_Y', 'MAX_VELOCITY')) * 30, \
float(inifile.find('TRAJ', 'MAX_LINEAR_VELOCITYs') or 100000))
maxHeight = round(hal.get_value('ini.z.max_limit') - hal.get_value('ini.z.min_limit'), 3)
unitsPerMm = hal.get_value('halui.machine.units-per-mm')
isIdle = False
isIdleHomed = False
isPaused = False
isRunning = False
homeInProgress = False
getMaterialBusy = False
materialFileDict = {}
materialNumList = []
materialAutoChange = False
probePressed = False
probeTimer = 0
probeButton = ''
torchPressed = False
torchTimer = 0
torchButton = ''
torchHeight = 0
cChangeButton = ''
cutType = 0
togglePins = {}
pulsePins = {}
dualCodes = {}
currentTool = None
manualCut = {'state':False, 'feed':vars.jog_speed.get()}
singleCut = {'state':False, 'G91':False}
framingState = False
runState = False
dryRun = None
lastMotionMode = None
laserTimer = 0.0
laserButtonState = 'laser'
laserOffsets = {}
laserOffsets['X'] = getPrefs(PREF, 'LASER_OFFSET', 'X axis', 0, float)
laserOffsets['Y'] = getPrefs(PREF, 'LASER_OFFSET', 'Y axis', 0, float)
probeOffsets = {}
probeOffsets['X'] = getPrefs(PREF, 'OFFSET_PROBING', 'X axis', 0, float)
probeOffsets['Y'] = getPrefs(PREF, 'OFFSET_PROBING', 'Y axis', 0, float)
probeOffsets['Delay'] = getPrefs(PREF, 'OFFSET_PROBING', 'Delay', 0, float)
machineBounds = {}
machineBounds['X-'] = round(float(inifile.find('AXIS_X', 'MIN_LIMIT')), 6)
machineBounds['X+'] = round(float(inifile.find('AXIS_X', 'MAX_LIMIT')), 6)
machineBounds['Y-'] = round(float(inifile.find('AXIS_Y', 'MIN_LIMIT')), 6)
machineBounds['Y+'] = round(float(inifile.find('AXIS_Y', 'MAX_LIMIT')), 6)
machineBounds['Z-'] = round(float(inifile.find('AXIS_Z', 'MIN_LIMIT')), 6)
machineBounds['Z+'] = round(float(inifile.find('AXIS_Z', 'MAX_LIMIT')), 6)
machineBounds['xLen'] = machineBounds['X+'] - machineBounds['X-']
machineBounds['yLen'] = machineBounds['Y+'] - machineBounds['Y-']
machineBounds['zLen'] = machineBounds['Z+'] - machineBounds['Z-']
startAlignPos = {X:0, Y:0}
relPos = {X:0, Y:0}
activeFunction = False
pausedState = False
convFirstRun = True
convViewOptions = {}
lastViewType = None
preConvFile = os.path.join(tmpPath, 'pre_conv.ngc')
keyDelay = {}
pmx485 = {'exists':False, 'meshMode':False}
spinBoxes = []
widgetValues = {}
statValues = {'length':0, 'pierce':0, 'rapid':0, 'probe':0, 'torch':0, 'cut':0, 'paused':0, 'run':0}
toolButtons = ['machine_estop','machine_power','file_open','reload','program_run',
'program_step','program_pause','program_stop','program_blockdelete',
'program_optpause','view_zoomin','view_zoomout','view_z','view_z2',
'view_x','view_y','view_y2','view_p','view_t','rotate','clear_plot']
matButtons = ['delete','new','reload','save']
maxUserButtons = 20
# allow right-click to select start from line
o.bind('<Button-3>', rClicker, '+')
# hijacked functions from axis.py
# vupdate
# get_coordinate_font
# install_help
# prompt_touchoff
# monkeypatched functions from axis.py
o.get_view = get_view
filter_program_old = filter_program
filter_program = filter_program_new
# monkeypatched functions from glcanon.py
o.draw_grid = draw_grid
o.posstrs = posstrs
# tcl called python functions hijacked from axis.py
TclCommands.reload_file = reload_file
TclCommands.task_run = task_run
TclCommands.task_run_line = task_run_line
TclCommands.open_file_name = open_file_name
TclCommands.set_view_p = set_view_p
TclCommands.set_view_z = set_view_z
TclCommands.get_jog_speed = get_jog_speed
TclCommands.send_mdi_command = send_mdi_command
# tcl functions hijacked from axis.tcl
TclCommands.update_title = update_title
TclCommands.update_jog_slider_vel = update_jog_slider_vel
TclCommands.set_pane_minsize = set_pane_minsize
# new functions
TclCommands.set_view_t = set_view_t
TclCommands.param_changed = param_changed
TclCommands.material_changed = material_changed
TclCommands.save_material_clicked = save_material_clicked
TclCommands.reload_material_clicked = reload_material_clicked
TclCommands.new_material_clicked = new_material_clicked
TclCommands.delete_material_clicked = delete_material_clicked
TclCommands.change_default_material = change_default_material
TclCommands.save_param_clicked = save_param_clicked
TclCommands.load_param_clicked = load_param_clicked
TclCommands.backup_clicked = backup_clicked
TclCommands.save_setup_clicked = save_setup_clicked
TclCommands.load_setup_clicked = load_setup_clicked
TclCommands.clear_program = clear_program
TclCommands.preview_toggle = preview_toggle
TclCommands.setup_toggle = setup_toggle
TclCommands.param_toggle = param_toggle
TclCommands.conv_toggle = conv_toggle
TclCommands.thc_enable_toggled = thc_enable_toggled
TclCommands.corner_enable_toggled = corner_enable_toggled
TclCommands.void_enable_toggled = void_enable_toggled
TclCommands.auto_volts_toggled = auto_volts_toggled
TclCommands.ohmic_enable_toggled = ohmic_enable_toggled
TclCommands.ignore_arc_ok_toggled = ignore_arc_ok_toggled
TclCommands.close_window = close_window
TclCommands.set_window_size = set_window_size
TclCommands.set_orientation = set_orientation
TclCommands.button_action = button_action
TclCommands.mode_changed = mode_changed
TclCommands.font_changed = font_changed
TclCommands.cone_size_changed = cone_size_changed
TclCommands.height_raise = height_raise
TclCommands.height_lower = height_lower
TclCommands.height_reset = height_reset
TclCommands.torch_enable = torch_enable
TclCommands.joint_mode_switch = joint_mode_switch
TclCommands.ja_button_activated = ja_button_activated
TclCommands.manual_cut = manual_cut
TclCommands.touch_off_xy = touch_off_xy
TclCommands.laser_button_toggled = laser_button_toggled
TclCommands.cut_rec_motion = cut_rec_motion
TclCommands.cut_rec_default_changed = cut_rec_default_changed
TclCommands.cut_rec_slider_changed = cut_rec_slider_changed
TclCommands.cut_rec_move = cut_rec_move
TclCommands.cut_rec_cancel = cut_rec_cancel
TclCommands.pmx485_mode_changed = pmx485_mode_changed
TclCommands.pmx485_pressure_changed = pmx485_pressure_changed
TclCommands.pmx485_enable_toggled = pmx485_enable_toggled
TclCommands.pmx485_mesh_enable_toggled = pmx485_mesh_enable_toggled
TclCommands.reset_all_stats = reset_all_stats
TclCommands.set_peripheral_offsets = set_peripheral_offsets
TclCommands.kb_shortcuts_changed = kb_shortcuts_changed
TclCommands.virtual_kb_changed = virtual_kb_changed
TclCommands.key_pressed = key_pressed
TclCommands.key_released = key_released
TclCommands.color_set = color_set
TclCommands.user_manual = user_manual
commands = TclCommands(root_window)
# set up the fonts
lastFontSize = pVars.fontSize.get()
fontsCode = []
fontsGui = []
for font in tuple_from_tcl_string(rE('font families')):
if 'caps' in font.lower() or 'slanted' in font.lower() or 'light' in font.lower() \
or 'dingbat' in font.lower() or 'symbols' in font.lower():
pass
else:
if 'mono' in font.lower() or 'courier' in font.lower():
if font not in fontsCode:
fontsCode.append(font)
else:
if font not in fontsGui:
fontsGui.append(font)
if pVars.guiFont.get() not in fontsCode:
fontsCode.append(pVars.guiFont.get())
if pVars.guiFont.get() not in fontsGui:
fontsGui.append(pVars.guiFont.get())
rE(f"font create fontGui -family {{{pVars.guiFont.get()}}} -size {pVars.fontSize.get()}")
rE(f"font create fontArc -family {{{pVars.guiFont.get()}}} -size {int(pVars.fontSize.get())*3}")
rE(f"font create fontCode -family {{{pVars.codeFont.get()}}} -size {pVars.fontSize.get()}")
update_check()
##############################################################################
# GUI ALTERATIONS AND ADDITIONS #
##############################################################################
# keep tab label sizes the same
rE(f"{ftabs} configure -homogeneous True")
rE(f"{fright} configure -homogeneous True")
# reduce margins on manual/mdi tabs
rE(f"{ftabs} configure -internalborderwidth 1")
rE(f"grid columnconfigure {fmanual} 0 -weight 1")
rE(f"grid columnconfigure {fmanual} 99 -weight 0")
# reduce margins on preview tabs
rE(f"{fright} configure -internalborderwidth 1")
# hide some existing widgets
rE('pack forget .toolbar.rule9')
rE('pack forget .toolbar.view_zoomin')
rE('pack forget .toolbar.view_zoomout')
rE('pack forget .toolbar.view_z')
rE('pack forget .toolbar.view_z2')
rE('pack forget .toolbar.view_x')
rE('pack forget .toolbar.view_y')
rE('pack forget .toolbar.view_y2')
rE('pack forget .toolbar.view_p')
rE('pack forget .toolbar.rotate')
rE('pack forget .toolbar.rule12')
rE('pack forget .toolbar.clear_plot')
rE(f"grid forget {fmanual}.axis")
rE(f"grid forget {fmanual}.jogf")
rE(f"grid forget {fmanual}.space1")
rE(f"grid forget {fmanual}.spindlel")
rE(f"grid forget {fmanual}.spindlef")
rE(f"grid forget {fmanual}.space2")
rE(f"grid forget {fmanual}.coolant")
rE(f"grid forget {fmanual}.mist")
rE(f"grid forget {fmanual}.flood")
rE(f"pack forget {ftop}.jogspeed.l1")
rE(f"grid forget {ftop}.spinoverride")
rE(f"grid forget {ftop}.maxvel")
# modify the toolbar
rE('vrule .toolbar.rule10')
rE('vrule .toolbar.rule11')
rE('pack .toolbar.rule9 -side left -padx {66 4} -pady 4 -fill y')
rE('pack .toolbar.clear_plot -side left')
rE('pack .toolbar.rule10 -side left -padx 4 -pady 4 -fill y')
rE('pack .toolbar.view_z -side left')
rE('pack .toolbar.view_p -side left')
rE(f".toolbar.view_p configure -image {rE(f'load_image {imagePath}/view_p')}")
rE(f".toolbar.view_z configure -image {rE(f'load_image {imagePath}/view_z')}")
rE(f"Button .toolbar.view_t -image {rE(f'load_image {imagePath}/view_t')} -command set_view_t -relief link -takefocus 0")
rE('pack .toolbar.view_t -side left')
rE('pack .toolbar.rule11 -side left -padx 4 -pady 4 -fill y')
rE('pack .toolbar.view_zoomout -side left')
rE('pack .toolbar.view_zoomin -side left')
rE('pack .toolbar.rule12 -side left -padx 4 -pady 4 -fill y')
rE('pack .toolbar.rotate -side left')
rE('.toolbar.rotate configure -takefocus 0')
# new setup toolbar
# create widgets
rE('frame .toolsetup -borderwidth 1 -relief raised')
rE(f"Button .toolsetup.save -command save_setup_clicked -width 8 -takefocus 0 -text {{{_('Save All')}}} -padx 0")
rE(f"Button .toolsetup.reload -command load_setup_clicked -width 8 -takefocus 0 -text {{{_('Reload')}}} -padx 0")
rE(f"Button .toolsetup.bkp -command backup_clicked -width 8 -takefocus 0 -text {{{_('Backup')}}} -padx 0")
rE(f"Button .toolsetup.close -command {{setup_toggle 0}} -width 8 -takefocus 0 -text {{{_('Close')}}} -padx 0")
# populate the tool bar
rE('pack .toolsetup.save -side left')
rE('pack .toolsetup.reload -side left -padx {8 0}')
rE('pack .toolsetup.bkp -side left -padx {8 0}')
rE('pack .toolsetup.close -side left -padx {16 0}')
rE('grid forget .toolsetup')
# new parameters toolbar
# create widgets
rE('frame .toolparam -borderwidth 1 -relief raised')
rE(f"Button .toolparam.save -command save_param_clicked -width 8 -takefocus 0 -text {{{_('Save All')}}} -padx 0")
rE(f"Button .toolparam.reload -command load_param_clicked -width 8 -takefocus 0 -text {{{_('Reload')}}} -padx 0")
rE(f"Button .toolparam.close -command {{param_toggle 0}} -width 8 -takefocus 0 -text {{{_('Close')}}} -padx 0")
# populate the tool bar
rE('pack .toolparam.save -side left')
rE('pack .toolparam.reload -side left -padx {8 0}')
rE('pack .toolparam.close -side left -padx {16 0}')
rE('grid forget .toolparam')
# new statistics page
rE(f"{fright} insert end stats -text {{{_('Statistics')}}}")
rE(f"{fright}.fstats configure -bd 2")
rE(f"labelframe {stats} -relief groove")
rE(f"label {stats}.itemL -text {{{_('Item')}}} -width 13 -anchor e")
rE(f"label {stats}.jobL -text {{{_('Job')}}} -width 9 -anchor e")
rE(f"label {stats}.totalL -text {{{_('Total')}}} -width 9 -anchor e")
rE(f"label {stats}.lengthL -text {{{_('Cut Length')}}} -width 13 -anchor e")
rE(f"label {stats}.lengthJ -textvariable lengthJ -width 9 -anchor e")
rE(f"label {stats}.lengthT -textvariable lengthT -width 9 -anchor e")
rE(f"button {stats}.lengthB -command {{reset_all_stats length}} -text {{{_('Reset')}}} -pady 0")
rE(f"label {stats}.pierceL -text {{{_('Torch Starts')}}} -width 13 -anchor e")
rE(f"label {stats}.pierceJ -textvariable pierceJ -width 9 -anchor e")
rE(f"label {stats}.pierceT -textvariable pierceT -width 9 -anchor e")
rE(f"button {stats}.pierceB -command {{reset_all_stats pierce}} -text {{{_('Reset')}}} -pady 0")
rE(f"label {stats}.rapidL -text {{{_('Rapid Time')}}} -width 13 -anchor e")
rE(f"label {stats}.rapidJ -textvariable rapidJ -width 9 -anchor e")
rE(f"label {stats}.rapidT -textvariable rapidT -width 9 -anchor e")
rE(f"button {stats}.rapidB -command {{reset_all_stats rapid}} -text {{{_('Reset')}}} -pady 0")
rE(f"label {stats}.probeL -text {{{_('Probe Time')}}} -width 13 -anchor e")
rE(f"label {stats}.probeJ -textvariable probeJ -width 9 -anchor e")
rE(f"label {stats}.probeT -textvariable probeT -width 9 -anchor e")
rE(f"button {stats}.probeB -command {{reset_all_stats probe}} -text {{{_('Reset')}}} -pady 0")
rE(f"label {stats}.torchL -text {{{_('Torch On Time')}}} -width 13 -anchor e")
rE(f"label {stats}.torchJ -textvariable torchJ -width 9 -anchor e")
rE(f"label {stats}.torchT -textvariable torchT -width 9 -anchor e")
rE(f"button {stats}.torchB -command {{reset_all_stats torch}} -text {{{_('Reset')}}} -pady 0")
rE(f"label {stats}.cutL -text {{{_('Cutting Time')}}} -width 13 -anchor e")
rE(f"label {stats}.cutJ -textvariable cutJ -width 9 -anchor e")
rE(f"label {stats}.cutT -textvariable cutT -width 9 -anchor e")
rE(f"button {stats}.cutB -command {{reset_all_stats cut}} -text {{{_('Reset')}}} -pady 0")
rE(f"label {stats}.pausedL -text {{{_('Paused Time')}}} -width 13 -anchor e")
rE(f"label {stats}.pausedJ -textvariable pausedJ -width 9 -anchor e")
rE(f"label {stats}.pausedT -textvariable pausedT -width 9 -anchor e")
rE(f"button {stats}.pausedB -command {{reset_all_stats paused}} -text {{{_('Reset')}}} -pady 0")
rE(f"label {stats}.runL -text {{{_('Total Run Time')}}} -width 13 -anchor e")
rE(f"label {stats}.runJ -textvariable runJ -width 9 -anchor e")
rE(f"label {stats}.runT -textvariable runT -width 9 -anchor e")
rE(f"button {stats}.runB -command {{reset_all_stats run}} -text {{{_('Reset')}}} -pady 0")
rE(f"label {stats}.arcL -text {{{_('Arc On Time')}}} -width 13 -anchor e")
rE(f"label {stats}.arcT -textvariable arcT -width 10 -anchor e")
rE(f"grid {stats}.itemL -column 0 -row 0")
rE(f"grid {stats}.jobL -column 1 -row 0")
rE(f"grid {stats}.totalL -column 2 -row 0")
rE(f"grid {stats}.lengthL -column 0 -row 1 -pady {{8 0}}")
rE(f"grid {stats}.lengthJ -column 1 -row 1 -pady {{8 0}}")
rE(f"grid {stats}.lengthT -column 2 -row 1 -pady {{8 0}}")
rE(f"grid {stats}.lengthB -column 3 -row 1 -padx {{16 0}} -pady {{8 0}}")
rE(f"grid {stats}.pierceL -column 0 -row 2 -pady {{8 0}}")
rE(f"grid {stats}.pierceJ -column 1 -row 2 -pady {{8 0}}")
rE(f"grid {stats}.pierceT -column 2 -row 2 -pady {{8 0}}")
rE(f"grid {stats}.pierceB -column 3 -row 2 -padx {{16 0}} -pady {{8 0}}")
rE(f"grid {stats}.rapidL -column 0 -row 3 -pady {{8 0}}")
rE(f"grid {stats}.rapidJ -column 1 -row 3 -pady {{8 0}}")
rE(f"grid {stats}.rapidT -column 2 -row 3 -pady {{8 0}}")
rE(f"grid {stats}.rapidB -column 3 -row 3 -padx {{16 0}} -pady {{8 0}}")
rE(f"grid {stats}.probeL -column 0 -row 4 -pady {{8 0}}")
rE(f"grid {stats}.probeJ -column 1 -row 4 -pady {{8 0}}")
rE(f"grid {stats}.probeT -column 2 -row 4 -pady {{8 0}}")
rE(f"grid {stats}.probeB -column 3 -row 4 -padx {{16 0}} -pady {{8 0}}")
rE(f"grid {stats}.torchL -column 0 -row 5 -pady {{8 0}}")
rE(f"grid {stats}.torchJ -column 1 -row 5 -pady {{8 0}}")
rE(f"grid {stats}.torchT -column 2 -row 5 -pady {{8 0}}")
rE(f"grid {stats}.torchB -column 3 -row 5 -padx {{16 0}} -pady {{8 0}}")
rE(f"grid {stats}.cutL -column 0 -row 6 -pady {{8 0}}")
rE(f"grid {stats}.cutJ -column 1 -row 6 -pady {{8 0}}")
rE(f"grid {stats}.cutT -column 2 -row 6 -pady {{8 0}}")
rE(f"grid {stats}.cutB -column 3 -row 6 -padx {{16 0}} -pady {{8 0}}")
rE(f"grid {stats}.pausedL -column 0 -row 7 -pady {{8 0}}")
rE(f"grid {stats}.pausedJ -column 1 -row 7 -pady {{8 0}}")
rE(f"grid {stats}.pausedT -column 2 -row 7 -pady {{8 0}}")
rE(f"grid {stats}.pausedB -column 3 -row 7 -padx {{16 0}} -pady {{8 0}}")
rE(f"grid {stats}.runL -column 0 -row 8 -pady {{8 0}}")
rE(f"grid {stats}.runJ -column 1 -row 8 -pady {{8 0}}")
rE(f"grid {stats}.runT -column 2 -row 8 -pady {{8 0}}")
rE(f"grid {stats}.runB -column 3 -row 8 -padx {{16 0}} -pady {{8 0}}")
if pmPort:
rE(f"grid {stats}.arcL -column 0 -row 9 -pady {{8 0}}")
rE(f"grid {stats}.arcT -column 2 -row 9 -pady {{8 0}}")
rE(f"pack {stats} -fill both -expand True -padx 2 -pady 2")
# destroy existing axes and joints
rE(f"destroy {faxes}")
rE(f"destroy {fjoints}")
# create widgets
rE(f"labelframe {faxes} -text {{{_('Axis:')}}} -relief flat -bd 0")
rE(f"labelframe {fjoints} -text {{{_('Joint:')}}} -relief flat -bd 0")
# make joints radiobuttons
for number in range(0,linuxcnc.MAX_JOINTS):
ja_button_setup(f"{fjoints}.joint{number}", number, number)
# populate joints frame
count = 0
for row in range(0,2):
for column in range(0,5):
if count == jointcount: break
pad = '{0 0}' if column == 0 else '{8 0}'
rE(f"grid {fjoints}.joint{count} -row {row} -column {column} -padx {pad}")
count += 1
# make axis radiobuttons
for letter in 'xyzabcuvw':
ja_button_setup(f"{faxes}.axis{letter}", letter, letter.upper())
# populate the axes frame
count = 0
letters = 'xyzabcuvw'
first_axis = ''
for row in range(0,2):
for column in range(0,5):
if letters[count] in trajcoordinates:
if first_axis == '':
first_axis = letters[count]
pad = '{0 0}' if column == 0 else '{8 0}'
rE(f"grid {faxes}.axis{letters[count]} -row {row} -column {column} -padx {pad}")
count += 1
if count == 9: break
# rework the jogf frame
rE(f"destroy {fjogf}")
# create the widgets
rE(f"frame {fjogf}")
rE(f"labelframe {fjogf}.jog -text {{{_('Jog:')}}} -relief flat -bd 0")
rE(f"button {fjogf}.jog.jogminus -command {{if ![is_continuous] {{jog_minus 1}}}} -height 1 -width 1 -text -")
rE(f"button {fjogf}.jog.jogplus -command {{if ![is_continuous] {{jog_plus 1}}}} -height 1 -width 1 -text +")
rE(f"combobox {fjogf}.jog.jogincr -editable 0 -textvariable jogincrement -value {{{_('Continuous')}}} -width 10")
rE(f"{fjogf}.jog.jogincr list insert end {{{_('Continuous')}}}")
if increments:
for inc in increments:
rE(f"{fjogf}.jog.jogincr list insert end {inc}")
rE(f"labelframe {fjogf}.zerohome -text {{{_('Zero:')}}} -relief flat -bd 0")
rE(f"button {fjogf}.zerohome.home -command home_joint -height 1 -width 8 -padx 0")
rE(f"setup_widget_accel {fjogf}.zerohome.home {{{_('Home Axis')}}}")
rE(f"button {fjogf}.zerohome.zero -command touch_off_system -height 1 -width 5 -padx 0")
rE(f"setup_widget_accel {fjogf}.zerohome.zero X0")
rE(f"button {fjogf}.zerohome.zeroxy -height 1 -width 5 -text {{{_('X0Y0')}}} -padx 0")
rE(f"button {fjogf}.zerohome.laser -height 1 -width 5 -textvariable laserText -padx 0")
rE(f"button {fjogf}.zerohome.tooltouch") # unused... kept for tk hierarchy
# widget bindings
rE(f"bind {fjogf}.jog.jogminus <Button-1> {{if [is_continuous] {{jog_minus}}}}")
rE(f"bind {fjogf}.jog.jogminus <ButtonRelease-1> {{if [is_continuous] {{jog_stop}}}}")
rE(f"bind {fjogf}.jog.jogplus <Button-1> {{if [is_continuous] {{jog_plus}}}}")
rE(f"bind {fjogf}.jog.jogplus <ButtonRelease-1> {{if [is_continuous] {{jog_stop}}}}")
rE(f"bind {fjogf}.zerohome.zeroxy <Button-1> {{touch_off_xy 1 0 0}}")
rE(f"bind {fjogf}.zerohome.zeroxy <ButtonRelease-1> {{touch_off_xy 0 0 0}}")
rE(f"bind {fjogf}.zerohome.laser <Button-1> {{laser_button_toggled 1 1}}")
rE(f"bind {fjogf}.zerohome.laser <ButtonRelease-1> {{laser_button_toggled 0 1}}")
# populate the frame
rE(f"grid {fjogf}.jog.jogminus -row 0 -column 0 -sticky nsew")
rE(f"grid {fjogf}.jog.jogincr -row 0 -column 1 -sticky nsew -padx 8")
rE(f"grid {fjogf}.jog.jogplus -row 0 -column 2 -sticky nsew")
rE(f"grid {fjogf}.jog -row 0 -column 0 -sticky ew")
rE(f"grid {fjogf}.zerohome.home -row 0 -column 0 -padx {{0 4}}")
rE(f"grid {fjogf}.zerohome.zero -row 0 -column 1 -padx {{0 4}}")
rE(f"grid {fjogf}.zerohome.zeroxy -row 0 -column 2 -padx {{0 4}}")
rE(f"grid {fjogf}.zerohome -row 1 -column 0 -pady {{2 0}} -sticky w")
rE(f"grid {fjogf} -column 0 -row 1 -padx 2 -pady {{0 0}} -sticky w")
# make home button a home all button if required
if homing_order_defined:
if ja_name.startswith('A'):
hbName = 'axes'
else:
hbName ='joints'
widgets.homebutton.configure(text = _('Home All'), command = 'home_all_joints')
else:
pre = _('Home')
widgets.homebutton.configure(text = f"{pre} X")
# add a spacer to keep the plasma widgets at the bottom
rE(f"vspace {fmanual}.vspace -height 1")
rE(f"grid {fmanual}.vspace -column 0 -row 2 -pady 0 -sticky nsew")
rE(f"grid rowconfigure {fmanual} 2 -weight 99")
# new limit switch override frame
rE(f"frame {flimitovr} -bd 0")
rE(f"button {fjogf}.override") # dummy button to placate original axis code
rE(f"checkbutton {flimitovr}.button -command toggle_override_limits -width 2 -anchor w -indicatoron 0")
rE(f"label {flimitovr}.label -text {{{_('Override Limits')}}} -anchor center")
# populate the frame
rE(f"grid {flimitovr}.button -column 0 -row 1 -sticky nsw")
rE(f"grid {flimitovr}.label -column 1 -row 1 -sticky w -padx {{4 0}}")
rE(f"grid rowconfigure {flimitovr} 0 -weight 1")
rE(f"grid rowconfigure {flimitovr} 1 -weight 0")
rE(f"grid rowconfigure {flimitovr} 2 -weight 1")
# new jog inhibit override frame
rE(f"frame {fjogiovr} -bd 0")
rE(f"checkbutton {fjogiovr}.button -variable jogInhibitOvr -width 2 -anchor w -indicatoron 0")
rE(f"label {fjogiovr}.label -text {{{_('Jog Inhibit Override')}}} -anchor center")
# populate the frame
rE(f"grid {fjogiovr}.button -column 0 -row 1 -sticky nsw")
rE(f"grid {fjogiovr}.label -column 1 -row 1 -sticky w -padx {{4 0}}")
rE(f"grid rowconfigure {fjogiovr} 0 -weight 1")
rE(f"grid rowconfigure {fjogiovr} 1 -weight 0")
rE(f"grid rowconfigure {fjogiovr} 2 -weight 1")
# new height override frame
# create the widgets
rE(f"labelframe {foverride} -text {{{_('THC Height Override:')}}} -relief flat -bd 0")
rE(f"Button {foverride}.lower -text - -width 1 -takefocus 0")
rE(f"Button {foverride}.raise -text + -width 1 -takefocus 0")
rE(f"label {foverride}.height-override -text {torchHeight:.1f}V -width 20")
rE(f"Button {foverride}.reset -text {{{_('Reset')}}} -width 1 -takefocus 0 -width 3")
# populate the frame
rE(f"grid {foverride}.lower -column 0 -row 0")
rE(f"grid {foverride}.raise -column 1 -row 0 -padx {{4 0}}")
rE(f"grid {foverride}.height-override -column 2 -row 0 -sticky ew")
rE(f"grid {foverride}.reset -column 3 -row 0")
rE(f"grid {foverride} -column 0 -row 3 -padx 2 -pady {{0 4}} -sticky ew")
rE(f"grid columnconfigure {foverride} 2 -weight 1")
# widget bindings
rE(f"bind {foverride}.lower <ButtonPress-1> height_lower")
rE(f"bind {foverride}.raise <ButtonPress-1> height_raise")
rE(f"bind {foverride}.reset <ButtonPress-1> height_reset")
# new plasma frame
# create the widgets
rE(f"frame {fplasma} -bd 0")
rE(f"label {fplasma}.arcvl -anchor nw -text {{{_('Arc Voltage:')}}}")
rE(f"label {fplasma}.arc-voltage -anchor se -width 4")
rE(f"checkbutton {fplasma}.thc -variable thcEnable -command thc_enable_toggled -width 2 -anchor w -indicatoron 0")
rE(f"label {fplasma}.thcL -anchor nw -text {{{_('THC Enable')}}} -width 11")
rE(f"checkbutton {fplasma}.vel -variable cornerEnable -command corner_enable_toggled -width 2 -anchor w -indicatoron 0")
rE(f"label {fplasma}.velL -anchor nw -text {{{_('Velocity Lock')}}} -width 11")
rE(f"checkbutton {fplasma}.void -variable voidEnable -command void_enable_toggled -width 2 -anchor w -indicatoron 0")
rE(f"label {fplasma}.voidL -anchor nw -text {{{_('Void Lock')}}} -width 11")
# populate the frame
rE(f"grid {fplasma}.thc -column 2 -row 0 -sticky e -pady {{0 2}}")
rE(f"grid {fplasma}.thcL -column 3 -row 0 -sticky e -pady {{0 2}}")
rE(f"grid {fplasma}.vel -column 2 -row 1 -sticky e -pady {{0 2}}")
rE(f"grid {fplasma}.velL -column 3 -row 1 -sticky e -pady {{0 2}}")
rE(f"grid {fplasma}.void -column 2 -row 2 -sticky e -pady {{0 2}}")
rE(f"grid {fplasma}.voidL -column 3 -row 2 -sticky e -pady {{0 2}}")
rE(f"grid {fplasma} -column 0 -row 4 -padx 2 -pady {{0 0}} -sticky ew")
rE(f"grid columnconfigure {fplasma} 1 -weight 1")
# shorten some text for nicer layout.
rE(f".pane.top.rapidoverride.l configure -text {{{_('Rapid Ovr:')}}}")
rE(f".pane.top.feedoverride.l configure -text {{{_('Feed Ovr:')}}}")
# hide bottom pane until modified
rE('pack forget .pane.bottom.t.text')
rE('pack forget .pane.bottom.t.sb')
# new led frame
# create the widgets
rE(f"frame {fleds} -relief flat -bd 0")
for led in wLeds:
# leds
rE(f"canvas {led} -width 20 -height 20 -bd 2 -relief ridge")
rE(f"{led} create rectangle {{1 1 22 22}} -width 0")
# labels
rE(f"label {led}L -text {{{wLeds[led][0]}}} -anchor w -width {wLeds[led][1]}")
# populate the frame
rE(f"grid {fleds}.led-arc-ok -column 0 -row 0 -padx {{4 0}} -pady {{4 0}} -sticky EW")
rE(f"grid {fleds}.led-arc-okL -column 1 -row 0 -padx {{0 0}} -pady {{4 0}} -sticky W")
rE(f"grid {fleds}.led-torch -column 0 -row 1 -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fleds}.led-torchL -column 1 -row 1 -padx {{0 0}} -pady {{4 0}} -sticky W")
rE(f"grid {fleds}.led-breakaway -column 0 -row 3 -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fleds}.led-breakawayL -column 1 -row 3 -padx {{0 0}} -pady {{4 0}} -sticky W")
rE(f"grid {fleds}.led-thc-enabled -column 2 -row 0 -padx {{0 0}} -pady {{4 0}}")
rE(f"grid {fleds}.led-thc-enabledL -column 3 -row 0 -padx {{0 0}} -pady {{4 0}} -sticky W")
rE(f"grid {fleds}.led-thc-active -column 2 -row 1 -padx {{0 0}} -pady {{4 0}}")
rE(f"grid {fleds}.led-thc-activeL -column 3 -row 1 -padx {{0 0}} -pady {{4 0}} -sticky W")
rE(f"grid {fleds}.led-ohmic -column 2 -row 2 -padx {{0 0}} -pady {{4 0}}")
rE(f"grid {fleds}.led-ohmicL -column 3 -row 2 -padx {{0 0}} -pady {{4 0}} -sticky W")
rE(f"grid {fleds}.led-float -column 2 -row 3 -padx {{0 0}} -pady {{4 0}}")
rE(f"grid {fleds}.led-floatL -column 3 -row 3 -padx {{0 0}} -pady {{4 0}} -sticky W")
rE(f"grid {fleds}.led-up -column 4 -row 0 -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fleds}.led-upL -column 5 -row 0 -padx {{0 0}} -pady {{4 0}} -sticky W")
rE(f"grid {fleds}.led-down -column 4 -row 1 -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fleds}.led-downL -column 5 -row 1 -padx {{0 0}} -pady {{4 0}} -sticky W")
rE(f"grid {fleds}.led-corner-locked -column 4 -row 2 -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fleds}.led-corner-lockedL -column 5 -row 2 -padx {{0 0}} -pady {{4 0}} -sticky W")
# rename keyboard shortcut window
rE('wm title .keys {plasmac2 Keyboard Shortcuts}')
# new keypad shortcut window
rE('toplevel .keyp')
rE('bind .keyp <Key-Return> {wm withdraw .keyp}')
rE('bind .keyp <Key-Escape> {wm withdraw .keyp}')
rE('frame .keyp.text')
rE('button .keyp.ok -command {wm wi .keyp} -default active -padx 0 -pady 0 -width 10')
rE('setup_widget_accel .keyp.ok OK')
rE('pack .keyp.text -expand 1 -fill y')
rE('pack .keyp.ok')
rE('wm title .keyp {plasmac2 Keypad Shortcuts}')
rE('wm iconname .keyp ')
rE('wm resiz .keyp 0 0')
rE('wm minsize .keyp 1 1')
rE('wm protocol .keyp WM_DELETE_WINDOW {wm wi .keys}')
rE('wm withdraw .keyp')
# menu alterations
# remove tearoffs
rE('.menu.machine configure -tearoff 0')
rE('.menu.machine.home configure -tearoff 0')
rE('.menu.machine.unhome configure -tearoff 0')
# delete some existing
rE('.menu.file delete last')
rE('.menu.machine delete last')
rE('.menu.machine delete last')
rE('.menu.machine delete last')
rE('.menu.view delete 20')
rE('.menu.view delete 8')
rE('.menu.view delete 7')
rE('.menu.view delete 6')
rE('.menu delete last')
# add new menu items
rE('.menu.file insert 2 command -command clear_program')
rE(f"setup_menu_accel .menu.file 2 {{{_('_Clear')}}}")
rE('.menu.file add command -command close_window')
rE(f"setup_menu_accel .menu.file end {{{_('_Quit')}}}")
rE('.menu.view insert 0 checkbutton -variable previewLarge -command preview_toggle')
rE(f"setup_menu_accel .menu.view 0 {{{_('Large Pre_view')}}}")
rE('.menu.view insert 1 separator')
rE('.menu add command -command {conv_toggle 1}')
rE(f"setup_menu_accel .menu end {{{_('_Conversational')}}}")
rE('.menu add command -command {param_toggle 1}')
rE(f"setup_menu_accel .menu end {{{_('_Parameters')}}}")
rE('.menu add command -command {setup_toggle 1}')
rE(f"setup_menu_accel .menu end {{{_('_Setup')}}}")
rE('.menu add cascade -menu .menu.help')
rE(f"setup_menu_accel .menu end {{{_('_Help')}}}")
rE('.menu.help delete 0 1')
rE('.menu.help add command -command {wm transient .about .;wm deiconify .about;show_all .about.message;focus .about.ok}')
rE(f"setup_menu_accel .menu.help end {{{_('About AXIS')}}}")
rE('.menu.help add separator')
rE('.menu.help add command -command user_manual')
rE(f"setup_menu_accel .menu.help end {{{_('User Manual')}}}")
# rework the status bar
rE(f"grid forget {ftop}.gcodel")
rE(f"grid forget {ftop}.gcodes")
rE('pack forget .info.tool')
rE('.info.task_state configure -width 6')
rE('.info.tool configure -width 10 -textvariable plasmatool')
rE('.info.position configure -width 25')
rE('grid .info -columnspan 4')
rE('labelframe .info.gcodef -relief sunken -bd 2')
rE('label .info.gcodef.gcodel -bd 0')
rE('setup_widget_accel .info.gcodef.gcodel {Active Codes:}')
rE('text .info.gcodef.gcodes -height 1 -width 10 -relief flat -bd 0 -pady 0')
rE('.info.gcodef.gcodes configure -undo 0 -wrap word')
rE('pack .info.tool -side left')
rE('pack .info.gcodef.gcodel -side left')
rE('pack .info.gcodef.gcodes -side left -fill x -expand 1')
rE('pack .info.gcodef -side left -fill x -expand 1')
# move existing pane to column 1
rE('grid .pane -column 1 -row 1 -rowspan 2 -sticky nsew')
# set pane column growth
rE('grid columnconfigure .pane.top 0 -weight 1')
rE('grid columnconfigure .pane.top 1 -weight 255')
# set root window column growth
rE('grid columnconfigure . 0 -weight 0')
rE('grid columnconfigure . 1 -weight 1')
rE('grid columnconfigure . 2 -weight 0')
# new cut recovery tab
# create the widgets
rE(f"{ftabs} insert end cutrecs -text {{{_('Cut Recovery')}}}")
rE(f"{fcutrecs} configure -borderwidth 2")
rE(f"{ftabs} delete cutrecs 0")
rE(f"labelframe {fcrspeed} -text {{{_('Speed:')}}} -relief flat")
rE(f"Button {fcrspeed}.reverse -text {{{_('Rev')}}} -takefocus 0 -width 3")
rE(f"bind {fcrspeed}.reverse <Button-1> {{cut_rec_motion -1}}")
rE(f"bind {fcrspeed}.reverse <ButtonRelease-1> {{cut_rec_motion 0}}")
rE(f"frame {fcrspeed}.display -relief flat")
rE(f"frame {fcrspeed}.display.top -relief flat")
rE(f"label {fcrspeed}.display.top.value -textvariable crSpeed -width 6 -anchor center")
rE(f"scale {fcrspeed}.display.cut-rec-speed -takefocus 0 -orient horizontal -showvalue 0 -command cut_rec_slider_changed")
rE(f"{fcrspeed}.display.cut-rec-speed configure -from 1 -to 100 -resolution 1")
rE(f"Button {fcrspeed}.forward -text {{{_('Fwd')}}} -takefocus 0 -width 3")
rE(f"frame {fcrbuttons} -relief flat")
rE(f"button {fcrbuttons}.laser -text {{{_('Laser')}}} -takefocus 0 -width 6")
rE(f"button {fcrbuttons}.cancel -text {{{_('Cancel')}}} -command cut_rec_cancel -takefocus 0 -width 6")
rE(f"labelframe {fcrleadin} -text {{{_('Leadins:')}}} -relief flat")
rE(f"Button {fcrleadin}.nw -takefocus 0 -image {rE(f'load_image {imagePath}/nw')}")
rE(f"Button {fcrleadin}.n -takefocus 0 -image {rE(f'load_image {imagePath}/n')}")
rE(f"Button {fcrleadin}.ne -takefocus 0 -image {rE(f'load_image {imagePath}/ne')}")
rE(f"Button {fcrleadin}.w -takefocus 0 -image {rE(f'load_image {imagePath}/w')}")
rE(f"Label {fcrleadin}.offset -textvariable kerfWidth -takefocus 0")
rE(f"Button {fcrleadin}.e -takefocus 0 -image {rE(f'load_image {imagePath}/e')}")
rE(f"Button {fcrleadin}.sw -takefocus 0 -image {rE(f'load_image {imagePath}/sw')}")
rE(f"Button {fcrleadin}.s -takefocus 0 -image {rE(f'load_image {imagePath}/s')}")
rE(f"Button {fcrleadin}.se -takefocus 0 -image {rE(f'load_image {imagePath}/se')}")
# populate the frame
rE(f"pack {fcrspeed}.reverse -side left -fill y")
rE(f"pack {fcrspeed}.display.top.value -side left -fill y")
rE(f"pack {fcrspeed}.display.top -fill y")
rE(f"pack {fcrspeed}.display.cut-rec-speed -fill y")
rE(f"pack {fcrspeed}.display -side left -fill x -expand 1")
rE(f"pack {fcrspeed}.forward -side right -fill y")
rE(f"grid {fcrbuttons}.laser -column 0 -row 0")
rE(f"grid {fcrbuttons}.cancel -column 2 -row 0")
rE(f"grid {fcrleadin}.nw -column 0 -row 0")
rE(f"grid {fcrleadin}.n -column 1 -row 0")
rE(f"grid {fcrleadin}.ne -column 2 -row 0")
rE(f"grid {fcrleadin}.w -column 0 -row 1")
rE(f"grid {fcrleadin}.offset -column 1 -row 1")
rE(f"grid {fcrleadin}.e -column 2 -row 1")
rE(f"grid {fcrleadin}.sw -column 0 -row 2")
rE(f"grid {fcrleadin}.s -column 1 -row 2")
rE(f"grid {fcrleadin}.se -column 2 -row 2")
rE(f"grid {fcrspeed} -column 0 -row 0 -sticky wne")
rE(f"grid {fcrbuttons} -column 0 -row 1 -sticky wne -pady 16")
rE(f"grid {fcrleadin} -column 0 -row 2 -sticky nsew")# -pady 12")
# set direction buttons stretch
rE(f"grid columnconfigure {fcrbuttons} 1 -weight 1")
rE(f"grid columnconfigure {fcrleadin} 0 -weight 1")
rE(f"grid columnconfigure {fcrleadin} 1 -weight 1")
rE(f"grid columnconfigure {fcrleadin} 2 -weight 1")
rE(f"grid rowconfigure {fcrleadin} 0 -weight 1")
rE(f"grid rowconfigure {fcrleadin} 1 -weight 1 -pad 20")
rE(f"grid rowconfigure {fcrleadin} 2 -weight 1")
# widget bindings
rE(f"bind {fcrspeed}.forward <Button-1> {{cut_rec_motion 1}}")
rE(f"bind {fcrspeed}.forward <ButtonRelease-1> {{cut_rec_motion 0}}")
rE(f"bind {fcrleadin}.nw <Button-1> {{cut_rec_move -1 1}}")
rE(f"bind {fcrleadin}.n <Button-1> {{cut_rec_move 0 1}}")
rE(f"bind {fcrleadin}.ne <Button-1> {{cut_rec_move 1 1}}")
rE(f"bind {fcrleadin}.w <Button-1> {{cut_rec_move -1 0}}")
rE(f"bind {fcrleadin}.e <Button-1> {{cut_rec_move 1 0}}")
rE(f"bind {fcrleadin}.sw <Button-1> {{cut_rec_move -1 -1}}")
rE(f"bind {fcrleadin}.s <Button-1> {{cut_rec_move 0 -1}}")
rE(f"bind {fcrleadin}.se <Button-1> {{cut_rec_move 1 -1}}")
rE(f"bind {fcrbuttons}.laser <Button-1> {{laser_button_toggled 1 0}}")
rE(f"bind {fcrbuttons}.laser <ButtonRelease-1> {{laser_button_toggled 0 0}}")
# new parameters frame
rE(f"frame {fparam}")
# create the widgets
rE(f"frame {fparam}.c1")
rE(f"frame {fparam}.c2")
rE(f"frame {fparam}.c3")
rE(f"labelframe {fparam}.c1.probe -text {{{_('Probing')}}} -relief groove")
rE(f"labelframe {fparam}.c1.motion -text {{{_('Motion')}}} -relief groove")
rE(f"labelframe {fparam}.c1.safety -text {{{_('Safety')}}} -relief groove")
rE(f"labelframe {fparam}.c2.thc -text {{{_('THC')}}} -relief groove")
rE(f"labelframe {fparam}.c2.scribe -text {{{_('Scribe')}}} -relief groove")
rE(f"labelframe {fparam}.c2.pierce -text {{{_('Pierce Only')}}} -relief groove")
rE(f"labelframe {fparam}.c3.arc -text {{{_('Arc')}}} -relief groove")
rE(f"labelframe {fparam}.c3.spotting -text {{{_('Spotting')}}} -relief groove")
rE(f"label {fparam}.c2.thc.thc-autoL -text {{{_('Auto')}}} -width 15 -anchor e")
rE(f"checkbutton {fparam}.c2.thc.thc-auto -width 2 -anchor w -indicatoron 0")
rE(f"{fparam}.c2.thc.thc-auto configure -variable thcAuto -command mode_changed")
# spinboxes [parent, name, decimals, value, min, max, increment, label text, prefs option]
cpList = [[f"{fparam}.c1.probe",'float-switch-travel',2,1.5,-25,25,0.01,'Float Travel','Float Switch Travel'], \
[f"{fparam}.c1.probe",'probe-feed-rate',0,300,1,thcFeedRate,1,'Probe Speed','Probe Feed Rate'], \
[f"{fparam}.c1.probe",'probe-start-height',0,38,1,maxHeight,1,'Probe Height','Probe Start Height'], \
[f"{fparam}.c1.probe",'ohmic-probe-offset',2,0,-25,+25,0.01,'Ohmic Z Offset','Ohmic Probe Offset'], \
[f"{fparam}.c1.probe",'ohmic-max-attempts',0,0,0,10,1,'Ohmic Retries','Ohmic Maximum Attempts'], \
[f"{fparam}.c1.probe",'skip-ihs-distance',0,0,0,999,1,'Skip IHS','Skip IHS Distance'], \
[f"{fparam}.c1.probe",'offset-feed-rate',0,offsetFeedRate*0.8,0,offsetFeedRate,1,'Offset Speed','Offset Feed Rate'], \
[f"{fparam}.c1.motion",'setup-feed-rate',0,int(thcFeedRate * 0.8),1000,thcFeedRate,1,'Setup Speed','Setup Feed Rate'], \
[f"{fparam}.c1.safety",'safe-height',0,20,0,maxHeight,1,'Safe Height','Safe Height'], \
[f"{fparam}.c2.thc",'thc-delay',1,0.5,0,9,0.1,'Start Delay','THC Delay'], \
[f"{fparam}.c2.thc",'thc-sample-counts',0,50,10,1000,1,'Auto Counts','THC Sample Counts'], \
[f"{fparam}.c2.thc",'thc-sample-threshold',1,1,0.1,9,0.1,'Auto Threshold (V)','THC Sample Threshold'], \
[f"{fparam}.c2.thc",'thc-threshold',2,1,0.05,9,0.01,'Cut Threshold (V)','THC Threshold'], \
[f"{fparam}.c2.thc",'pid-p-gain',0,10,0,1000,1,'PID P Gain (Speed)','Pid P Gain'], \
[f"{fparam}.c2.thc",'pid-i-gain',0,0,0,1000,1,'PID I Gain','Pid I Gain'], \
[f"{fparam}.c2.thc",'pid-d-gain',0,0,0,1000,1,'PID D Gin','Pid D Gain'], \
[f"{fparam}.c2.thc",'cornerlock-threshold',0,90,1,99,1,'VAD Threshold (%)','Velocity Anti Dive Threshold'], \
[f"{fparam}.c2.thc",'voidlock-slope',0,500,1,10000,1,'Void Slope (V/sec)','Void Sense Slope'], \
[f"{fparam}.c2.scribe",'scribe-arm-delay',1,0,0,9,0.1,'Arm Delay','Scribe Arming Delay'], \
[f"{fparam}.c2.scribe",'scribe-on-delay',1,0,0,9,0.1,'On delay','Scribe On Delay'], \
[f"{fparam}.c2.pierce",'x-pierce-offset',1,1.6,-5,5,0.1,'X Offset','X Pierce Offset'], \
[f"{fparam}.c2.pierce",'y-pierce-offset',1,0,-5,5,0.1,'Y Offset','Y Pierce Offset'], \
[f"{fparam}.c3.arc",'arc-fail-delay',1,3,0.1,60,0.1,'Fail Timeout','Arc Fail Timeout'], \
[f"{fparam}.c3.arc",'arc-max-starts',0,3,1,9,1,'Max. Attempts','Arc Maximum Starts'], \
[f"{fparam}.c3.arc",'restart-delay',0,3,1,60,1,'Retry Delay','Arc Restart Delay'], \
[f"{fparam}.c3.arc",'arc-voltage-scale',6,1,-9999,9999,0.000001,'Voltage Scale','Arc Voltage Scale'], \
[f"{fparam}.c3.arc",'arc-voltage-offset',3,0,-999999,999999,0.001,'Voltage Offset','Arc Voltage Offset'], \
[f"{fparam}.c3.arc",'arc-ok-high',0,99999,0,99999,1,'OK High Volts','Arc OK High'], \
[f"{fparam}.c3.arc",'arc-ok-low',0,60,0,100,1,'OK Low Volts','Arc OK Low'], \
[f"{fparam}.c3.arc",'height-per-volt',3,0.1,0.025,0.5,0.01,'Height Per Volt','Height Per Volt'], \
[f"{fparam}.c3.spotting",'spotting-threshold',0,1,0,199,1,'Threshold (V)','Spotting Threshold'], \
[f"{fparam}.c3.spotting",'spotting-time',0,0,0,9999,1,'On Time (mS)','Spotting Time'], \
]
cpRow = 0
cpFrame = cpList[0][0]
for cpItem in cpList:
if s.linear_units != 1:
if cpItem[1] == 'float-switch-travel':
cpItem[2:7] = [3,0.06,-1,1,0.001]
elif cpItem[1] == 'probe-feed-rate':
cpItem[2:7] = [1,12,0.1,thcFeedRate,.1]
elif cpItem[1] == 'probe-start-height':
cpItem[2:7] = [2,1.5,.1,maxHeight,0.01]
elif cpItem[1] == 'ohmic-probe-offset':
cpItem[2:7] = [3,0,-1,1,0.001]
elif cpItem[1] == 'skip-ihs-distance':
cpItem[2:7] = [1,0,0,99,.1]
elif cpItem[1] == 'offset-feed-rate':
cpItem[2:7] = [1,offsetFeedRate * 0.8,40,offsetFeedRate,.1]
elif cpItem[1] == 'setup-feed-rate':
cpItem[2:7] = [1,int(thcFeedRate * 0.8),0.1,thcFeedRate,0.1]
elif cpItem[1] == 'safe-height':
cpItem[2:7] = [2,0.75,0,maxHeight,0.01]
elif cpItem[1] == 'height-per-volt':
cpItem[2:7] = [4,0.004,0.001,0.020,0.001]
elif cpItem[1] == 'x-pierce-offset':
cpItem[2:7] = [2,0.06,-0.2,0.2,0.01]
elif cpItem[1] == 'y-pierce-offset':
cpItem[2:7] = [2,0,-0.2,0.2,0.01]
if cpItem[0] != cpFrame:
cpFrame = cpItem[0]
cpRow = 0
if cpItem[1] == 'thc-delay':
cpRow += 1
cpName = f"{cpItem[0]}.{cpItem[1]}"
cpType = 'flt' if cpItem[2] > 0 else 'int'
rE(f"label {cpName}L -text {{{cpItem[7]}}} -width 15 -anchor e")
rE(f"spinbox {cpName} -width 8 -justify right -wrap true")
spinBoxes.append(cpName)
rE(f"{cpName} configure -from {cpItem[4]} -to {cpItem[5]}")
rE(f"{cpName} configure -increment {cpItem[6]} -format %0.{cpItem[2]}f")
rE(f"{cpName} configure -validate key -vcmd {{{valspin} %W {cpType} {cpItem[2]} %P %s}}")
if cpItem[1] == 'setup-feed-rate':
rE(f"label {cpItem[0]}.maxzL -text {{{_('Max Z Speed')}}} -width 15 -anchor e")
rE(f"label {cpItem[0]}.maxz -text {int(thcFeedRate)} -width 8 -anchor e")
rE(f"grid {cpItem[0]}.maxzL -column 0 -row {cpRow} -sticky e -padx {{0 0}} -pady {{4 0}}")
rE(f"grid {cpItem[0]}.maxz -column 1 -row {cpRow} -sticky e -padx {{0 8}} -pady {{4 0}}")
cpRow += 1
rE(f"grid {cpName}L -column 0 -row {cpRow} -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {cpName} -column 1 -row {cpRow} -sticky e -padx {{0 4}} -pady {{4 0}}")
cpRow += 1
# populate parameters frame
rE(f"grid {fparam}.c1.probe -column 0 -row 0 -sticky new -padx {{4 2}} -pady {{4 0}} -ipady 4")
rE(f"grid {fparam}.c1.motion -column 0 -row 1 -sticky new -padx {{4 2}} -pady {{4 0}} -ipady 4")
rE(f"grid {fparam}.c1.safety -column 0 -row 2 -sticky new -padx {{4 2}} -pady {{4 0}} -ipady 4")
rE(f"grid {fparam}.c2.thc -column 0 -row 0 -sticky new -padx {{2 2}} -pady {{4 0}} -ipady 4")
rE(f"grid {fparam}.c2.scribe -column 0 -row 1 -sticky new -padx {{2 2}} -pady {{4 0}} -ipady 4")
rE(f"grid {fparam}.c2.pierce -column 0 -row 2 -sticky new -padx {{2 2}} -pady {{4 0}} -ipady 4")
rE(f"grid {fparam}.c3.arc -column 0 -row 0 -sticky new -padx {{2 2}} -pady {{4 0}} -ipady 4")
rE(f"grid {fparam}.c3.spotting -column 0 -row 1 -sticky new -padx {{2 2}} -pady {{4 0}} -ipady 4")
rE(f"grid {fparam}.c1 -column 0 -row 0 -sticky n")
rE(f"grid {fparam}.c2 -column 2 -row 0 -sticky n")
rE(f"grid {fparam}.c3 -column 4 -row 0 -sticky n")
rE(f"grid columnconfigure {fparam} 1 -weight 1")
rE(f"grid columnconfigure {fparam} 3 -weight 1")
# new settings frame
rE(f"frame {fsetup}")
# left panel
rE(f"frame {fsetup}.l")
# gui frame
rE(f"labelframe {fsetup}.l.gui -text {{{_('GUI')}}} -relief groove")
rE(f"label {fsetup}.l.gui.closedialogL -text {{{_('Close Dialog')}}} -anchor e")
rE(f"checkbutton {fsetup}.l.gui.closedialog -variable closeDialog -width 2 -anchor w -indicatoron 0")
rE(f"label {fsetup}.l.gui.wsizeL -text {{{_('Window Size')}}} -width 13 -anchor e")
rE(f"ComboBox {fsetup}.l.gui.wsize -modifycmd {{set_window_size combo}} -textvariable winSize -bd 1 -width 10 -justify right -editable 0")
rE(f"{fsetup}.l.gui.wsize configure -values {{default last fullscreen maximized}}")
rE(f"label {fsetup}.l.gui.orientL -text {{{_('Window Orient')}}} -width 13 -anchor e")
rE(f"ComboBox {fsetup}.l.gui.orient -modifycmd set_orientation -textvariable orient -bd 1 -width 10 -justify right -editable 0")
rE(f"{fsetup}.l.gui.orient configure -values {{landscape portrait}}")
rE(f"label {fsetup}.l.gui.fsizeL -text {{{_('Font Size')}}} -anchor e")
rE(f"ComboBox {fsetup}.l.gui.fsize -modifycmd font_changed -textvariable fontSize -bd 1 -width 10 -justify right -editable 0")
rE(f"{fsetup}.l.gui.fsize configure -values {{7 8 9 10 11 12 13 14 15 16 17 18 19 20}}")
rE(f"label {fsetup}.l.gui.fguiL -text {{{_('GUI Font')}}} -anchor e")
rE(f"ComboBox {fsetup}.l.gui.fgui -modifycmd {{font_changed 1}} -textvariable guiFont -bd 1 -width 10 -justify right -editable 0")
for font in sorted(fontsGui):
rE(f"{fsetup}.l.gui.fgui insert end {{{font}}}")
rE(f"label {fsetup}.l.gui.fcodeL -text {{{_('Gcode Font')}}} -anchor e")
rE(f"ComboBox {fsetup}.l.gui.fcode -modifycmd font_changed -textvariable codeFont -bd 1 -width 10 -justify right -editable 0")
populate_gcode_fonts()
rE(f"label {fsetup}.l.gui.coneL -text {{{_('Cone Size')}}} -anchor e")
rE(f"ComboBox {fsetup}.l.gui.cone -modifycmd cone_size_changed -textvariable coneSize -bd 1 -width 10 -justify right -editable 0")
rE(f"{fsetup}.l.gui.cone configure -values {{0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0}}")
rE(f"label {fsetup}.l.gui.popLocationL -text {{{_('Popup Location')}}} -width 14 -anchor e")
rE(f"ComboBox {fsetup}.l.gui.popLocation -textvariable popLocation -bd 1 -width 10 -justify right -editable 0")
rE(f"{fsetup}.l.gui.popLocation configure -values {{pointer gui center screen center}}")
rE(f"label {fsetup}.l.gui.zoomL -text {{{_('Table Zoom')}}} -anchor e")
rE(f"spinbox {fsetup}.l.gui.zoom -width 10 -justify right -wrap true -from 0.1 -to 10.0 -increment 0.1 -format %0.1f")
rE(f"{fsetup}.l.gui.zoom configure -validate key -vcmd {{{valspin} %W flt 1 %P %s}}")
spinBoxes.append(f"{fsetup}.l.gui.zoom")
rE(f"label {fsetup}.l.gui.jogspeedL -width 14 -text {{{_('Jog Speed')}}} -anchor e")
rE(f"spinbox {fsetup}.l.gui.jogspeed -width 10 -justify right -wrap true")
minJogSpeed = int(float(rE(f"{ftop}.jogspeed.s cget -from")) * vars.max_speed.get() * 60)
rE(f"{fsetup}.l.gui.jogspeed configure -from {minJogSpeed} -to {vars.max_speed.get() * 60} -increment 1")
rE(f"{fsetup}.l.gui.jogspeed configure -format %0.0f")
rE(f"{fsetup}.l.gui.jogspeed configure -validate key -vcmd {{{valspin} %W int 0 %P %s}}")
spinBoxes.append(f"{fsetup}.l.gui.jogspeed")
rE(f"label {fsetup}.l.gui.crspeedL -width 14 -text {{{_('Cut Rec Speed %')}}} -anchor e")
rE(f"spinbox {fsetup}.l.gui.crspeed -width 10 -justify right -wrap true")
minJogSpeed = int(float(rE(f"{ftop}.jogspeed.s cget -from")) * vars.max_speed.get() * 60)
rE(f"{fsetup}.l.gui.crspeed configure -from 1 -to 100 -increment 1")
rE(f"{fsetup}.l.gui.crspeed configure -format %0.0f")
rE(f"{fsetup}.l.gui.crspeed configure -validate key -vcmd {{{valspin} %W int 0 %P %s}}")
spinBoxes.append(f"{fsetup}.l.gui.crspeed")
rE(f"label {fsetup}.l.gui.matdefaultL -text {{{_('Default Material')}}} -width 14 -anchor e")
rE(f"ComboBox {fsetup}.l.gui.matdefault -modifycmd change_default_material -textvariable matDefault -bd 1 -width 10 -justify right -editable 0")
rE(f"label {fsetup}.l.gui.kbShortcutsL -text {{{_('Use KB Shortcuts')}}} -anchor e")
rE(f"checkbutton {fsetup}.l.gui.kbShortcuts -variable kbShortcuts -command kb_shortcuts_changed -width 2 -anchor w -indicatoron 0")
rE(f"label {fsetup}.l.gui.useVirtKBL -text {{{_('Use Virtual KB')}}} -anchor e")
rE(f"checkbutton {fsetup}.l.gui.useVirtKB -variable useVirtKB -command virtual_kb_changed -width 2 -anchor w -indicatoron 0")
# populate gui frame
rE(f"grid {fsetup}.l.gui.closedialogL -column 0 -row 0 -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fsetup}.l.gui.closedialog -column 1 -row 0 -sticky e -padx {{0 4}} -pady {{4 0}}")
rE(f"grid {fsetup}.l.gui.wsizeL -column 0 -row 1 -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fsetup}.l.gui.wsize -column 1 -row 1 -sticky ew -padx {{0 4}} -pady {{4 0}}")
rE(f"grid {fsetup}.l.gui.orientL -column 0 -row 2 -sticky e -padx {{4 0}} -pady {{4 0}}")
rE(f"grid {fsetup}.l.gui.orient -column 1 -row 2 -sticky ew -padx {{0 4}} -pady {{4 0}}")
rE(f"grid {fsetup}.l.gui.fsizeL -column 0 -row 3 -sticky e -padx {{4 0}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.fsize -column 1 -row 3 -sticky ew -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.fguiL -column 0 -row 4 -sticky e -padx {{4 0}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.fgui -column 1 -row 4 -sticky ew -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.fcodeL -column 0 -row 5 -sticky e -padx {{4 0}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.fcode -column 1 -row 5 -sticky ew -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.coneL -column 0 -row 6 -sticky e -padx {{4 0}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.cone -column 1 -row 6 -sticky ew -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.popLocationL -column 0 -row 7 -sticky e -padx {{4 0}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.popLocation -column 1 -row 7 -sticky ew -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.zoomL -column 0 -row 8 -sticky e -padx {{4 0}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.zoom -column 1 -row 8 -sticky ew -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.jogspeedL -column 0 -row 9 -sticky e -padx {{4 0}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.jogspeed -column 1 -row 9 -sticky ew -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.crspeedL -column 0 -row 10 -sticky e -padx {{4 0}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.crspeed -column 1 -row 10 -sticky ew -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.matdefaultL -column 0 -row 11 -sticky e -padx {{4 0}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.matdefault -column 1 -row 11 -sticky ew -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.kbShortcutsL -column 0 -row 12 -sticky e -padx {{4 0}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.kbShortcuts -column 1 -row 12 -sticky e -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.useVirtKBL -column 0 -row 13 -sticky e -padx {{4 0}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.gui.useVirtKB -column 1 -row 13 -sticky e -padx {{0 4}} -pady {{4 4}}")
rE(f"grid columnconfigure {fsetup}.l.gui 0 -weight 1")
# plasmac frame
rE(f"labelframe {fsetup}.l.plasmac -text {{{_('Plasmac')}}} -relief groove")
rE(f"label {fsetup}.l.plasmac.modeL -width 13 -text {{{_('Mode')}}} -anchor e")
rE(f"ComboBox {fsetup}.l.plasmac.mode -modifycmd mode_changed -textvariable plasmacMode -bd 1 -width 12 -justify right -editable 0")
rE(f"{fsetup}.l.plasmac.mode configure -values {{0 1 2}}")
# populate plasmac frame
rE(f"grid {fsetup}.l.plasmac.modeL -column 0 -row 0 -sticky e -padx {{4 0}} -pady {{4 4}}")
rE(f"grid {fsetup}.l.plasmac.mode -column 1 -row 0 -sticky ew -padx {{0 4}} -pady {{4 4}}")
rE(f"grid columnconfigure {fsetup}.l.plasmac 0 -weight 1")
#populate left panel
rE(f"grid {fsetup}.l.gui -column 0 -row 0 -sticky new")
rE(f"grid {fsetup}.l.plasmac -column 0 -row 2 -sticky new")
# middle panel for utilities
rE(f"frame {fsetup}.m")
# utilities frame
rE(f"labelframe {fsetup}.m.utilities -text {{{_('Utilities')}}} -relief groove")
rE(f"button {fsetup}.m.utilities.offsets -command set_peripheral_offsets -text {{{_('Peripheral Offsets')}}} -padx 0")
# populate utilities frame
rE(f"grid {fsetup}.m.utilities.offsets -column 0 -row 0 -sticky new -padx 4 -pady {{0 4}}")
rE(f"grid columnconfigure {fsetup}.m.utilities 0 -weight 1")
# color frame
rE(f"labelframe {fsetup}.m.colors -text {{{_('Colors')}}} -relief groove")
rE(f"label {fsetup}.m.colors.foreL -width 13 -text {{{_('Foreground')}}} -anchor e")
rE(f"button {fsetup}.m.colors.fore -command {{color_set fore}}")
rE(f"label {fsetup}.m.colors.backL -width 13 -text {{{_('Background')}}} -anchor e")
rE(f"button {fsetup}.m.colors.back -command {{color_set back}}")
rE(f"label {fsetup}.m.colors.disableL -width 13 -text {{{_('Disabled')}}} -anchor e")
rE(f"button {fsetup}.m.colors.disable -command {{color_set disable}}")
rE(f"label {fsetup}.m.colors.activeL -width 13 -text {{{_('Active')}}} -anchor e")
rE(f"button {fsetup}.m.colors.active -command {{color_set active}}")
rE(f"label {fsetup}.m.colors.warnL -width 13 -text {{{_('Warning')}}} -anchor e")
rE(f"button {fsetup}.m.colors.warn -command {{color_set warn}}")
rE(f"label {fsetup}.m.colors.voltL -width 13 -text {{{_('Arc Voltage')}}} -anchor e")
rE(f"button {fsetup}.m.colors.volt -command {{color_set volt}}")
rE(f"label {fsetup}.m.colors.arcL -width 13 -text {{{_('Arc OK')}}} -anchor e")
rE(f"button {fsetup}.m.colors.arc -command {{color_set arc}}")
rE(f"label {fsetup}.m.colors.ledL -width 13 -text {{{_('LED''s')}}} -anchor e")
rE(f"button {fsetup}.m.colors.led -command {{color_set led}}")
rE(f"label {fsetup}.m.colors.troughL -width 13 -text {{{_('Slider Trough')}}} -anchor e")
rE(f"button {fsetup}.m.colors.trough -command {{color_set trough}}")
# populate color frame
rE(f"grid {fsetup}.m.colors.foreL -column 0 -row 0 -sticky e -padx 4 -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.fore -column 1 -row 0 -sticky e -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.backL -column 0 -row 1 -sticky e -padx 4 -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.back -column 1 -row 1 -sticky e -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.disableL -column 0 -row 2 -sticky e -padx 4 -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.disable -column 1 -row 2 -sticky e -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.activeL -column 0 -row 3 -sticky e -padx 4 -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.active -column 1 -row 3 -sticky e -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.warnL -column 0 -row 4 -sticky e -padx 4 -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.warn -column 1 -row 4 -sticky e -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.voltL -column 0 -row 5 -sticky e -padx 4 -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.volt -column 1 -row 5 -sticky e -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.arcL -column 0 -row 6 -sticky e -padx 4 -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.arc -column 1 -row 6 -sticky e -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.ledL -column 0 -row 7 -sticky e -padx 4 -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.led -column 1 -row 7 -sticky e -padx {{0 4}} -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.troughL -column 0 -row 8 -sticky e -padx 4 -pady {{4 4}}")
rE(f"grid {fsetup}.m.colors.trough -column 1 -row 8 -sticky e -padx {{0 4}} -pady {{4 4}}")
rE(f"grid columnconfigure {fsetup}.m.colors 0 -weight 1")
# populate middle panel
rE(f"grid {fsetup}.m.utilities -column 0 -row 0 -sticky new")
rE(f"grid {fsetup}.m.colors -column 0 -row 1 -sticky new")
# right panel for text entries
rE(f"frame {fsetup}.r")
# frame for torch enable
rE(f"labelframe {fsetup}.r.torch -text {{{_('Torch Enable Text')}}} -relief groove")
rE(f"label {fsetup}.r.torch.blankL -width 2 -anchor w")
rE(f"label {fsetup}.r.torch.enabledL -text {{{_('Enabled')}}} -width 14 -anchor w")
rE(f"label {fsetup}.r.torch.disabledL -text {{{_('Disabled')}}} -width 14 -anchor w")
rE(f"entry {fsetup}.r.torch.enabled -bd 1 -width 14")
rE(f"entry {fsetup}.r.torch.disabled -bd 1 -width 14")
rE(f"grid {fsetup}.r.torch.blankL -column 0 -row 0 -sticky nw -padx {{4 0}}")
rE(f"grid {fsetup}.r.torch.enabledL -column 1 -row 0 -sticky nw -padx {{4 0}}")
rE(f"grid {fsetup}.r.torch.disabledL -column 2 -row 0 -sticky nw -padx 4")
rE(f"grid {fsetup}.r.torch.enabled -column 1 -row 1 -sticky nw -padx {{4 0}} -pady {{0 4}}")
rE(f"grid {fsetup}.r.torch.disabled -column 2 -row 1 -sticky nw -padx 4 -pady {{0 4}}")
# frame for user buttons
rE(f"labelframe {fsetup}.r.ubuttons -text {{{_('User Buttons')}}} -relief groove")
# canvas for scrolling
rE(f"canvas {fsetup}.r.ubuttons.canvas")
rE(f"frame {fsetup}.r.ubuttons.canvas.frame")
rE(f"scrollbar {fsetup}.r.ubuttons.yscroll -orient vertical -command {{{fsetup}.r.ubuttons.canvas yview}}")
rE(f"scrollbar {fsetup}.r.ubuttons.xscroll -orient horizontal -command {{{fsetup}.r.ubuttons.canvas xview}}")
rE(f"{fsetup}.r.ubuttons.canvas create window {{0 0}} -anchor nw -window {{{fsetup}.r.ubuttons.canvas.frame}}")
rE(f"{fsetup}.r.ubuttons.canvas configure -xscrollcommand {{{fsetup}.r.ubuttons.xscroll set}}")
rE(f"{fsetup}.r.ubuttons.canvas configure -yscrollcommand {{{fsetup}.r.ubuttons.yscroll set}}")
# layout the canvas
rE(f"grid {fsetup}.r.ubuttons.yscroll -column 0 -row 0 -sticky nsw -padx {{4 0}}")
rE(f"grid {fsetup}.r.ubuttons.canvas -column 1 -row 0 -sticky nsew")
rE(f"grid {fsetup}.r.ubuttons.xscroll -column 1 -row 1 -sticky new -padx {{4 0}}")
rE(f"grid columnconfigure {fsetup}.r.ubuttons 1 -weight 1")
rE(f"grid rowconfigure {fsetup}.r.ubuttons 0 -weight 1")
# user button widgets
rE(f"label {fsetup}.r.ubuttons.canvas.frame.numL -text # -width 2 -anchor e")
rE(f"label {fsetup}.r.ubuttons.canvas.frame.nameL -text {{{_('Name')}}} -width 14 -anchor w")
rE(f"label {fsetup}.r.ubuttons.canvas.frame.codeL -text {{{_('Code')}}} -width 60 -anchor w")
for n in range(1, maxUserButtons + 1):
rE(f"label {fsetup}.r.ubuttons.canvas.frame.num{n} -text {str(n)} -anchor e")
rE(f"entry {fsetup}.r.ubuttons.canvas.frame.name{n} -bd 1 -width 14")
rE(f"entry {fsetup}.r.ubuttons.canvas.frame.code{n} -bd 1")
rE(f"grid {fsetup}.r.ubuttons.canvas.frame.num{n} -column 0 -row {n} -sticky ne -padx {{4 0}} -pady {{0 4}}")
rE(f"grid {fsetup}.r.ubuttons.canvas.frame.name{n} -column 1 -row {n} -sticky nw -padx {{4 0}} -pady {{0 4}}")
rE(f"grid {fsetup}.r.ubuttons.canvas.frame.code{n} -column 2 -row {n} -sticky new -padx {{4 4}} -pady {{0 4}}")
rE(f"grid {fsetup}.r.ubuttons.canvas.frame.numL -column 0 -row 0 -sticky ne -padx {{4 0}}")
rE(f"grid {fsetup}.r.ubuttons.canvas.frame.nameL -column 1 -row 0 -sticky nw -padx {{4 0}}")
rE(f"grid {fsetup}.r.ubuttons.canvas.frame.codeL -column 2 -row 0 -sticky nw -padx {{4 4}}")
rE(f"grid columnconfigure {fsetup}.r.ubuttons.canvas.frame 2 -weight 1")
# frame for shutdown message
rE(f"labelframe {fsetup}.r.shutdown -text {{{_('Shutdown Message')}}} -relief groove")
rE(f"entry {fsetup}.r.shutdown.msg -textvariable closeText -bd 1 -width 54")
rE(f"grid {fsetup}.r.shutdown.msg -column 0 -row 0 -sticky new -padx 4 -pady {{0 4}}")
rE(f"grid columnconfigure {fsetup}.r.shutdown 0 -weight 1")
# populate right panel
rE(f"grid {fsetup}.r.torch -column 0 -row 0 -sticky new")
rE(f"grid {fsetup}.r.ubuttons -column 0 -row 1 -sticky nsew")
rE(f"grid {fsetup}.r.shutdown -column 0 -row 2 -sticky new")
rE(f"grid rowconfigure {fsetup}.r 1 -weight 1")
rE(f"grid columnconfigure {fsetup}.r 0 -weight 1")
# populate settings frame
populate_settings_frame()
# bottom pane widgets
# configure
rE(f"{ft} configure -relief flat -pady 0 -bd 0")
rE(f"{ft}.sb configure -bd 1")
rE(f"{ft}.text configure -width 10 -height 6 -borderwidth 1 -relief sunken")
# populate
rE(f"grid {fleds} -column 0 -row 1 -padx {{2 0}} -pady {{0 2}} -sticky nsew")
rE(f"grid {ft} -padx {{0 2}} -pady {{0 2}}")
rE(f"pack {ft}.sb -fill y -side left -padx 1")
rE(f"pack {ft}.text -fill both -expand 1 -side left -pady 0")
# new conversational frames
rE('frame .toolconv -borderwidth 1 -relief raised')
rE('frame .fconv -relief flat')
# change jog slider resolution
rE('.pane.top.jogspeed.s configure -resolution 0.00001')
# remove all tooltips
rE('DynamicHelp::delete ftabs_manual.axes.axisx')
for w in toolButtons:
rE(f"DynamicHelp::delete .toolbar.{w}")
# populate run panel
populate_run_panel()
##############################################################################
# INITIALIZATION #
##############################################################################
# reinitialize notifications to keep them on top of new widgets
notify_list = []
for nw in rE(f"winfo children {notifications}").split():
for n in rE(f"winfo children {nw}").split():
if n[-5:] == 'label':
img = rE(f"{n} cget -image").rsplit('_',1)[1]
elif n[-6:] == 'label2':
txt = rE(f"{n} cget -text")
notify_list.append([img, txt])
notifications.clear()
notifications.__init__(root_window)
if notify_list:
for n in notify_list:
notifications.add(n[0], n[1])
update_title()
o.show_overlay = False
o.colors['overlay_alpha'] = 0
pVars.jogMultiplier.set(1)
hal.set_p('plasmac.mode', f"{pVars.plasmacMode.get()}")
hal.set_p('plasmac.torch-enable', '0')
hal.set_p('plasmac.height-override', f"{torchHeight:0.3f}")
hal.set_p('plasmac.thc-feed-rate', f"{thcFeedRate}")
pVars.previewLarge.set(False)
pVars.laserText.set(_('Laser'))
value = getPrefs(PREF, 'ENABLE_OPTIONS', 'THC auto', False, bool)
pVars.thcAuto.set(value)
value = getPrefs(PREF, 'ENABLE_OPTIONS', 'THC enable', False, bool)
pVars.thcEnable.set(value)
hal.set_p('plasmac.thc-enable', str(value))
value = getPrefs(PREF, 'ENABLE_OPTIONS', 'Corner lock enable', False, bool)
pVars.cornerEnable.set(value)
hal.set_p('plasmac.cornerlock-enable', str(value))
value = getPrefs(PREF, 'ENABLE_OPTIONS', 'Void lock enable', False, bool)
pVars.voidEnable.set(value)
hal.set_p('plasmac.voidlock-enable', str(value))
value = getPrefs(PREF, 'ENABLE_OPTIONS', 'Use auto volts', False, bool)
pVars.autoVolts.set(value)
hal.set_p('plasmac.use-auto-volts', str(value))
value = getPrefs(PREF, 'ENABLE_OPTIONS', 'Ohmic probe enable', False, bool)
pVars.ohmicEnable.set(value)
hal.set_p('plasmac.ohmic-probe-enable', str(value))
value = getPrefs(PREF, 'GUI_OPTIONS', 'Jog speed', int(vars.max_speed.get() * 60 * 0.5), int)
rE(f"{fsetup}.l.gui.jogspeed set {value}")
restoreSetup['jogSpeed'] = value
set_jog_slider(value / vars.max_speed.get() / 60)
value = getPrefs(PREF, 'GUI_OPTIONS', 'Exit warning text', '', str)
pVars.closeText.set(value)
restoreSetup['closeText'] = value
value = getPrefs(PREF, 'GUI_OPTIONS', 'Exit warning', True, bool)
pVars.closeDialog.set(value)
restoreSetup['closeDialog'] = value
root_window.protocol('WM_DELETE_WINDOW', close_window)
value = getPrefs(PREF, 'GUI_OPTIONS', 'Preview cone size', 0.5, float)
pVars.coneSize.set(value)
restoreSetup['coneSize'] = value
cone_size_changed()
value = getPrefs(PREF, 'GUI_OPTIONS', 'Popup location', 'pointer', str)
pVars.popLocation.set(value)
restoreSetup['popLocation'] = value
value = getPrefs(PREF, 'GUI_OPTIONS', 'Table zoom', 1, float)
rE(f"{fsetup}.l.gui.zoom set {value}")
restoreSetup['tableZoom'] = value
value = getPrefs(PREF, 'GUI_OPTIONS','Cut recovery speed %', 20, int)
restoreSetup['crPercent'] = value
rE(f"{fsetup}.l.gui.crspeed set {value}")
# check for valid onboard virtual keyboard
vkb_validate()
if vkbData['valid']:
value = getPrefs(PREF, 'GUI_OPTIONS', 'Use soft keyboard', False, bool)
else:
value = False
pVars.useVirtKB.set(value)
restoreSetup['useVirtKB'] = value
if vkbData['valid']:
virtual_kb_changed()
# disable keyboard shortcuts if virtual keyboard enabled
if value:
value = False
rE(f"{fsetup}.l.gui.kbShortcutsL configure -state disabled")
rE(f"{fsetup}.l.gui.kbShortcuts configure -state disabled")
else:
value = getPrefs(PREF, 'GUI_OPTIONS', 'Use keyboard shortcuts', True, bool)
pVars.kbShortcuts.set(value)
restoreSetup['kbShortcuts'] = value
keyboard_bindings(value)
statDivisor = 1000 if unitsPerMm == 1 else 1
statSuffix = 'M' if unitsPerMm == 1 else '"'
unitSuffix = 'mm' if unitsPerMm == 1 else 'in'
# we bring in some ints as floats so we can read QtPlasmaC statistics
pVars.lengthS.set(getPrefs(PREF, 'STATISTICS', 'Cut length', 0, float))
pVars.lengthJ.set(f"0.00{statSuffix}")
pVars.lengthT.set(f"{pVars.lengthS.get() / statDivisor:0.2f}{statSuffix}")
pVars.pierceS.set(getPrefs(PREF, 'STATISTICS', 'Pierce count', 0, int))
pVars.pierceJ.set('0')
pVars.pierceT.set(pVars.pierceS.get())
pVars.rapidS.set(int(getPrefs(PREF, 'STATISTICS', 'Rapid time', 0, float)))
pVars.rapidJ.set('00:00:00')
pVars.rapidT.set(secs_to_hms(pVars.rapidS.get()))
pVars.probeS.set(int(getPrefs(PREF, 'STATISTICS', 'Probe time', 0, float)))
pVars.probeJ.set('00:00:00')
pVars.probeT.set(secs_to_hms(pVars.probeS.get()))
pVars.torchS.set(int(getPrefs(PREF, 'STATISTICS', 'Torch on time', 0, float)))
pVars.torchJ.set('00:00:00')
pVars.torchT.set(secs_to_hms(pVars.torchS.get()))
pVars.pausedS.set(int(getPrefs(PREF, 'STATISTICS', 'Paused time', 0, float)))
pVars.pausedJ.set('00:00:00')
pVars.pausedT.set(secs_to_hms(pVars.pausedS.get()))
pVars.cutS.set(int(getPrefs(PREF, 'STATISTICS', 'Cut time', 0, float)))
pVars.cutJ.set('00:00:00')
pVars.cutT.set(secs_to_hms(pVars.cutS.get()))
pVars.runS.set(int(getPrefs(PREF, 'STATISTICS', 'Program run time', 0, float)))
pVars.runJ.set('00:00:00')
pVars.runT.set(secs_to_hms(pVars.runS.get()))
laser_button_enable()
offset_probe_setup()
for widget in wLeds:
rE(f"{widget} configure -state disabled")
widgetValues[widget] = 0
load_param_clicked()
mode_changed()
font_changed()
ucFile = os.path.join(configPath, 'user_commands.py')
if os.path.isfile(ucFile):
exec(compile(open(ucFile, "rb").read(), ucFile, 'exec'))
else:
firstRun = 'invalid'
title = _('LOAD ERROR')
msg0 = _('Cannot find valid library directory')
msg1 = _('plasmac2 extensions are not loaded')
sys.exit()
##############################################################################
# HAL SETUP - CALLED DIRECTLY FROM AXIS ONCE AT STARTUP #
##############################################################################
def user_hal_pins():
global firstRun, previewSize, halPinList, serial, comPorts
global materialChangePin, materialChangeNumberPin, materialChangeTimeoutPin
global materialReloadPin, materialTempPin
if firstRun == 'invalid':
return
# create new hal pins
comp.newpin('arc-voltage', hal.HAL_FLOAT, hal.HAL_IN)
comp.newpin('led-arc-ok', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('led-torch', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('led-thc-enabled', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('led-ohmic', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('led-float', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('led-breakaway', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('led-thc-active', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('led-up', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('led-down', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('led-corner-locked', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('led-void-locked', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('refresh', hal.HAL_S32, hal.HAL_IN)
comp.newpin('material-change-number', hal.HAL_S32, hal.HAL_IN)
comp.newpin('material-change', hal.HAL_S32, hal.HAL_IN)
comp.newpin('material-change-timeout', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('material-reload', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('material-temp', hal.HAL_S32, hal.HAL_IN)
comp.newpin('cut-type', hal.HAL_S32, hal.HAL_IN)
comp.newpin('thc-enable-out', hal.HAL_BIT, hal.HAL_OUT)
comp.newpin('program-is-idle', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('laser-on', hal.HAL_BIT, hal.HAL_OUT)
comp.newpin('preview-tab', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('development', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('conv-block-loaded', hal.HAL_BIT, hal.HAL_IN)
comp.newpin('offset-set-probe', hal.HAL_BIT, hal.HAL_OUT)
comp.newpin('offset-set-scribe', hal.HAL_BIT, hal.HAL_OUT)
ext_hal_create()
# create some new signals and connect pins
hal_data = [[0,'plasmac:arc-voltage-out','plasmac.arc-voltage-out','axisui.arc-voltage'],\
[1,'plasmac:axis-x-min-limit','ini.x.min_limit','plasmac.axis-x-min-limit'],\
[2,'plasmac:axis-x-max-limit','ini.x.max_limit','plasmac.axis-x-max-limit'],\
[3,'plasmac:axis-y-min-limit','ini.y.min_limit','plasmac.axis-y-min-limit'],\
[4,'plasmac:axis-y-max-limit','ini.y.max_limit','plasmac.axis-y-max-limit'],\
[5,'plasmac:axis-z-min-limit','ini.z.min_limit','plasmac.axis-z-min-limit'],\
[6,'plasmac:axis-z-max-limit','ini.z.max_limit','plasmac.axis-z-max-limit'],\
[7,'plasmac:arc-ok-out','plasmac.arc-ok-out','axisui.led-arc-ok'],\
[8,'plasmac:thc-enabled','plasmac.thc-enabled','axisui.led-thc-enabled'],\
[9,'plasmac:thc-active','plasmac.thc-active','axisui.led-thc-active'],\
[10,'plasmac:led-up','plasmac.led-up','axisui.led-up'],\
[11,'plasmac:led-down','plasmac.led-down','axisui.led-down'],\
[11,'plasmac:cornerlock-is-locked','plasmac.cornerlock-is-locked','axisui.led-corner-locked'],\
[13,'plasmac:voidlock-is-locked','plasmac.voidlock-is-locked','axisui.led-void-locked'],\
[14,'plasmac:offset-set-probe','plasmac.offset-set-probe','axisui.offset-set-probe'],\
[15,'plasmac:offset-set-scribe','plasmac.offset-set-scribe','axisui.offset-set-scribe'],\
]
for line in hal_data:
if line[0] < 7:
hal.new_sig(line[1],hal.HAL_FLOAT)
else:
hal.new_sig(line[1],hal.HAL_BIT)
hal.connect(line[2],line[1])
hal.connect(line[3],line[1])
# connect pins to some existing signals
hal.connect('axisui.led-ohmic','plasmac:ohmic-probe-out')
hal.connect('axisui.led-float','plasmac:float-switch-out')
hal.connect('axisui.led-breakaway','plasmac:breakaway-switch-out')
hal.connect('axisui.led-torch','plasmac:torch-on')
# connect laser-on if it exists
laser = False
for signal in hal.get_info_signals():
if signal['NAME'] == 'plasmac:laser-on':
laser = True
break
if laser:
hal.connect('axisui.laser-on','plasmac:laser-on')
# initialize halpin variables
materialChangePin = comp['material-change']
materialChangeNumberPin = comp['material-change-number']
materialChangeTimeoutPin = comp['material-change-timeout']
materialReloadPin = comp['material-reload']
materialTempPin = comp['material-temp']
# do user button setup after hal pin creation
halPinList = hal.get_info_pins()
user_button_setup()
# load materials when setup is complete
load_materials(-1, False)
# start powermax comms if valid port
if pmPort:
global pmx485
for parm in ['compStatus','commsError','connected','exists','meshMode']:
pmx485[parm] = False
for parm in ['compArcTime','compFault','commsTimer','retryTimer','oldPressure']:
pmx485[parm] = 0.0
for parm in ['compMinC','compMaxC','compMinP','compMaxP']:
pmx485[parm] = 0.0
for parm in ['oldMode']:
pmx485[parm] = 0
try:
import serial
import serial.tools.list_ports as comPorts
if pmx485_check(pmPort):
pmx485_startup(pmPort)
except:
comPorts = None
msg0 = _('python3-serial cannot be found')
msg1 = _('Install python3-serial or linuxcnc-dev')
notifications.add('error', f"{msg0}\n{msg1}")
# check preferences for a file to load
addRecent = True
openFile = getPrefs(PREF, 'GUI_OPTIONS','Open file', '', str)
loadLast = True if openFile == 'last' else False
if loadLast:
openFile = ap.getpref('recentfiles', [], repr).pop(0) if len(ap.getpref('recentfiles', [], repr)) else None
addRecent = False
elif openFile:
openFile = os.path.join(open_directory,openFile)
# load the file
if openFile:
if os.path.exists(openFile):
open_file_guts(openFile, False, addRecent)
commands.set_view_z()
else:
msg0 = _('does not exist')
notifications.add('error', f"'{os.path.realpath(openFile)}' {msg0}\n")
# run users custom hal commands if it exists
uhFile = os.path.join(configPath, 'user_hal.py')
if os.path.isfile(uhFile):
exec(compile(open(uhFile, "rb").read(), uhFile, 'exec'))
# setup shortcuts help
install_kb_text(root_window)
install_kp_text(root_window)
# setup the about text
rE('.about.message configure -height 14')
text = f"\n\nplasmac2 extensions v{VER}\nCopyright (C) 2022, 2023\nPhillip A Carter and Gregory D Carl"
rE('.about.message configure -state normal')
rE(f".about.message insert end {{{text}}}")
rE('.about.message configure -state disabled')
previewSize = {'w':rE(f"winfo width {tabs_preview}"), 'h':rE(f"winfo height {tabs_preview}")}
##############################################################################
# PERIODIC FUNCTION - CALLED FROM AXIS EVERY CYCLE #
##############################################################################
def user_live_update():
global firstRun, upFile
# don't do any updates until first run is complete.
if firstRun:
# test this last command in axis to see if we are loaded
if widgets.numbers_text.bind() and extHalPins:
upFile = os.path.join(configPath, 'user_periodic.py')
# setup the colors
color_change()
# set the pierce offset hal pins
comp['x-pierce-offset'] = widgetValues['.param.c2.pierce.x-pierce-offset']
comp['y-pierce-offset'] = widgetValues['.param.c2.pierce.y-pierce-offset']
firstRun = None
return
if orientStart:
return
global isIdle, isIdleHomed, isPaused, isRunning
global runState, pausedState, relPos, previewSize
global probeTimer, laserButtonState
global framingState, activeFunction
global currentTool, pmx485, homeInProgress
global materialChangePin, materialChangeNumberPin
global materialReloadPin, materialTempPin
global materialChangeTimeoutPin, dryRun
# set machine state variables
isIdle = s.task_state == linuxcnc.STATE_ON and s.interp_state == linuxcnc.INTERP_IDLE
isIdleHomed = isIdle and all_homed()
isPaused = s.task_state == linuxcnc.STATE_ON and s.paused
isRunning = not s.interp_state == linuxcnc.INTERP_IDLE and not s.paused
# check external hal pins
ext_hal_watch()
# set current x and y relative positions
relPos['X'] = round(s.position[0] - s.g5x_offset[0] - s.g92_offset[0], 6)
relPos['Y'] = round(s.position[1] - s.g5x_offset[1] - s.g92_offset[1], 6)
# set display units
if o.canon:
if 200 in o.canon.state.gcodes:
vars.metric.set(0)
else:
vars.metric.set(1)
else:
vars.metric.set(s.linear_units == 1)
# program has started
if running() and not runState:
runState = True
clear_job_stats()
# program has ended
elif not running() and runState:
runState = False
save_total_stats()
# reset single-cut
if singleCut['G91']:
ensure_mode(linuxcnc.MODE_MDI)
c.mdi('G91')
# reset moving pierce
for pin in ['creep-speed', 'creep-speed-distance', 'cut-height-delay', 'gouge-speed', \
'gouge-speed-distance', 'pierce-end-height', 'pierce-motion-delay', 'pierce-type']:
hal.set_p(f'plasmac.{pin}', '0')
# override standard tool info
if current_tool.id != currentTool:
currentTool = current_tool.id
if currentTool < 1:
tool = 'Torch'
elif currentTool == 1:
tool = 'Scribe'
vupdate(pVars.plasmatool, f"Tool: {tool}")
# update status leds
for widget in wLeds:
tmp, item = widget.rsplit('.',1)
if comp[item] != widgetValues[widget]:
widgetValues[widget] = comp[item]
if comp[item] == 1:
rE(f"{widget} configure -state normal")
else:
rE(f"{widget} configure -state disabled")
# update arc voltage
rE(f"{fplasma}.arc-voltage configure -text {comp['arc-voltage']:3.0f}v")
# halpin toggle
for button in togglePins:
if hal.get_value(togglePins[button]['pin']) != togglePins[button]['state']:
set_toggle_pins(button)
# halpin pulse
for button in pulsePins:
# set button color for pulse-halpin buttons
if hal.get_value(pulsePins[button]['pin']) != pulsePins[button]['state']:
pulsePins[button]['state'] = hal.get_value(pulsePins[button]['pin'])
if pulsePins[button]['state']:
rE(f"{fbuttons}.button{button} configure -bg {colorActive}")
else:
rE(f"{fbuttons}.button{button} configure -bg {colorBack}")
# reset a consumable change
if (hal.get_value('plasmac.consumable-change') or hal.get_value('plasmac.consumable-change')) and \
(hal.get_value('axisui.abort') or not s.paused):
hal.set_p('plasmac.consumable-change', '0')
hal.set_p('plasmac.x-offset', '0')
hal.set_p('plasmac.y-offset', '0')
hal.set_p('plasmac.xy-feed-rate', '0')
rE(f"{fbuttons}.button{cChangeButton} configure -bg {colorBack}")
activeFunction = False
# abort a manual cut
if manualCut['state'] and not hal.get_value('spindle.0.on'):
manualCut['state'] = False
vars.jog_speed.set(manualCut['feed'])
rE(f".pane.top.jogspeed.s configure -state normal")
save_total_stats()
# show cut recovery tab
if s.paused and 'manual' in rE(f"{ftabs} pages"):
rE(f"{ftabs} delete manual 0")
rE(f"{ftabs} delete mdi 0")
rE(f"{ftabs} insert end cutrecs")
rE(f"{ftabs} raise cutrecs")
rE(f"{ftabs} itemconfigure cutrecs -text {{{_('Cut Recovery')}}} -background {colorBack} -foreground {colorFore}")
# hide cut recovery tab
if not s.paused and 'cutrecs' in rE(f"{ftabs} pages") and \
(not hal.get_value('plasmac.paused-motion') or \
hal.get_value('axisui.abort') or \
s.task_state == linuxcnc.STATE_ESTOP):
rE(f"{ftabs} delete cutrecs 0")
rE(f"{ftabs} insert end manual")
rE(f"{ftabs} insert end mdi")
rE(f"{ftabs} raise manual")
rE(f"{ftabs} itemconfigure manual -text {{{_('Manual')}}} -background {colorBack} -foreground {colorFore}")
rE(f"{ftabs} itemconfigure mdi -text {{{_('MDI')}}} -background {colorBack} -foreground {colorFore}")
# if resumed and component has completed a cut recovery then cancel cut recovey
if not s.paused and not hal.get_value('plasmac.cut-recovering') and hal.get_value('plasmac.cut-recovery'):
cut_rec_cancel()
# if consumable change finished then clear offsets
if hal.get_value('plasmac.state-out') == 26:
hal.set_p('plasmac.x-offset', '0')
hal.set_p('plasmac.y-offset', '0')
# if resumed and laser recovery is active then undo laser offset
if not s.paused and hal.get_value('plasmac.laser-recovery-state') and hal.get_value('plasmac.laser-recovery-start'):
hal.set_p('plasmac.laser-recovery-start', '0')
# reset framing when complete
if framingState and not hal.get_value('motion.motion-type'):
comp['laser-on'] = 0
framingState = False
activeFunction = False
live_plotter.clear()
# dry run button state
wState = 'disabled' if rE('.toolbar.program_run cget -state') == 'disabled' or not loaded_file else 'normal'
rE(f"{fbuttons}.dry-run configure -state {wState}")
# reset after dry run
if dryRun and isIdle:
oldX = s.actual_position[0] - dryRun[0]
oldY = s.actual_position[1] - dryRun[1]
units = '20' if s.linear_units != 1 else '21'
ensure_mode(linuxcnc.MODE_MDI)
c.mdi(f'G{units} G10 L20 P0 X{oldX} Y{oldY}')
c.wait_complete()
dryRun = None
comp['laser-on'] = 0
hal.set_p('plasmac.dry-run', '0')
live_plotter.clear()
# set X0Y0, laser, and offset_setup buttons state
wState = 'normal' if isIdleHomed else 'disabled'
rE(f"{fjogf}.zerohome.zeroxy configure -state {wState}")
rE(f"{fjogf}.zerohome.laser configure -state {wState}")
rE(f"{fsetup}.m.utilities.offsets configure -state {wState}")
# set height override buttons state
wState = 'normal' if isIdle or isPaused or isRunning else 'disabled'
rE(f"{foverride}.raise configure -state {wState}")
rE(f"{foverride}.lower configure -state {wState}")
rE(f"{foverride}.reset configure -state {wState}")
# set material panel state
wState = 'normal' if not (isPaused or isRunning) else 'disabled'
rE(f"{fruns}.material.materials configure -state {wState}")
rE(f"{fruns}.material.cut-feed-rate configure -state {wState}")
for child in get_all_children(toolmat):
rE(f"{child} configure -state {wState}")
for n in range(1, maxUserButtons + 1):
if buttonCodes[n]['code']:
if buttonCodes[n]['code'] == 'ohmic-test':
if (isIdle or isPaused) and hal.get_value('plasmac.ohmic-probe-enable'):
rE(f"{fbuttons}.button{n} configure -state normal")
else:
rE(f"{fbuttons}.button{n} configure -state disabled")
elif buttonCodes[n]['code'] == 'cut-type':
if not isRunning and not isPaused:
rE(f"{fbuttons}.button{n} configure -state normal")
else:
rE(f"{fbuttons}.button{n} configure -state disabled")
elif buttonCodes[n]['code'] == 'single-cut':
if isIdleHomed and not activeFunction and str(rE('.toolbar.program_run cget -state')) == 'normal':
rE(f"{fbuttons}.button{n} configure -state normal")
else:
rE(f"{fbuttons}.button{n} configure -state disabled")
elif buttonCodes[n]['code'] == 'manual-cut':
if isIdleHomed and not activeFunction and str(rE('.toolbar.program_run cget -state')) == 'normal':
rE(f"{fbuttons}.button{n} configure -state normal")
else:
rE(f"{fbuttons}.button{n} configure -state disabled")
elif buttonCodes[n]['code'] == 'probe-test':
if isIdleHomed and (not activeFunction or probeTimer):
rE(f"{fbuttons}.button{n} configure -state normal")
else:
rE(f"{fbuttons}.button{n} configure -state disabled")
elif buttonCodes[n]['code'] == 'torch-pulse':
if (isIdleHomed or isPaused) and not activeFunction and hal.get_value('plasmac.torch-enable'):
rE(f"{fbuttons}.button{n} configure -state normal")
else:
rE(f"{fbuttons}.button{n} configure -state disabled")
elif buttonCodes[n]['code'] == 'change-consumables':
if isPaused and (not activeFunction or hal.get_value('plasmac.consumable-change')):
rE(f"{fbuttons}.button{n} configure -state normal")
else:
rE(f"{fbuttons}.button{n} configure -state disabled")
elif buttonCodes[n]['code'] == 'framing':
if isIdleHomed and not activeFunction:
rE(f"{fbuttons}.button{n} configure -state normal")
else:
rE(f"{fbuttons}.button{n} configure -state disabled")
elif buttonCodes[n]['code'] == 'load':
if isIdleHomed and not activeFunction:
rE(f"{fbuttons}.button{n} configure -state normal")
else:
rE(f"{fbuttons}.button{n} configure -state disabled")
elif buttonCodes[n]['code'] == 'latest-file':
if isIdleHomed and not activeFunction:
rE(f"{fbuttons}.button{n} configure -state normal")
else:
rE(f"{fbuttons}.button{n} configure -state disabled")
elif buttonCodes[n]['code'] == 'pulse-halpin':
if hal.get_value('halui.machine.is-on'):
rE(f"{fbuttons}.button{n} configure -state normal")
else:
rE(f"{fbuttons}.button{n} configure -state disabled")
elif buttonCodes[n]['code'] == 'toggle-halpin':
if hal.get_value('halui.machine.is-on'):
rE(f"{fbuttons}.button{n} configure -state normal")
else:
rE(f"{fbuttons}.button{n} configure -state disabled")
elif 'shell' in buttonCodes[n]['code'][0]:
if hal.get_value('halui.machine.is-on'):
rE(f"{fbuttons}.button{n} configure -state normal")
else:
rE(f"{fbuttons}.button{n} configure -state disabled")
else:
if isIdleHomed and not activeFunction:
rE(f"{fbuttons}.button{n} configure -state normal")
else:
rE(f"{fbuttons}.button{n} configure -state disabled")
wState = 'normal' if not (isPaused or isRunning) else 'disabled'
rE(f"{fsetup}.r.ubuttons.canvas.frame.name{n} configure -state {wState}")
rE(f"{fsetup}.r.ubuttons.canvas.frame.code{n} configure -state {wState}")
# material handling
if materialChangePin != comp['material-change']:
materialChangePin = comp['material-change']
material_change_pin_changed(materialChangePin)
if materialChangeNumberPin != comp['material-change-number']:
materialChangeNumberPin = comp['material-change-number']
material_change_number_pin_changed(materialChangeNumberPin)
if materialChangeTimeoutPin != comp['material-change-timeout']:
materialChangeTimeoutPin = comp['material-change-timeout']
material_change_timeout_pin_changed(materialChangeTimeoutPin)
if materialReloadPin != comp['material-reload']:
materialReloadPin = comp['material-reload']
material_reload_pin_changed(materialReloadPin)
if materialTempPin != comp['material-temp']:
materialTempPin = comp['material-temp']
material_temp_pin_changed(materialTempPin)
# try to show the preview tab if hal pin is set
if comp['preview-tab']:
try:
rE('.pane.top.right raise preview')
comp['preview-tab'] = 0
except:
pass
# allows user to set a HAL pin to initiate the sequence of:
# reloading the program
if comp['refresh'] == 1:
comp['refresh'] = 2
commands.reload_file()
# clearing the live plot
elif comp['refresh'] == 2:
comp['refresh'] = 3
commands.clear_live_plot()
# rezooming the axis
elif comp['refresh'] == 3:
comp['refresh'] = 0
commands.set_view_z()
# powermax signals
if pmx485['exists']:
if pmx485['compStatus'] != hal.get_value('pmx485.status'):
pmx485['compStatus'] = hal.get_value('pmx485.status')
pmx485_status_changed(pmx485['compStatus'])
if pmx485['compFault'] != hal.get_value('pmx485.fault'):
pmx485['compFault'] = hal.get_value('pmx485.fault')
pmx485_fault_changed(pmx485['compFault'])
if pmx485['compArcTime'] != hal.get_value('pmx485.arcTime'):
pmx485['compArcTime'] = hal.get_value('pmx485.arcTime')
pVars.arcT.set(secs_to_hms(pmx485['compArcTime']))
if pmx485['compMinC'] != hal.get_value('pmx485.current_min'):
pmx485['compMinC'] = hal.get_value('pmx485.current_min')
pmx485_min_current_changed()
if pmx485['compMaxC'] != hal.get_value('pmx485.current_max'):
pmx485['compMaxC'] = hal.get_value('pmx485.current_max')
pmx485_max_current_changed()
if pmx485['compMaxP'] != hal.get_value('pmx485.pressure_max'):
pmx485['compMaxP'] = hal.get_value('pmx485.pressure_max')
pmx485_max_pressure_changed()
if pmx485['commsTimer']:
pmx485['commsTimer'] -= 0.1
if pmx485['commsTimer'] <= 0:
pmx485['commsTimer'] = 0
pmx485_comms_timeout()
if pmx485['retryTimer']:
pmx485['retryTimer'] -= 0.1
if pmx485['retryTimer'] <= 0:
pmx485['retryTimer'] = 0
pmx485_retry_timeout()
# hide/show override limits checkbox
if vars.on_any_limit.get() and not rE(f"winfo ismapped {flimitovr}"):
homeInProgress = False
for j in range(jointcount):
if hal.get_value(f"joint.{j}.homing"):
homeInProgress = True
break
if not homeInProgress:
rE(f"grid {flimitovr} -row 1 -column 0 -columnspan 3 -sticky nsew")
elif not vars.on_any_limit.get() and rE(f"winfo ismapped {flimitovr}"):
rE(f"{flimitovr}.button invoke")
rE(f"grid forget {flimitovr}")
# hide/show jog inhibit override checkbox
if rE(f"winfo ismapped {fjogiovr}"):
if pVars.jogInhibitOvr.get():
hal.set_p('plasmac.override-jog', '1')
if not hal.get_value('plasmac.breakaway') and \
not hal.get_value('plasmac.float-switch') and \
not hal.get_value('plasmac.ohmic-probe'):
pVars.jogInhibitOvr.set(False)
rE(f"grid forget {fjogiovr}")
else:
if hal.get_value('plasmac.jog-inhibit'):
if isIdleHomed:
rE(f"grid {fjogiovr} -column 0 -row 1 -columnspan 3 -sticky nsew")
else:
if not hal.get_value('plasmac.breakaway') and \
not hal.get_value('plasmac.float-switch') and \
not hal.get_value('plasmac.ohmic-probe'):
hal.set_p('plasmac.override-jog', '0')
# statistics
if runState or manualCut['state']:
# cut length
sNow = int(hal.get_value('plasmac.cut-length') + 0.5)
if statValues['length'] != sNow:
if sNow:
pVars.lengthJ.set(f"{sNow / statDivisor:0.2f}{statSuffix}")
if hal.get_value('plasmac.torch-enable'):
pVars.lengthT.set(f"{(sNow + pVars.lengthS.get()) / statDivisor:0.2f}{statSuffix}")
statValues['length'] = sNow
# pierce count
sNow = hal.get_value('plasmac.pierce-count')
if statValues['pierce'] != sNow:
if sNow:
pVars.pierceJ.set(sNow)
if hal.get_value('plasmac.torch-enable'):
pVars.pierceT.set(sNow + pVars.pierceS.get())
statValues['pierce'] = sNow
# rapid time
sNow = int(hal.get_value('plasmac.rapid-time') + 0.5)
if statValues['rapid'] != sNow:
if sNow:
pVars.rapidJ.set(secs_to_hms(sNow))
if hal.get_value('plasmac.torch-enable'):
pVars.rapidT.set(secs_to_hms(sNow + pVars.rapidS.get()))
statValues['rapid'] = sNow
# probe time
sNow = int(hal.get_value('plasmac.probe-time') + 0.5)
if statValues['probe'] != sNow:
if sNow:
pVars.probeJ.set(secs_to_hms(sNow))
if hal.get_value('plasmac.torch-enable'):
pVars.probeT.set(secs_to_hms(sNow + pVars.probeS.get()))
statValues['probe'] = sNow
# torch time
sNow = int(hal.get_value('plasmac.torch-time') + 0.5)
if statValues['torch'] != sNow:
if sNow:
pVars.torchJ.set(secs_to_hms(sNow))
if hal.get_value('plasmac.torch-enable'):
pVars.torchT.set(secs_to_hms(sNow + pVars.torchS.get()))
statValues['torch'] = sNow
# cut time
sNow = int(hal.get_value('plasmac.cut-time') + 0.5)
if statValues['cut'] != sNow:
if sNow:
pVars.cutJ.set(secs_to_hms(sNow))
if hal.get_value('plasmac.torch-enable'):
pVars.cutT.set(secs_to_hms(sNow + pVars.cutS.get()))
statValues['cut'] = sNow
# paused time
sNow = int(hal.get_value('plasmac.paused-time') + 0.5)
if statValues['paused'] != sNow:
if sNow:
pVars.pausedJ.set(secs_to_hms(sNow))
if hal.get_value('plasmac.torch-enable'):
pVars.pausedT.set(secs_to_hms(sNow + pVars.pausedS.get()))
statValues['paused'] = sNow
# run time
sNow = int(hal.get_value('plasmac.run-time') + 0.5)
if statValues['run'] != sNow:
if sNow:
pVars.runJ.set(secs_to_hms(sNow))
if hal.get_value('plasmac.torch-enable'):
pVars.runT.set(secs_to_hms(sNow + pVars.runS.get()))
statValues['run'] = sNow
# set table view zoom if window resized
if get_view_type() == 't' and (previewSize['w'] != rE(f"winfo width {tabs_preview}") or previewSize['h'] != rE(f"winfo height {tabs_preview}")):
previewSize = {'w':rE(f"winfo width {tabs_preview}"), 'h':rE(f"winfo height {tabs_preview}")}
root_window.update_idletasks()
commands.set_view_t()
# show/hide scrollbars for user buttons setup
if int(rE(f"winfo ismapped {fsetup}")):
if int(rE(f"winfo width {fsetup}.r.ubuttons.canvas.frame")) > \
int(rE(f"winfo width {fsetup}.r")) - \
(20 * int(rE(f"winfo ismapped {fsetup}.r.ubuttons.yscroll"))):
if not int(rE(f"winfo ismapped {fsetup}.r.ubuttons.xscroll")):
rE(f"grid {fsetup}.r.ubuttons.xscroll -column 1 -row 1 -sticky new -padx {{4 0}}")
else:
if int(rE(f"winfo ismapped {fsetup}.r.ubuttons.xscroll")):
rE(f"grid forget {fsetup}.r.ubuttons.xscroll")
if int(rE(f"winfo height {fsetup}.r.ubuttons.canvas.frame")) > \
int(rE(f"winfo height {fsetup}.r.ubuttons")) - \
(int(pVars.fontSize.get()) * 2 + 16 * int(rE(f"winfo ismapped {fsetup}.r.ubuttons.xscroll"))):
if not int(rE(f"winfo ismapped {fsetup}.r.ubuttons.yscroll")):
rE(f"grid {fsetup}.r.ubuttons.yscroll -column 0 -row 0 -sticky nsw -padx {{4 0}}")
else:
if int(rE(f"winfo ismapped {fsetup}.r.ubuttons.yscroll")):
rE(f"grid forget {fsetup}.r.ubuttons.yscroll")
cbbox = rE(f"{fsetup}.r.ubuttons.canvas bbox all")
rE(f"{fsetup}.r.ubuttons.canvas configure -scrollregion {{{cbbox}}}")
# show/hide virtual keyboard for materials
if str(rE('focus'))[:15] == f"{fruns}.material.":
if pVars.useVirtKB.get() and not vkbData['visible']:
vkb_show('numpad')
else:
if vkbData['visible'] and not vkbData['required']:
vkb_hide()
# resize manual tab
ttWidth = int(rE(f"winfo width {fjogf}.zerohome.home")) + int(rE(f"winfo width {fjogf}.zerohome.zero")) * 3 + 18
rE(f".pane.top.tabs configure -width {ttWidth}")
# start powermax comms if a valid port exists and it is not yet started
if pmPort and not hal.component_exists('pmx485') and pmx485_check(pmPort, True):
pmx485_startup(pmPort)
# run users custom periodic commands if it exists
if os.path.isfile(upFile):
exec(open(upFile).read())
o.tkRedraw()