Advertisement
Guest User

tooledit_widget.py patch

a guest
Nov 16th, 2015
131
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 21.67 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # GladeVcp Widget - tooledit
  3. #
  4. # Copyright (c) 2012 Chris Morley
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 2 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. # GNU General Public License for more details.
  15.  
  16. import sys, os, pango, linuxcnc, hashlib
  17. datadir = os.path.abspath(os.path.dirname(__file__))
  18. KEYWORDS = ['S','T', 'P', 'X', 'Y', 'Z', 'A', 'B', 'C', 'U', 'V', 'W', 'D', 'I', 'J', 'Q', ';']
  19. try:
  20.     import gobject,gtk
  21. except:
  22.     print('GTK not available')
  23.     sys.exit(1)
  24.  
  25. # localization
  26. import locale
  27. BASE = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), ".."))
  28. LOCALEDIR = os.path.join(BASE, "share", "locale")
  29. locale.setlocale(locale.LC_ALL, '')
  30.  
  31. # we put this in a try so there is no error in the glade editor
  32. # linuxcnc is probably not running then
  33. try:
  34.     INIPATH = os.environ['INI_FILE_NAME']
  35. except:
  36.     INIPATH = None
  37.  
  38. class ToolEdit(gtk.VBox):
  39.     __gtype_name__ = 'ToolEdit'
  40.     __gproperties__ = {
  41.         'font' : ( gobject.TYPE_STRING, 'Pango Font', 'Display font to use',
  42.                 "sans 12", gobject.PARAM_READWRITE|gobject.PARAM_CONSTRUCT),
  43.         'hide_columns' : (gobject.TYPE_STRING, 'Hidden Columns', 'A no-spaces list of columns to hide: stpxyzabcuvwdijq and ; are the options',
  44.                     "", gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT),
  45.         'lathe_display_type' : ( gobject.TYPE_BOOLEAN, 'Display Type', 'True: Lathe layout, False standard layout',
  46.                     False, gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT),
  47.     }
  48.     __gproperties = __gproperties__
  49.  
  50.     def __init__(self,toolfile=None, *a, **kw):
  51.         super(ToolEdit, self).__init__()
  52.         self.emcstat = linuxcnc.stat()
  53.         self.hash_check = None
  54.         self.lathe_display_type = True
  55.         self.toolfile = toolfile
  56.         self.num_of_col = 1
  57.         self.font="sans 12"
  58.         self.hide_columns =''
  59.         self.toolinfo_num = 0
  60.         self.toolinfo = []
  61.         self.wTree = gtk.Builder()
  62.         self.wTree.set_translation_domain("linuxcnc") # for locale translations
  63.         self.wTree.add_from_file(os.path.join(datadir, "tooledit_gtk.glade") )
  64.         # connect the signals from Glade
  65.         dic = {
  66.             "on_delete_clicked" : self.delete,
  67.             "on_add_clicked" : self.add,
  68.             "on_reload_clicked" : self.reload,
  69.             "on_save_clicked" : self.save,
  70.             "on_style_clicked" : self.display_toggle,
  71.             "cell_toggled" : self.toggled
  72.             }
  73.         self.wTree.connect_signals( dic )
  74.  
  75.         # for raw view 1:
  76.  
  77.         # toggle button useable
  78.         renderer = self.wTree.get_object("cell_toggle1")
  79.         renderer.set_property('activatable', True)
  80.         # make colums editable
  81.         self.tool_cell_list = "cell_tool#","cell_pos","cell_x","cell_y","cell_z","cell_a","cell_b","cell_c","cell_u","cell_v","cell_w","cell_d", \
  82.                 "cell_front","cell_back","cell_orient","cell_comments"
  83.         for col,name in enumerate(self.tool_cell_list):
  84.             #print name,col
  85.             renderer = self.wTree.get_object(name+'1')
  86.             renderer.connect( 'edited', self.col_editted, col+1, None)
  87.  
  88.         # for lathe wear view2:
  89.  
  90.         # make columns editable
  91.         temp =[ ("cell_x2",3),("cell_z2",5),("cell_front2",13),("cell_back2",14), \
  92.                 ("cell_orient2",15), ("cell_comments2",16)]
  93.         for name,col in temp:
  94.             renderer = self.wTree.get_object(name)
  95.             renderer.connect( 'edited', self.col_editted, col, 'wear' )
  96.         # Hide columns we don't want to see
  97.         self.set_col_visible(list='spyabcuvwdijq', bool= False, tab= '2')
  98.  
  99.         # for tool offsets view 3:
  100.  
  101.         # make columns editable
  102.         temp =[ ("cell_x3",3),("cell_z3",5),("cell_front3",13),("cell_back3",14), \
  103.                 ("cell_orient3",15), ("cell_comments3",16)]
  104.         for name,col in temp:
  105.             renderer = self.wTree.get_object(name)
  106.             renderer.connect( 'edited', self.col_editted, col, 'tool' )
  107.         # Hide columns we don't want to see
  108.         self.set_col_visible(list='spyabcuvwdij', bool= False, tab= '3')
  109.  
  110.         # global references
  111.         self.model = self.wTree.get_object("liststore1")
  112.         self.notebook = self.wTree.get_object("tool_offset_notebook")
  113.         self.all_window = self.wTree.get_object("all_window")
  114.         self.wear_window = self.wTree.get_object("wear_window")
  115.         self.tool_window = self.wTree.get_object("tool_window")
  116.         self.view1 = self.wTree.get_object("treeview1")
  117.         #diameter column sorting
  118.         # sort routine for treeview column  
  119.         def compare(model, row1, row2, user_data=None):
  120.             sort_column, _ = model.get_sort_column_id()
  121.             value1 = model.get_value(row1,sort_column)
  122.             value2 = model.get_value(row2,sort_column)
  123.             if value2<value1:
  124.                 return -1
  125.             elif value1==value2:
  126.                 return 0
  127.             else:
  128.                 return 1
  129.         model = self.view1.get_model()
  130.         model.set_sort_func(12, compare)  
  131.         self.view2 = self.wTree.get_object("treeview2")
  132.         self.view2.connect('button_press_event', self.on_treeview2_button_press_event)
  133.         self.selection = self.view2.get_selection()
  134.         self.selection.set_mode(gtk.SELECTION_SINGLE)
  135.         self.view3 = self.wTree.get_object("treeview3")
  136.         self.view3.connect('button_press_event', self.on_treeview2_button_press_event)
  137.         self.apply = self.wTree.get_object("apply")
  138.         self.buttonbox = self.wTree.get_object("buttonbox")
  139.         self.tool_filter = self.wTree.get_object("tool_modelfilter")
  140.         self.tool_filter.set_visible_func(self.match_type,False)
  141.         self.wear_filter = self.wTree.get_object("wear_modelfilter")
  142.         self.wear_filter.set_visible_func(self.match_type,True)
  143.         # reparent tooledit box from Glades tp level window to tooledit's VBox
  144.         window = self.wTree.get_object("tooledit_box")
  145.         window.reparent(self)
  146.         # If the toolfile was specified when tooledit was created load it
  147.         if toolfile:
  148.             self.reload(None)
  149.         # check the ini file if UNITS are set to mm
  150.         # first check the global settings
  151.         # if not available then the X axis units
  152.         try:
  153.             self.inifile = linuxcnc.ini(INIPATH)
  154.             test = self.inifile.find("DISPLAY", "LATHE")
  155.             print test,"<<<<<<"
  156.             if test == '1' or test == 'True':
  157.                 self.lathe_display_type = True
  158.                 self.set_lathe_display(True)
  159.         except:
  160.             pass
  161.  
  162.         # check linuxcnc status every second
  163.         gobject.timeout_add(1000, self.periodic_check)
  164.    
  165.     # used to split tool and wear data by the tool number
  166.     # if the tool number is above 10000 then its a wear offset (as per fanuc)
  167.     # returning true will show the row
  168.     def match_type(self, model, iter, data):
  169.         value = model.get_value(iter, 1)
  170.         if value < 10000:
  171.             return not data
  172.         return data
  173.  
  174.         # delete the selected tools
  175.     def delete(self,widget):
  176.         liststore  = self.model
  177.         def match_value_cb(model, path, iter, pathlist):
  178.             if model.get_value(iter, 0) == 1 :
  179.                 pathlist.append(path)
  180.             return False     # keep the foreach going
  181.  
  182.         pathlist = []
  183.         liststore.foreach(match_value_cb, pathlist)
  184.         # foreach works in a depth first fashion
  185.         pathlist.reverse()
  186.         for path in pathlist:
  187.             liststore.remove(liststore.get_iter(path))
  188.  
  189.         # return the selected tool number
  190.     def get_selected_tool(self):
  191.         liststore  = self.model
  192.         def match_value_cb(model, path, iter, pathlist):
  193.             if model.get_value(iter, 0) == 1 :
  194.                 pathlist.append(path)
  195.             return False     # keep the foreach going
  196.         pathlist = []
  197.         liststore.foreach(match_value_cb, pathlist)
  198.         # foreach works in a depth first fashion
  199.         if len(pathlist) != 1:
  200.             return None
  201.         else:
  202.             return(liststore.get_value(liststore.get_iter(pathlist[0]),1))
  203.  
  204.     def set_selected_tool(self,toolnumber):
  205.         try:
  206.             treeselection = self.view2.get_selection()
  207.             liststore  = self.model
  208.             def match_tool(model, path, iter, pathlist):
  209.                 if model.get_value(iter, 1) == toolnumber:
  210.                     pathlist.append(path)
  211.                 return False     # keep the foreach going
  212.             pathlist = []
  213.             liststore.foreach(match_tool, pathlist)
  214.             # foreach works in a depth first fashion
  215.             if len(pathlist) == 1:
  216.                 liststore.set_value(liststore.get_iter(pathlist[0]),0,1)
  217.                 treeselection.select_path(pathlist[0])
  218.         except:
  219.             print "tooledit_widget error: cannot select tool number",toolnumber
  220.  
  221.     def add(self,widget,data=[1,0,0,'0','0','0','0','0','0','0','0','0','0','0','0','0',"comment"]):
  222.         self.model.append(data)
  223.         self.num_of_col +=1
  224.  
  225.         # this is for adding a filename path after the tooleditor is already loaded.
  226.     def set_filename(self,filename):
  227.         self.toolfile = filename
  228.         self.reload(None)
  229.  
  230.         # Reload the tool file into display
  231.     def reload(self,widget):
  232.         self.hash_code = self.md5sum(self.toolfile)
  233.         # clear the current liststore, search the tool file, and add each tool
  234.         if self.toolfile == None:return
  235.         self.model.clear()
  236.         print "toolfile:",self.toolfile
  237.         if not os.path.exists(self.toolfile):
  238.             print "Toolfile does not exist"
  239.             return
  240.         logfile = open(self.toolfile, "r").readlines()
  241.         self.toolinfo = []
  242.         for rawline in logfile:
  243.             # strip the comments from line and add directly to array
  244.             # if index = -1 the delimiter ; is missing - clear comments
  245.             index = rawline.find(";")
  246.             comment =''
  247.             if not index == -1:
  248.                 comment = (rawline[index+1:])
  249.                 comment = comment.rstrip("\n")
  250.                 line = rawline.rstrip(comment)
  251.             else:
  252.                 line = rawline
  253.             array = [0,0,0,'0','0','0','0','0','0','0','0','0','0','0','0','0',comment]
  254.             toolinfo_flag = False
  255.             # search beginning of each word for keyword letters
  256.             # offset 0 is the checkbutton so ignore it
  257.             # if i = ';' that is the comment and we have already added it
  258.             # offset 1 and 2 are integers the rest floats
  259.             # we strip leading and following spaces from the comments
  260.             for offset,i in enumerate(KEYWORDS):
  261.                 if offset == 0 or i == ';': continue
  262.                 for word in line.split():
  263.                     if word.startswith(';'): break
  264.                     if word.startswith(i):
  265.                         if offset == 1:
  266.                             if int(word.lstrip(i)) == self.toolinfo_num:
  267.                                 toolinfo_flag = True
  268.                         if offset in(1,2):
  269.                             try:
  270.                                 array[offset]= int(word.lstrip(i))
  271.                             except:
  272.                                 print "Tooledit widget int error"
  273.                         else:
  274.                             try:
  275.                                 array[offset]= "%10.4f"% float(word.lstrip(i))
  276.                             except:
  277.                                 print "Tooledit_widget float error"
  278.                         break
  279.             if toolinfo_flag:
  280.                 self.toolinfo = array
  281.             # add array line to liststore
  282.             self.add(None,array)
  283.  
  284.         # Note we have to save the float info with a decimal even if the locale uses a comma
  285.     def save(self,widget):
  286.         if self.toolfile == None:return
  287.         file = open(self.toolfile, "w")
  288.         print self.toolfile
  289.         liststore = self.model
  290.         for row in liststore:
  291.             values = [ value for value in row ]
  292.             #print values
  293.             line = ""
  294.             for num,i in enumerate(values):
  295.                 if num == 0: continue
  296.                 elif num in (1,2): # tool# pocket#
  297.                     line = line + "%s%d "%(KEYWORDS[num], i)
  298.                 elif num == 16: # comments
  299.                     test = i.strip()
  300.                     line = line + "%s%s "%(KEYWORDS[num],test)
  301.                 else:
  302.                     test = i.lstrip() # localized floats
  303.                     line = line + "%s%s "%(KEYWORDS[num], locale.atof(test))
  304.  
  305.             #print >>file,line
  306.         # Theses lines are required to make sure the OS doesn't cache the data
  307.         # That would make linuxcnc and the widget to be out of synch leading to odd errors
  308.         file.flush()
  309.         os.fsync(file.fileno())
  310.         # tell linuxcnc we changed the tool table entries
  311.         try:
  312.             linuxcnc.command().load_tool_table()
  313.         except:
  314.             print "Reloading tooltable into linuxcnc failed"
  315.  
  316.         # This is for changing the display after tool editor was loaded using the style button
  317.         # note that it toggles the display
  318.     def display_toggle(self,widget=None):
  319.         value = (self.display_type * -1) +1
  320.         self.set_display(value)
  321.  
  322.         # There is an 'everything' display and a 'lathe' display
  323.         # the same model is used in each case just the listview is different
  324.         # does the actual display change by hiding what we don't want
  325.         # for whatever reason I have to hide/show both the view and it's container
  326.     def set_lathe_display(self,value):
  327.         #print "    lathe_display    ",value
  328.         self.lathe_display_type = value
  329.         #if self.all_window.flags() & gtk.VISIBLE:
  330.         self.notebook.set_show_tabs(value)
  331.         if value:
  332.             self.wear_window.show()
  333.             self.view3.show()
  334.             self.tool_window.show()
  335.             self.view2.show()
  336.         else:
  337.             self.view2.hide()
  338.             self.wear_window.hide()
  339.            
  340.             self.tool_window.hide()
  341.             self.view3.hide()
  342.  
  343.     def set_font(self, value, tab='123'):
  344.         for i in range(0, len(tab)):
  345.             if tab[i] in ('1','2','3'):
  346.                 for col,name in enumerate(self.tool_cell_list):
  347.                     temp2 = self.wTree.get_object(name+tab[i])
  348.                     temp2.set_property('font', value)
  349.  
  350.     # for legacy interface
  351.     def set_visible(self,list,bool):
  352.         self.set_col_visible(list, bool, tab= '1')
  353.  
  354.     # This allows hiding or showing columns from a text string of columnns
  355.     # eg list ='xyz'
  356.     # tab= selects what tabs to apply it to
  357.     def set_col_visible(self, list, bool= False, tab= '1'):
  358.         #print list,bool,tab
  359.         for i in range(0, len(tab)):
  360.             if tab[i] in ('1','2','3'):
  361.                 #print tab[i]
  362.                 for index in range(0, len(list)):
  363.                     colstr = str(list[index])
  364.                     #print colstr
  365.                     colnum = 'stpxyzabcuvwdijq;'.index(colstr.lower())
  366.                     #print colnum
  367.                     name = KEYWORDS[colnum].lower()+tab[i]
  368.                     #print name
  369.                     renderer = self.wTree.get_object(name)
  370.                     renderer.set_property('visible', bool)
  371.  
  372.     # For single click selection when in edit mode
  373.     def on_treeview2_button_press_event(self, widget, event):
  374.         if event.button == 1 : # left click
  375.             try:
  376.                 path, model, x, y = widget.get_path_at_pos(int(event.x), int(event.y))
  377.                 widget.set_cursor(path, None, True)
  378.             except:
  379.                 pass
  380.  
  381.         # depending what is editted add the right type of info integer,float or text
  382.         # If it's a filtered display then we must convert the path
  383.     def col_editted(self, widget, path, new_text, col, filter):
  384.         if filter == 'wear':
  385.             (store_path,) = self.wear_filter.convert_path_to_child_path(path)
  386.             path = store_path
  387.         elif filter == 'tool':
  388.             (store_path,) = self.tool_filter.convert_path_to_child_path(path)
  389.             path = store_path
  390.        
  391.         if col in(1,2):
  392.             try:
  393.                 self.model[path][col] = int(new_text)
  394.             except:
  395.                 pass
  396.         elif col in range(3,16):
  397.             try:
  398.                 self.model[path][col] = locale.format("%10.4f",locale.atof(new_text))
  399.             except:
  400.                 pass
  401.         elif col == 16:
  402.             try:
  403.                 self.model[path][col] = (new_text)
  404.             except:
  405.                 pass
  406.         #print path,new_text, col
  407.         if filter in('wear','tool'):
  408.             self.save(None)
  409.  
  410.         # this makes the checkboxes actually update
  411.     def toggled(self, widget, path):
  412.         model = self.model
  413.         model[path][0] = not model[path][0]
  414.  
  415.         # check for linnuxcnc ON and IDLE which is the only safe time to edit the tool file.
  416.         # check to see if the tool file is current
  417.     def periodic_check(self):
  418.         try:
  419.             self.emcstat.poll()
  420.             on = self.emcstat.task_state > linuxcnc.STATE_OFF
  421.             idle = self.emcstat.interp_state == linuxcnc.INTERP_IDLE
  422.             self.apply.set_sensitive(bool(on and idle))
  423.         except:
  424.             pass
  425.         if self.toolfile:
  426.             self.file_current_check()
  427.         return True
  428.  
  429.         # create a hash code
  430.     def md5sum(self,filename):
  431.         try:
  432.             f = open(filename, "rb")
  433.         except IOError:
  434.             return None
  435.         else:
  436.             return hashlib.md5(f.read()).hexdigest()
  437.  
  438.         # check the hash code on the toolfile against
  439.         # the saved hash code when last reloaded.
  440.     def file_current_check(self):
  441.         m = self.hash_code
  442.         m1 = self.md5sum(self.toolfile)
  443.         if m1 and m != m1:
  444.             self.toolfile_stale()
  445.  
  446.         # you could overload this to do something else.
  447.     def toolfile_stale(self):
  448.         print "Tool file was modified since it was last read"
  449.         self.reload(None)
  450.  
  451.         # Returns the tool information array of the requested toolnumber
  452.         # or current tool if no tool number is specified
  453.         # returns None if tool not found in table or if there is no current tool
  454.     def get_toolinfo(self,toolnum=None):
  455.         if toolnum == None:
  456.             self.toolinfo_num = self.emcstat.tool_in_spindle
  457.         else:
  458.             self.toolinfo_num = toolnum
  459.         self.reload(None)
  460.         if self.toolinfo == []: return None
  461.         return self.toolinfo
  462.  
  463.         # 'convenience' method to hide buttons
  464.         # you must call this after show_all()
  465.     def hide_buttonbox(self, data):
  466.         if data:
  467.             self.buttonbox.hide()
  468.         else:
  469.             self.buttonbox.show()
  470.  
  471.         # standard Gobject method
  472.     def do_get_property(self, property):
  473.         name = property.name.replace('-', '_')
  474.         if name in self.__gproperties.keys():
  475.             return getattr(self, name)
  476.         else:
  477.             raise AttributeError('unknown property %s' % property.name)
  478.  
  479.         # standard Gobject method
  480.         # changing the Gobject property 'display_type' will actually change the display
  481.         # This is so that in the Glade editor, you can change the display
  482.         # Note this sets the display absolutely vrs the display_toggle method that toggles the display
  483.     def do_set_property(self, property, value):
  484.         name = property.name.replace('-', '_')
  485.         if name == 'font':
  486.             try:
  487.                 self.set_font(value)
  488.             except:
  489.                 pass
  490.         elif name == 'lathe_display_type':
  491.             #print value
  492.             if INIPATH is None:
  493.                 try:
  494.                     self.set_lathe_display(value)
  495.                 except:
  496.                     pass
  497.         elif name == 'hide_columns':
  498.             try:
  499.                 self.set_col_visible("sptxyzabcuvwdijq;", True)
  500.                 self.set_col_visible("%s" % value, False)
  501.             except:
  502.                 pass
  503.  
  504. # for testing without glade editor:
  505. # for what ever reason tooledit always shows both display lists,
  506. # in the glade editor it shows only one at a time (as it should)
  507. # you can specify a tool table file at the command line
  508. # or uncomment the line and set the path correctly.
  509. def main(filename=None):
  510.     window = gtk.Dialog("My dialog",
  511.                    None,
  512.                    gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
  513.                    (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
  514.                     gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
  515.     tooledit = ToolEdit(filename)
  516.    
  517.     window.vbox.add(tooledit)
  518.     window.connect("destroy", gtk.main_quit)
  519.     tooledit.set_col_visible("abcUVW", False, tab='1')
  520.     tooledit.set_filename("/home/chris/emc2-dev/configs/sim/gscreen/gscreen_custom/lathe-fanucy.tbl")
  521.     #tooledit.set_filename("/home/chris/emc2-dev/configs/sim/gscreen/test.tbl")
  522.     tooledit.set_font("sans 16",tab='23')
  523.     window.show_all()
  524.     #tooledit.set_lathe_display(True)
  525.     response = window.run()
  526.     if response == gtk.RESPONSE_ACCEPT:
  527.        print "True"
  528.     else:
  529.        print "False"
  530.  
  531. if __name__ == "__main__":
  532.     # if there are two arguments then specify the path
  533.     if len(sys.argv) > 1: main(sys.argv[1])
  534.     else: main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement