diff --git a/lib/python/gladevcp/calculatorwidget.py b/lib/python/gladevcp/calculatorwidget.py
index cb6dbb8511f..68cb7a2d982 100644
--- a/lib/python/gladevcp/calculatorwidget.py
+++ b/lib/python/gladevcp/calculatorwidget.py
@@ -164,13 +164,15 @@ def set_value( self, value ):
def get_value( self ):
self.compute()
try:
- value = self.entry.get_text()
- if self.use_localization:
- return locale.atof( value )
- else:
- return float(value)
+ value = float(self.entry.get_text())
except:
return None
+ if self.use_localization:
+ try:
+ value = locale.atof( value )
+ except:
+ return None
+ return value
def get_preset_value( self ):
return self.preset_value
@@ -198,10 +200,7 @@ def compute( self ):
else:
qualified = qualified + i
try :
- if self.integer_only:
- b = str( int( eval( qualified ) ) )
- else:
- b = str( eval( qualified ) )
+ b = str( eval( qualified ) )
except:
b = "Error"
self.eval_string = ''
diff --git a/lib/python/gladevcp/offsetpage_widget.py b/lib/python/gladevcp/offsetpage_widget.py
index 7e1a3fb1df8..3d6c6aa5054 100755
--- a/lib/python/gladevcp/offsetpage_widget.py
+++ b/lib/python/gladevcp/offsetpage_widget.py
@@ -159,6 +159,16 @@ def __init__(self, filename = None, *a, **kw):
# check linuxcnc status every half second
GLib.timeout_add(500, self.periodic_check)
+ def warning_dialog(self, message):
+ dialog = Gtk.MessageDialog(parent=self.wTree.get_object("window1"),
+ destroy_with_parent = True,
+ message_type=Gtk.MessageType.ERROR,
+ text=message)
+ dialog.add_buttons(Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT)
+ dialog.show()
+ dialog.run()
+ dialog.destroy()
+
# Reload the offsets into display
def reload_offsets(self):
g28, g30, g92, g54, g55, g56, g57, g58, g59, g59_1, g59_2, g59_3 = self.read_file()
@@ -353,13 +363,16 @@ def set_editing(self, widget):
self.queue_draw()
# When the column is edited this does the work
- def col_editted(self, widget, filtered_path, new_text, col):
+ def col_editted(self, widget, path, new_text, col):
+ self.validate_input(path, new_text, col)
+
+
+ def validate_input(self, path, new_text, col):
model, treeiter = self.view2.get_selection().get_selected()
path = self.modelfilter.get_path(treeiter)
(store_path,) = self.modelfilter.convert_path_to_child_path(path)
row = store_path
axisnum = col - 1
- # print "EDITED:", new_text, col, int(filtered_path), row, "axis num:", axisnum
def system_to_p(system):
convert = { "G54":1, "G55":2, "G56":3, "G57":4, "G58":5, "G59":6, "G59.1":7, "G59.2":8, "G59.3":9}
@@ -374,23 +387,27 @@ def system_to_p(system):
tmpl = lambda s: self.mm_text_template % s
else:
tmpl = lambda s: self.imperial_text_template % s
-
+ msg = None
# allow 'Comment' column text to be arbitrarily changed
if col == 11:
self.store[row][15] = new_text
return
# for all other columns we expect a float value
else:
- try:
- if self.use_localization:
+ if self.use_localization:
+ try:
# using locale settings can lead to issues but we make it optional for backwards compatibility
new_float = float(locale.atof(new_text))
- else:
+ except Exception as e:
+ print("offsetpage_widget, Error trying to convert to localized float: ", e)
+ else:
+ try:
# this is the preferred way, allowing dot or comma as decimal symbol
new_float = float(new_text.replace(',', '.'))
- except Exception as error:
- print('new_text: ', new_text, error)
- print(_("offsetpage widget error: unrecognized float input"))
+ except:
+ msg = (_(f"\nMust be a decimal number"))
+ if msg is not None:
+ self.warning_dialog(msg)
return
# ignore entries to the Rot column in non-wcs rows
diff --git a/lib/python/gladevcp/tooledit_widget.py b/lib/python/gladevcp/tooledit_widget.py
index 8f21e9db42c..73aa6562211 100644
--- a/lib/python/gladevcp/tooledit_widget.py
+++ b/lib/python/gladevcp/tooledit_widget.py
@@ -69,7 +69,7 @@ def __init__(self,toolfile=None, *a, **kw):
self.editable = None
self.edit_path = None
self.edit_column = None
-
+ self.tooltable_error_msg = None
# connect the signals from Glade
dic = {
"on_delete_clicked" : self.delete,
@@ -251,10 +251,21 @@ def match_tool(model, path, iter, pathlist):
except:
print(_("tooledit_widget error: cannot select tool number"),toolnumber)
- def add(self,widget,data=[1,0,0,'0','0','0','0','0','0','0','0','0','0','0','0',0,"comment"]):
+ def add(self, widget, data=None):
+ liststore = self.model
+ if data is None: # an empty line is being added
+ data = [1,0,0,'0','0','0','0','0','0','0','0','0','0','0','0',0,"comment"]
+ tool_nbrs = [0]
+ for row in liststore:
+ values = [ value for value in row ]
+ tool_nbrs.append(values[1])
+ missing = sorted(set(range(1, max(tool_nbrs))) - set(tool_nbrs))
+ if len(missing) > 0:
+ data[1] = min(missing)
+ else:
+ data[1] = max(tool_nbrs)+1
self.model.append(data)
self.num_of_col +=1
- liststore = self.model
self.wTree.get_object("treeview1").scroll_to_cell(len(liststore)-1)
# this is for adding a filename path after the tooleditor is already loaded.
@@ -262,8 +273,7 @@ def set_filename(self,filename):
self.toolfile = filename
self.reload(None)
- def warning_dialog(self, line_number):
- message = f"Error in tool table line {line_number} in column orientation.\nValid range is 0 ~ 9."
+ def warning_dialog(self, message):
dialog = Gtk.MessageDialog(parent=self.wTree.get_object("window1"),
destroy_with_parent = True,
message_type=Gtk.MessageType.ERROR,
@@ -286,7 +296,15 @@ def reload(self,widget):
logfile = open(self.toolfile, "r").readlines()
self.toolinfo = []
line_number = 0
+ tool_nbrs = []
+ rejected_lines = []
+ error_msg = []
for rawline in logfile:
+ if rawline.isspace():
+ continue
+ elif rawline[0] != ';' and rawline[0] != 'T':
+ rejected_lines.append(rawline)
+ continue
# strip the comments from line and add directly to array
# if index = -1 the delimiter ; is missing - clear comments
index = rawline.find(";")
@@ -314,33 +332,63 @@ def reload(self,widget):
if word.startswith(';'): break
if word.startswith(i):
if offset == 1:
+ try:
+ current_tool = int(word.lstrip(i))
+ tool_nbrs.append(current_tool)
+ except:
+ rejected_lines.append(rawline)
+ continue
if int(word.lstrip(i)) == self.toolinfo_num:
toolinfo_flag = True
if offset in(1,2):
try:
array[offset]= int(word.lstrip(i))
except:
- print(_("Tooledit widget int error"))
+ rejected_lines.append(rawline)
+ continue
elif offset == 15:
try:
# Accept also float for 'orientation' for backward compatibility
value = int(float(word.lstrip(i)))
array[offset] = value
if value not in range(10):
- self.warning_dialog(line_number)
- break
+ msg = _(f"Tool {current_tool} Orientation: '{value}'\nValid range is 0 ~ 9.")
+ error_msg.append(msg)
except:
- print(_("Tooledit widget float error"))
+ rejected_lines.append(rawline)
+ continue
else:
try:
array[offset]= f"{float(word.lstrip(i)):10.4f}"
except:
- print(_("Tooledit widget float error"))
+ rejected_lines.append(rawline)
+ continue
break
if toolinfo_flag:
self.toolinfo = array
# add array line to liststore
self.add(None,array)
+ # check for duplicate toolnumbers
+ s = []
+ dup_tool = []
+ for n in tool_nbrs:
+ if n in s:
+ dup_tool.append(n)
+ else:
+ s.append(n)
+ # compose user message about duplicate tool nummbers
+ if len(dup_tool) > 0:
+ msg = _("Duplicate tool number(s): ") + str(dup_tool)[1:-1] +""
+ error_msg.append(msg)
+ # write malformed lines to a file and compose user message
+ if len(rejected_lines) > 0:
+ file_name = "tooltable_rejected.txt"
+ self.rejected_lines = rejected_lines
+ with open(file_name, "w", encoding="utf-8") as f:
+ f.writelines(rejected_lines)
+ msg = (_(f"{len(rejected_lines)} malformed line(s) have been rejected and copied to '{file_name}'"))
+ error_msg.append(msg)
+ self.tooltable_error_msg = error_msg
def save(self,widget):
if self.toolfile == None: return
@@ -350,12 +398,19 @@ def save(self,widget):
# pre check before saving the file
# if not done before, the file will be saved only until the erroneous line and the rest will be lost
line_number = 0
+ tool_nbrs = []
for row in liststore:
values = [ value for value in row ]
line_number += 1
- if values[15] > 9:
- self.warning_dialog(line_number)
+ msg = None
+ if values[1] in tool_nbrs:
+ msg = f"\nError in tool table:\nDuplicate tool number '{values[1]}'"
+ elif values[15] not in range(10):
+ msg = f"\nError in tool table line {line_number} in column orientation.\nValid range is 0 ~ 9."
+ if msg is not None:
+ self.warning_dialog(msg)
return
+ tool_nbrs.append(values[1])
if(locale.getlocale(locale.LC_NUMERIC)[0] is None):
raise ExceptionMessage("\n\n"+_("Something wrong with the locale settings. Will not save the tool table."))
@@ -509,32 +564,40 @@ def validate_input(self, path, new_text, col):
elif filter == 'tool':
(store_path,) = self.tool_filter.convert_path_to_child_path(path)
path = store_path
-
+ msg = None
+ # validate positive integer for tool and pocket number
if col in(1,2):
- try:
- self.model[path][col] = int(new_text)
+ try :
+ value = float(new_text)
+ if value != int(value) or value < 0:
+ raise TypeError
+ else:
+ self.model[path][col] = int(value)
except:
- pass
+ msg = (_(f"\nMust be a positive whole number"))
# validate input for float columns
elif col in range(3,15):
try:
self.model[path][col] = f"{float(new_text.replace(',', '.')):10.4f}"
except:
- pass
+ msg = (_(f"\nMust be a decimal number"))
# validate input for orientation: check if int and valid range
elif col == 15:
- try:
- value = int(new_text)
- if value in range(10):
- self.model[path][col] = value
+ try :
+ value = float(new_text)
+ if value != int(value) or value not in range(10):
+ raise TypeError
+ else:
+ self.model[path][col] = int(value)
except:
- pass
+ msg = (_(f"\nMust be one of (0,1,2,3,4,5,6,7,8,9)"))
elif col == 16:
try:
self.model[path][col] = (new_text)
except:
pass
- #print path,new_text, col
+ if msg is not None:
+ self.warning_dialog(msg)
if filter in('wear','tool'):
self.save(None)
diff --git a/src/emc/usr_intf/gmoccapy/dialogs.py b/src/emc/usr_intf/gmoccapy/dialogs.py
index 08a16ee7249..842df6b2a08 100644
--- a/src/emc/usr_intf/gmoccapy/dialogs.py
+++ b/src/emc/usr_intf/gmoccapy/dialogs.py
@@ -99,10 +99,7 @@ def entry_dialog(self, caller, data = None, header = _("Enter value") , label =
dialog.destroy()
if response == Gtk.ResponseType.ACCEPT:
if value != None:
- if integer:
- return int(value)
- else:
- return float(value)
+ return value
else:
return "ERROR"
return "CANCEL"
diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py
index 1e6852dcc90..0e7bfbb4b17 100644
--- a/src/emc/usr_intf/gmoccapy/gmoccapy.py
+++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py
@@ -517,6 +517,7 @@ def __init__(self, argv):
self.progress = 0
self._startup_message()
+ self._tooltable_message()
# This allows sourcing an user defined file
rcfile = "~/.gmoccapyrc"
@@ -573,6 +574,11 @@ def _startup_message(self):
self.notification.add_message(message, INFO_ICON, show_checkbox=True)
self.num = len(messages)
+ def _tooltable_message(self):
+ if self.widgets.tooledit1.tooltable_error_msg is not None:
+ title = _("Error in tool table\n")
+ for msg in self.widgets.tooledit1.tooltable_error_msg:
+ self.notification.add_message(title + msg, ALERT_ICON, show_checkbox=False)
def _get_ini_data(self):
self.get_ini_info = getiniinfo.GetIniInfo()
@@ -2106,21 +2112,21 @@ def on_tool_col_edit_started(self, widget, filtered_path, new_text, col):
header=_("Enter value"),
label=_("Tool") + f" {model[treeiter][1]}, {captations[col]}:",
integer=col in [1,2,15])
- if value == "ERROR":
- LOG.debug("conversion error")
- self.dialogs.warning_dialog(self, _("Conversion error !"),
- ("Please enter only numerical values\nValues have not been applied"))
- elif value == "CANCEL":
+ if value == "CANCEL":
pass
else:
+ if isinstance(value, float):
+ cell_text = f"{value:11.4f}"
+ else:
+ cell_text = value
path = model.get_path(treeiter)
row = path.get_indices()[0]
# Clicking on a cell emits 'editing-started' which leads to the evaluation of the text in edit mode.
# To use the return value of the calculator, it must be pretended that there is no editable (=no edit mode).
self.widgets.tooledit1.editable = None
- self.widgets.tooledit1.validate_input(row, f"{value:11.4f}", col)
+ self.widgets.tooledit1.validate_input(row, cell_text, col)
self.widgets.tooledit1.edited = True
- # this is needed to get offsetview out of editing mode
+ # this is needed to get out of editing mode
GLib.timeout_add(50,
toolview.set_cursor,
toolpage.model.get_path(treeiter),
@@ -2504,51 +2510,29 @@ def on_offset_col_edit_started(self, widget, filtered_path, new_text, col):
(store_path,) = offsetpage.modelfilter.convert_path_to_child_path(path)
row = store_path
if self.widgets.offsetpage1.btn_edit_offsets.get_active():
- offset = self.dialogs.entry_dialog(self,
+ value = self.dialogs.entry_dialog(self,
data=offsetpage.store[row][col],
header=_("Enter value for offset"),
label=f"{offsetpage.store[row][0]} {AXISLIST[col]}-" + _("offset:"),
integer=False)
- if offset == "ERROR":
- LOG.debug("conversion error")
- self.dialogs.warning_dialog(self, _("Conversion error !"),
- ("Please enter only numerical values\nValues have not been applied"))
- elif offset == "CANCEL":
- pass
+ if value == "CANCEL":
+ pass
+ else:
+ if isinstance(value, float):
+ cell_text = f"{value:11.4f}"
else:
- axisnum = col - 1
- try:
- if self.stat.task_mode != linuxcnc.MODE_MDI:
- self.command.mode(linuxcnc.MODE_MDI)
- self.command.wait_complete()
- if row == 0:
- self.command.mdi("G43.1 %s %10.4f" % (AXISLIST[col], offset))
- elif row == 1:
- self.command.mdi("#%s = %10.4f" % (str(5161 + axisnum), offset))
- elif row == 2:
- self.command.mdi("#%s = %10.4f" % (str(5181 + axisnum), offset))
- elif row == 3:
- self.command.mdi("G92 %s %10.4f" % (AXISLIST[col], offset))
- else:
- pnum = row-3
- if not pnum == None:
- if col == 10:
- self.command.mdi("G10 L2 P%d R %10.4f" % (pnum, offset))
- else:
- self.command.mdi("G10 L2 P%d %s %10.4f" % (pnum, AXISLIST[col], offset))
- self.command.mode(linuxcnc.MODE_MANUAL)
- self.command.wait_complete()
- self.command.mode(linuxcnc.MODE_MDI)
- self.command.wait_complete()
- except:
- print(_("offsetpage widget error: MDI call error"))
- offsetpage.reload_offsets()
- # this is needed to get offsetview out of editing mode
- GLib.timeout_add(50,
- offsetview.set_cursor,
- path,
- offsetview.get_columns()[0],
- True)
+ cell_text = value
+ path = model.get_path(treeiter)
+ row = path.get_indices()[0]
+ self.widgets.offsetpage1.validate_input(row, cell_text, col)
+ self.widgets.offsetpage1.edited = True
+
+ GLib.timeout_add(50,
+ offsetview.set_cursor,
+ model.get_path(treeiter),
+ offsetview.get_columns()[0],
+ True)
+
# Icon file selection stuff
def _init_IconFileSelection(self):
@@ -6619,3 +6603,4 @@ def _terminate(signum, frame):
# start the event loop
Gtk.main()
+