فهرست منبع

CirkuixObj and derivatives modified and enhanced to handle form interaction better. Some threading changes.

Juan Pablo Caram 12 سال پیش
والد
کامیت
4ede050ba6
2فایلهای تغییر یافته به همراه213 افزوده شده و 236 حذف شده
  1. 201 224
      cirkuix.py
  2. 12 12
      cirkuix.ui

+ 201 - 224
cirkuix.py

@@ -1,4 +1,3 @@
-
 import threading
 import threading
 from gi.repository import Gtk, Gdk, GLib, GObject
 from gi.repository import Gtk, Gdk, GLib, GObject
 
 
@@ -12,6 +11,9 @@ from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCan
 from camlib import *
 from camlib import *
 
 
 
 
+########################################
+##            CirkuixObj              ##
+########################################
 class CirkuixObj:
 class CirkuixObj:
     """
     """
     Base type of objects handled in Cirkuix. These become interactive
     Base type of objects handled in Cirkuix. These become interactive
@@ -31,13 +33,17 @@ class CirkuixObj:
     app = None
     app = None
 
 
     def __init__(self, name):
     def __init__(self, name):
-        self.name = name
+        self.options = {"name": name}
+        self.form_kinds = {"name": "entry_text"}  # Kind of form element for each option
+        self.radios = {}  # Name value pairs for radio sets
+        self.radios_inv = {}  # Inverse of self.radios
         self.axes = None  # Matplotlib axes
         self.axes = None  # Matplotlib axes
-        self.options = {}
+        self.kind = None  # Override with proper name
 
 
     def setup_axes(self, figure):
     def setup_axes(self, figure):
         if self.axes is None:
         if self.axes is None:
-            self.axes = figure.add_axes([0.05, 0.05, 0.9, 0.9], label=self.name)
+            self.axes = figure.add_axes([0.05, 0.05, 0.9, 0.9],
+                                        label=self.options["name"])
         elif self.axes not in figure.axes:
         elif self.axes not in figure.axes:
             figure.add_axes(self.axes)
             figure.add_axes(self.axes)
 
 
@@ -52,23 +58,21 @@ class CirkuixObj:
         return
         return
 
 
     def to_form(self):
     def to_form(self):
-        for name in self.options:
-            if name in self.form_setters:
-                self.form_setters[name](self.options[name])
+        for option in self.options:
+            self.set_form_item(option)
 
 
     def read_form(self):
     def read_form(self):
-        for name in self.form_getters:
-            self.options[name] = self.form_getters[name]()
+        for option in self.form_getters:
+            self.read_form_item(option)
 
 
-    def build_ui(self, kind):
+    def build_ui(self):
         """
         """
         Sets up the UI/form for this object.
         Sets up the UI/form for this object.
-        @param kind: Kind of object, i.e. 'gerber'
-        @type kind: str
         @return: None
         @return: None
+        @rtype : None
         """
         """
 
 
-        # Where to the UI for this object
+        # Where the UI for this object is drawn
         box_selected = self.app.builder.get_object("box_selected")
         box_selected = self.app.builder.get_object("box_selected")
 
 
         # Remove anything else in the box
         # Remove anything else in the box
@@ -76,21 +80,53 @@ class CirkuixObj:
         for child in box_children:
         for child in box_children:
             box_selected.remove(child)
             box_selected.remove(child)
 
 
-        osw = self.app.builder.get_object("offscrwindow_" + kind)  # offscreenwindow
-        sw = self.app.builder.get_object("sw_" + kind)  # scrollwindows
+        osw = self.app.builder.get_object("offscrwindow_" + self.kind)  # offscreenwindow
+        sw = self.app.builder.get_object("sw_" + self.kind)  # scrollwindows
         osw.remove(sw)  # TODO: Is this needed ?
         osw.remove(sw)  # TODO: Is this needed ?
-        vp = self.app.builder.get_object("vp_" + kind)  # Viewport
+        vp = self.app.builder.get_object("vp_" + self.kind)  # Viewport
         vp.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(1, 1, 1, 1))
         vp.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(1, 1, 1, 1))
 
 
         # Put in the UI
         # Put in the UI
         box_selected.pack_start(sw, True, True, 0)
         box_selected.pack_start(sw, True, True, 0)
 
 
-        entry_name = self.app.builder.get_object("entry_" + kind + "name")
-        entry_name.set_text(self.name)
+        entry_name = self.app.builder.get_object("entry_text_" + self.kind + "_name")
         entry_name.connect("activate", self.app.on_activate_name)
         entry_name.connect("activate", self.app.on_activate_name)
         self.to_form()
         self.to_form()
         sw.show()
         sw.show()
 
 
+    def set_form_item(self, option):
+        fkind = self.form_kinds[option]
+        fname = fkind + "_" + self.kind + "_" + option
+
+        if fkind == 'entry_eval' or fkind == 'entry_text':
+            self.app.builder.get_object(fname).set_text(str(self.options[option]))
+            return
+        if fkind == 'cb':
+            self.app.builder.get_object(fname).set_active(self.options[option])
+            return
+        if fkind == 'radio':
+            self.app.builder.get_object(self.radios_inv[option][self.options[option]]).set_active(True)
+            return
+        print "Unknown kind of form item:", fkind
+
+    def read_form_item(self, option):
+        fkind = self.form_kinds[option]
+        fname = fkind + "_" + self.kind + "_" + option
+
+        if fkind == 'entry_text':
+            self.options[option] = self.app.builder(fname).get_text()
+            return
+        if fkind == 'entry_eval':
+            self.options[option] = self.app.get_eval(fname)
+            return
+        if fkind == 'cb':
+            self.options[option] = self.app.builder.get_object(fname).get_active()
+            return
+        if fkind == 'radio':
+            self.options[option] = self.app.get_radio_value(self.radios[option])
+            return
+        print "Unknown kind of form item:", fkind
+
 
 
 class CirkuixGerber(CirkuixObj, Gerber):
 class CirkuixGerber(CirkuixObj, Gerber):
     """
     """
@@ -101,7 +137,10 @@ class CirkuixGerber(CirkuixObj, Gerber):
         Gerber.__init__(self)
         Gerber.__init__(self)
         CirkuixObj.__init__(self, name)
         CirkuixObj.__init__(self, name)
 
 
-        self.options = {
+        self.kind = "gerber"
+
+        # The 'name' is already in self.options
+        self.options.update({
             "plot": True,
             "plot": True,
             "mergepolys": True,
             "mergepolys": True,
             "multicolored": False,
             "multicolored": False,
@@ -111,20 +150,29 @@ class CirkuixGerber(CirkuixObj, Gerber):
             "cutoutgapsize": 0.15,
             "cutoutgapsize": 0.15,
             "gaps": "tb",
             "gaps": "tb",
             "noncoppermargin": 0.0
             "noncoppermargin": 0.0
-        }
-
-    def build_ui(self):
-        """
-        @return: None
-        """
-        CirkuixObj.build_ui(self, "gerber")
+        })
+
+        self.form_kinds.update({
+            "plot": "cb",
+            "mergepolys": "cb",
+            "multicolored": "cb",
+            "solid": "cb",
+            "isotooldia": "entry_eval",
+            "cutoutmargin": "entry_eval",
+            "cutoutgapsize": "entry_eval",
+            "gaps": "radio",
+            "noncoppermargin": "entry_eval"
+        })
+
+        self.radios = {"gaps": {"rb_2tb": "tb", "rb_2lr": "lr", "rb_4": "4"}}
+        self.radios_inv = {"gaps": {"tb": "rb_2tb", "lr": "rb_2lr", "4": "rb_4"}}
 
 
     def plot(self, figure):
     def plot(self, figure):
         self.setup_axes(figure)
         self.setup_axes(figure)
 
 
         self.create_geometry()
         self.create_geometry()
 
 
-        geometry = None
+        geometry = None  # TODO: Test if needed
         if self.options["mergepolys"]:
         if self.options["mergepolys"]:
             geometry = self.solid_geometry
             geometry = self.solid_geometry
         else:
         else:
@@ -132,7 +180,7 @@ class CirkuixGerber(CirkuixObj, Gerber):
                         [poly['polygon'] for poly in self.regions] + \
                         [poly['polygon'] for poly in self.regions] + \
                         self.flash_geometry
                         self.flash_geometry
 
 
-        linespec = None
+        linespec = None  # TODO: Test if needed
         if self.options["multicolored"]:
         if self.options["multicolored"]:
             linespec = '-'
             linespec = '-'
         else:
         else:
@@ -155,14 +203,19 @@ class CirkuixExcellon(CirkuixObj, Excellon):
         Excellon.__init__(self)
         Excellon.__init__(self)
         CirkuixObj.__init__(self, name)
         CirkuixObj.__init__(self, name)
 
 
-        self.options = {
+        self.kind = "excellon"
+
+        self.options.update({
             "plot": True,
             "plot": True,
             "solid": False,
             "solid": False,
             "multicolored": False
             "multicolored": False
-        }
+        })
 
 
-    def build_ui(self):
-        CirkuixObj.build_ui(self, "excellon")
+        self.form_kinds.update({
+            "plot": "cb",
+            "solid": "cb",
+            "multicolored": "cb"
+        })
 
 
     def plot(self, figure):
     def plot(self, figure):
         self.setup_axes(figure)
         self.setup_axes(figure)
@@ -187,14 +240,21 @@ class CirkuixCNCjob(CirkuixObj, CNCjob):
                         feedrate=feedrate, z_cut=z_cut, tooldia=tooldia)
                         feedrate=feedrate, z_cut=z_cut, tooldia=tooldia)
         CirkuixObj.__init__(self, name)
         CirkuixObj.__init__(self, name)
 
 
-        self.options = {
+        self.kind = "cncjob"
+
+        self.options.update({
             "plot": True,
             "plot": True,
             "solid": False,
             "solid": False,
+            "multicolored": "cb",
             "tooldia": 0.4/25.4  # 0.4mm in inches
             "tooldia": 0.4/25.4  # 0.4mm in inches
-        }
+        })
 
 
-    def build_ui(self):
-        CirkuixObj.build_ui(self, "cncjob")
+        self.form_kinds.update({
+            "plot": "cb",
+            "solid": "cb",
+            "multicolored": "cb",
+            "tooldia": "entry_eval"
+        })
 
 
     def plot(self, figure):
     def plot(self, figure):
         self.setup_axes(figure)
         self.setup_axes(figure)
@@ -209,21 +269,26 @@ class CirkuixGeometry(CirkuixObj, Geometry):
 
 
     def __init__(self, name):
     def __init__(self, name):
         CirkuixObj.__init__(self, name)
         CirkuixObj.__init__(self, name)
-        self.options = {"plot": True,
-                        "solid": False,
-                        "multicolored": False}
 
 
-        self.options = {
+        self.kind = "geometry"
+
+        self.options.update({
             "plot": True,
             "plot": True,
             "solid": False,
             "solid": False,
             "multicolored": False,
             "multicolored": False,
             "cutz": -0.002,
             "cutz": -0.002,
             "travelz": 0.1,
             "travelz": 0.1,
             "feedrate": 5.0
             "feedrate": 5.0
-        }
+        })
 
 
-    def build_ui(self):
-        CirkuixObj.build_ui(self, "geometry")
+        self.form_kinds.update({
+            "plot": "cb",
+            "solid": "cb",
+            "multicolored": "cb",
+            "cutz": "entry_eval",
+            "travelz": "entry_eval",
+            "feedrate": "entry_eval"
+        })
 
 
     def plot(self, figure):
     def plot(self, figure):
         self.setup_axes(figure)
         self.setup_axes(figure)
@@ -244,6 +309,9 @@ class CirkuixGeometry(CirkuixObj, Geometry):
                 continue
                 continue
 
 
 
 
+########################################
+##                App                 ##
+########################################
 class App:
 class App:
     """
     """
     The main application class. The constructor starts the GUI.
     The main application class. The constructor starts the GUI.
@@ -256,7 +324,7 @@ class App:
         """
         """
 
 
         # Needed to interact with the GUI from other threads.
         # Needed to interact with the GUI from other threads.
-        GLib.threads_init()
+        #GLib.threads_init()
         GObject.threads_init()
         GObject.threads_init()
         #Gdk.threads_init()
         #Gdk.threads_init()
 
 
@@ -331,10 +399,8 @@ class App:
         self.canvas = FigureCanvas(self.figure)  # a Gtk.DrawingArea
         self.canvas = FigureCanvas(self.figure)  # a Gtk.DrawingArea
         self.canvas.set_hexpand(1)
         self.canvas.set_hexpand(1)
         self.canvas.set_vexpand(1)
         self.canvas.set_vexpand(1)
-        
-        ########################################
-        ##              EVENTS                ##
-        ########################################
+
+        # Events
         self.canvas.mpl_connect('button_press_event', self.on_click_over_plot)
         self.canvas.mpl_connect('button_press_event', self.on_click_over_plot)
         self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move_over_plot)
         self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move_over_plot)
         self.canvas.set_can_focus(True)  # For key press
         self.canvas.set_can_focus(True)  # For key press
@@ -346,74 +412,6 @@ class App:
     def setup_obj_classes(self):
     def setup_obj_classes(self):
         CirkuixObj.app = self
         CirkuixObj.app = self
 
 
-        CirkuixGerber.form_getters = {
-            "plot": self.builder.get_object("cb_gerber_plot").get_active,
-            "mergepolys": self.builder.get_object("cb_gerber_mergepolys").get_active,
-            "solid": self.builder.get_object("cb_gerber_solid").get_active,
-            "multicolored": self.builder.get_object("cb_gerber_multicolored").get_active,
-            "isotooldia": lambda: self.get_eval("entry_gerberisotooldia"),
-            "cutoutmargin": lambda: self.get_eval("entry_gerber_cutout_margin"),
-            "cutoutgapsize": lambda: self.get_eval("entry_gerber_cutout_gapsize"),
-            "gaps": lambda: self.get_radio_value({"rb_2tb": "tb", "rb_2lr": "lr", "rb_4": "4"}),
-            "noncoppermargin": lambda: self.get_eval("entry_gerber_noncopper_margin")
-        }
-
-        CirkuixGerber.form_setters = {
-            "plot": self.builder.get_object("cb_gerber_plot").set_active,
-            "mergepolys": self.builder.get_object("cb_gerber_mergepolys").set_active,
-            "solid": self.builder.get_object("cb_gerber_solid").set_active,
-            "multicolored": self.builder.get_object("cb_gerber_multicolored").set_active,
-            "isotooldia": lambda x: self.builder.get_object("entry_gerberisotooldia").set_text(str(x)),
-            "cutoutmargin": lambda x: self.builder.get_object("entry_gerber_cutout_margin").set_text(str(x)),
-            "cutoutgapsize": lambda x: self.builder.get_object("entry_gerber_cutout_gapsize").set_text(str(x)),
-            "gaps": lambda x: self.builder.get_object("cb_gerber_solid").set_active(
-                                    {"tb": "rb_2tb", "lr": "rb_2lr", "4": "rb_4"}[x]),
-            "noncoppermargin": lambda x: self.builder.get_object("entry_gerber_noncopper_margin").set_text(str(x))
-        }
-
-        CirkuixExcellon.form_getters = {
-            "plot": self.builder.get_object("cb_excellon_plot").get_active,
-            "solid": self.builder.get_object("cb_excellon_solid").get_active,
-            "multicolored": self.builder.get_object("cb_excellon_multicolored").get_active
-        }
-
-        CirkuixExcellon.form_setters = {
-            "plot": self.builder.get_object("cb_excellon_plot").set_active,
-            "solid": self.builder.get_object("cb_excellon_solid").set_active,
-            "multicolored": self.builder.get_object("cb_excellon_multicolored").set_active
-        }
-
-        CirkuixCNCjob.form_getters = {
-            "plot": self.builder.get_object("cb_cncjob_plot").get_active,
-            "solid": self.builder.get_object("cb_cncjob_solid").get_active,
-            "multicolored": self.builder.get_object("cb_cncjob_multicolored").get_active,
-            "tooldia": lambda: self.get_eval("entry_cncjob_tooldia")
-        }
-
-        CirkuixCNCjob.form_setters = {
-            "plot": self.builder.get_object("cb_cncjob_plot").set_active,
-            "solid": self.builder.get_object("cb_cncjob_solid").set_active,
-            "tooldia": lambda x: self.builder.get_object("entry_cncjob_tooldia").set_text(str(x))
-        }
-
-        CirkuixGeometry.form_getters = {
-            "plot": self.builder.get_object("cb_geometry_plot").get_active,
-            "solid": self.builder.get_object("cb_geometry_solid").get_active,
-            "multicolored": self.builder.get_object("cb_geometry_multicolored").get_active,
-            "cutz": lambda: self.get_eval("entry_geometry_cutz"),
-            "travelz": lambda: self.get_eval("entry_geometry_travelz"),
-            "feedrate": lambda: self.get_eval("entry_geometry_feedrate")
-        }
-
-        CirkuixGeometry.form_setters = {
-            "plot": self.builder.get_object("cb_geometry_plot").set_active,
-            "solid": self.builder.get_object("cb_geometry_solid").set_active,
-            "multicolored": self.builder.get_object("cb_geometry_multicolored").set_active,
-            "cutz": lambda x: self.builder.get_object("entry_geometry_cutz").set_text(str(x)),
-            "travelz": lambda x: self.builder.get_object("entry_geometry_travelz").set_text(str(x)),
-            "feedrate": lambda x: self.builder.get_object("entry_geometry_feedrate").set_text(str(x))
-        }
-
     def setup_component_viewer(self):
     def setup_component_viewer(self):
         """
         """
         List or Tree where whatever has been loaded or created is
         List or Tree where whatever has been loaded or created is
@@ -429,7 +427,6 @@ class App:
         renderer = Gtk.CellRendererText()
         renderer = Gtk.CellRendererText()
         column = Gtk.TreeViewColumn("Title", renderer, text=0)
         column = Gtk.TreeViewColumn("Title", renderer, text=0)
         self.tree.append_column(column)
         self.tree.append_column(column)
-        #self.builder.get_object("notebook1").append_page(self.tree, Gtk.Label("Project"))
         self.builder.get_object("box_project").pack_start(self.tree, False, False, 1)
         self.builder.get_object("box_project").pack_start(self.tree, False, False, 1)
         
         
     def setup_component_editor(self):
     def setup_component_editor(self):
@@ -567,6 +564,10 @@ class App:
         self.tree_select.unselect_all()
         self.tree_select.unselect_all()
         self.tree_select.select_iter(iter)
         self.tree_select.select_iter(iter)
 
 
+        # Need to return False such that GLib.idle_add
+        # or .timeout_add do not repear.
+        return False
+
     def new_object(self, kind, name, initialize):
     def new_object(self, kind, name, initialize):
         """
         """
         Creates a new specalized CirkuixObj and attaches it to the application,
         Creates a new specalized CirkuixObj and attaches it to the application,
@@ -595,24 +596,34 @@ class App:
         obj = classdict[kind](name)
         obj = classdict[kind](name)
 
 
         # Initialize as per user request
         # Initialize as per user request
+        # User must take care to implement initialize
+        # in a thread-safe way as is is likely that we
+        # have been invoked in a separate thread.
         initialize(obj, self)
         initialize(obj, self)
 
 
         # Add to our records
         # Add to our records
         self.stuff[name] = obj
         self.stuff[name] = obj
 
 
-        # Update GUI list and select it
+        # Update GUI list and select it (Thread-safe?)
         self.store.append([name])
         self.store.append([name])
         #self.build_list()
         #self.build_list()
-        self.set_list_selection(name)
+        GLib.idle_add(lambda: self.set_list_selection(name))
+        # TODO: Gtk.notebook.set_current_page is not known to
+        # TODO: return False. Fix this??
         GLib.timeout_add(100, lambda: self.notebook.set_current_page(1))
         GLib.timeout_add(100, lambda: self.notebook.set_current_page(1))
 
 
         # Plot
         # Plot
+        # TODO: (Thread-safe?)
         obj.plot(self.figure)
         obj.plot(self.figure)
         obj.axes.set_alpha(0.0)
         obj.axes.set_alpha(0.0)
         self.on_zoom_fit(None)
         self.on_zoom_fit(None)
 
 
         return obj
         return obj
 
 
+    def set_progress_bar(self, percentage, text=""):
+        self.progress_bar.set_text(text)
+        self.progress_bar.set_fraction(percentage)
+        return False
 
 
     ########################################
     ########################################
     ##         EVENT HANDLERS             ##
     ##         EVENT HANDLERS             ##
@@ -637,8 +648,8 @@ class App:
 
 
         def geo_init(geo_obj, app_obj):
         def geo_init(geo_obj, app_obj):
             # TODO: get from object
             # TODO: get from object
-            margin = app_obj.get_eval("entry_gerber_cutout_margin")
-            gap_size = app_obj.get_eval("entry_gerber_cutout_gapsize")
+            margin = app_obj.get_eval("entry_eval_gerber_cutoutmargin")
+            gap_size = app_obj.get_eval("entry_eval_gerber_cutoutgapsize")
             gerber = app_obj.stuff[app_obj.selected_item_name]
             gerber = app_obj.stuff[app_obj.selected_item_name]
             minx, miny, maxx, maxy = gerber.bounds()
             minx, miny, maxx, maxy = gerber.bounds()
             minx -= margin
             minx -= margin
@@ -690,7 +701,7 @@ class App:
         def iso_init(geo_obj, app_obj):
         def iso_init(geo_obj, app_obj):
             # TODO: Object must be updated on form change and the options
             # TODO: Object must be updated on form change and the options
             # TODO: read from the object.
             # TODO: read from the object.
-            tooldia = app_obj.get_eval("entry_gerberisotooldia")
+            tooldia = app_obj.get_eval("entry_eval_gerber_isotooldia")
             geo_obj.solid_geometry = self.stuff[self.selected_item_name].isolation_geometry(tooldia/2.0)
             geo_obj.solid_geometry = self.stuff[self.selected_item_name].isolation_geometry(tooldia/2.0)
 
 
         # TODO: Do something if this is None. Offer changing name?
         # TODO: Do something if this is None. Offer changing name?
@@ -705,9 +716,9 @@ class App:
         def job_init(job_obj, app_obj):
         def job_init(job_obj, app_obj):
             # TODO: Object must be updated on form change and the options
             # TODO: Object must be updated on form change and the options
             # TODO: read from the object.
             # TODO: read from the object.
-            z_cut = app_obj.get_eval("entry_geometry_cutz")
-            z_move = app_obj.get_eval("entry_geometry_travelz")
-            feedrate = app_obj.get_eval("entry_geometry_feedrate")
+            z_cut = app_obj.get_eval("entry_eval_geometry_cutz")
+            z_move = app_obj.get_eval("entry_eval_geometry_travelz")
+            feedrate = app_obj.get_eval("entry_eval_geometry_feedrate")
 
 
             geometry = app_obj.stuff[app_obj.selected_item_name]
             geometry = app_obj.stuff[app_obj.selected_item_name]
             assert isinstance(job_obj, CirkuixCNCjob)
             assert isinstance(job_obj, CirkuixCNCjob)
@@ -722,7 +733,7 @@ class App:
 
 
     def on_cncjob_tooldia_activate(self, widget):
     def on_cncjob_tooldia_activate(self, widget):
         job = self.stuff[self.selected_item_name]
         job = self.stuff[self.selected_item_name]
-        tooldia = self.get_eval("entry_cncjob_tooldia")
+        tooldia = self.get_eval("entry_eval_cncjob_tooldia")
         job.tooldia = tooldia
         job.tooldia = tooldia
         print "Changing tool diameter to:", tooldia
         print "Changing tool diameter to:", tooldia
 
 
@@ -753,16 +764,23 @@ class App:
         self.clear_plots()
         self.clear_plots()
         
         
     def on_activate_name(self, entry):
     def on_activate_name(self, entry):
-        '''
+        """
         Hitting 'Enter' after changing the name of an item
         Hitting 'Enter' after changing the name of an item
         updates the item dictionary and re-builds the item list.
         updates the item dictionary and re-builds the item list.
-        '''
-        print "Changing name"
+        """
+
+        # Disconnect event listener
         self.tree.get_selection().disconnect(self.signal_id)
         self.tree.get_selection().disconnect(self.signal_id)
+
         new_name = entry.get_text()  # Get from form
         new_name = entry.get_text()  # Get from form
         self.stuff[new_name] = self.stuff.pop(self.selected_item_name)  # Update dictionary
         self.stuff[new_name] = self.stuff.pop(self.selected_item_name)  # Update dictionary
+        self.stuff[new_name].options["name"] = new_name  # update object
+        self.info('Name change: ' + self.selected_item_name + " to " + new_name)
         self.selected_item_name = new_name  # Update selection name
         self.selected_item_name = new_name  # Update selection name
+
         self.build_list()  # Update the items list
         self.build_list()  # Update the items list
+
+        # Reconnect event listener
         self.signal_id = self.tree.get_selection().connect(
         self.signal_id = self.tree.get_selection().connect(
                              "changed", self.on_tree_selection_changed)
                              "changed", self.on_tree_selection_changed)
                              
                              
@@ -833,125 +851,84 @@ class App:
             dialog.destroy()
             dialog.destroy()
     
     
     def on_fileopengerber(self, param):
     def on_fileopengerber(self, param):
-        def on_success(self, filename):
-            self.progress_bar.set_text("Opening Gerber ...")
-            self.progress_bar.set_fraction(0.1)
-
-            name = filename.split('/')[-1].split('\\')[-1]
-            gerber = CirkuixGerber(name)
 
 
-            self.progress_bar.set_text("Parsing ...")
-            self.progress_bar.set_fraction(0.2)
+        # IMPORTANT: on_success will run on a separate thread. Use
+        # GLib.idle_add(function, **kwargs) to launch actions that will
+        # updata the GUI.
+        def on_success(app_obj, filename):
+            assert isinstance(app_obj, App)
+            GLib.idle_add(lambda: app_obj.set_progress_bar(0.1, "Opening Gerber ..."))
 
 
-            gerber.parse_file(filename)
-            self.store.append([name])
-            self.stuff[name] = gerber
+            def obj_init(gerber_obj, app_obj):
+                GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Parsing ..."))
+                gerber_obj.parse_file(filename)
+                GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Plotting ..."))
 
 
-            self.progress_bar.set_text("Plotting ...")
-            self.progress_bar.set_fraction(0.6)
-
-            #self.plot_gerber(gerber)
-            gerber.plot(self.figure)
-            gerber.axes.set_alpha(0.0)
-            self.on_zoom_fit(None)
-
-            self.progress_bar.set_text("Done!")
-            self.progress_bar.set_fraction(1.0)
-
-            #self.notebook.set_current_page(0)
-            self.set_list_selection(name)
-            #self.notebook.set_current_page(1)
-            GLib.timeout_add(100, lambda: self.notebook.set_current_page(1))
+            name = filename.split('/')[-1].split('\\')[-1]
+            app_obj.new_object("gerber", name, obj_init)
 
 
-            def clear_bar(bar):
-                bar.set_text("")
-                bar.set_fraction(0.0)
-                return False
+            GLib.idle_add(lambda: app_obj.set_progress_bar(1.0, "Done!"))
+            GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, ""))
 
 
-            #threading.Timer(1, clear_bar, args=(self.progress_bar,)).start()
-            GLib.timeout_add_seconds(1, clear_bar, self.progress_bar)
+        # on_success gets run on a separate thread
         self.file_chooser_action(on_success)
         self.file_chooser_action(on_success)
     
     
     def on_fileopenexcellon(self, param):
     def on_fileopenexcellon(self, param):
-        def on_success(self, filename):
-            self.progress_bar.set_text("Opening Excellon ...")
-            self.progress_bar.set_fraction(0.1)
-
-            name = filename.split('/')[-1].split('\\')[-1]
-            excellon = CirkuixExcellon(name)
-
-            self.progress_bar.set_text("Parsing ...")
-            self.progress_bar.set_fraction(0.2)
-
-            excellon.parse_file(filename)
-            self.store.append([name])
-            self.stuff[name] = excellon
 
 
-            self.progress_bar.set_text("Plotting ...")
-            self.progress_bar.set_fraction(0.6)
+        # IMPORTANT: on_success will run on a separate thread. Use
+        # GLib.idle_add(function, **kwargs) to launch actions that will
+        # updata the GUI.
+        def on_success(app_obj, filename):
+            assert isinstance(app_obj, App)
+            GLib.idle_add(lambda: app_obj.set_progress_bar(0.1, "Opening Excellon ..."))
 
 
-            #self.plot_excellon(excellon)
-            excellon.plot(self.figure)
-            self.on_zoom_fit(None)
-
-            self.progress_bar.set_text("Done!")
-            self.progress_bar.set_fraction(1.0)
+            def obj_init(excellon_obj, app_obj):
+                GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Parsing ..."))
+                excellon_obj.parse_file(filename)
+                GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Plotting ..."))
 
 
-            #self.notebook.set_current_page(0)
-            self.set_list_selection(name)
-            #self.notebook.set_current_page(1)
-            GLib.timeout_add(100, lambda: self.notebook.set_current_page(1))
+            name = filename.split('/')[-1].split('\\')[-1]
+            app_obj.new_object("excellon", name, obj_init)
 
 
-            def clear_bar(bar):
-                bar.set_text("")
-                bar.set_fraction(0.0)
-                return False
-            #threading.Timer(1, clear_bar, args=(self.progress_bar,)).start()
-            GLib.timeout_add_seconds(1, clear_bar, self.progress_bar)
+            GLib.idle_add(lambda: app_obj.set_progress_bar(1.0, "Done!"))
+            GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, ""))
 
 
+        # on_success gets run on a separate thread
         self.file_chooser_action(on_success)
         self.file_chooser_action(on_success)
     
     
     def on_fileopengcode(self, param):
     def on_fileopengcode(self, param):
-        def on_success(self, filename):
-            self.progress_bar.set_text("Opening G-Code ...")
-            self.progress_bar.set_fraction(0.1)
 
 
-            name = filename.split('/')[-1].split('\\')[-1]
-            f = open(filename)
-            gcode = f.read()
-            f.close()
-            tooldia = self.get_eval("entry_tooldia")
-            job = CirkuixCNCjob(name, tooldia=tooldia)
-            job.gcode = gcode
+        # IMPORTANT: on_success will run on a separate thread. Use
+        # GLib.idle_add(function, **kwargs) to launch actions that will
+        # updata the GUI.
+        def on_success(app_obj, filename):
+            assert isinstance(app_obj, App)
 
 
-            self.progress_bar.set_text("Parsing ...")
-            self.progress_bar.set_fraction(0.2)
+            def obj_init(job_obj, app_obj):
+                assert isinstance(app_obj, App)
+                GLib.idle_add(lambda: app_obj.set_progress_bar(0.1, "Opening G-Code ..."))
 
 
-            job.gcode_parse()
-            job.create_geometry()
-            self.store.append([name])
-            self.stuff[name] = job
+                f = open(filename)
+                gcode = f.read()
+                f.close()
 
 
-            self.progress_bar.set_text("Plotting ...")
-            self.progress_bar.set_fraction(0.6)
+                job_obj.gcode = gcode
 
 
-            #self.plot_cncjob(job)
-            job.plot(self.figure)
-            self.on_zoom_fit(None)
+                GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Parsing ..."))
+                job_obj.gcode_parse()
+
+                GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Creating geometry ..."))
+                job_obj.create_geometry()
 
 
-            self.progress_bar.set_text("Done!")
-            self.progress_bar.set_fraction(1.0)
+                GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Plotting ..."))
+
+            name = filename.split('/')[-1].split('\\')[-1]
+            app_obj.new_object("cncjob", name, obj_init)
 
 
-            #self.notebook.set_current_page(0)
-            self.set_list_selection(name)
-            #self.notebook.set_current_page(1)
+            GLib.idle_add(lambda: app_obj.set_progress_bar(1.0, "Done!"))
+            GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, ""))
 
 
-            def clear_bar(bar):
-                bar.set_text("")
-                bar.set_fraction(0.0)
-                return False
-            #threading.Timer(1, clear_bar, args=(self.progress_bar,)).start()
-            GLib.timeout_add_seconds(1, clear_bar, self.progress_bar)
+        # on_success gets run on a separate thread
         self.file_chooser_action(on_success)
         self.file_chooser_action(on_success)
         
         
     def on_mouse_move_over_plot(self, event):
     def on_mouse_move_over_plot(self, event):

+ 12 - 12
cirkuix.ui

@@ -71,7 +71,7 @@
                       </packing>
                       </packing>
                     </child>
                     </child>
                     <child>
                     <child>
-                      <object class="GtkEntry" id="entry_cncjobname">
+                      <object class="GtkEntry" id="entry_text_cncjob_name">
                         <property name="visible">True</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="can_focus">True</property>
                         <property name="invisible_char">●</property>
                         <property name="invisible_char">●</property>
@@ -174,7 +174,7 @@
                       </packing>
                       </packing>
                     </child>
                     </child>
                     <child>
                     <child>
-                      <object class="GtkEntry" id="entry_cncjob_tooldia">
+                      <object class="GtkEntry" id="entry_eval_cncjob_tooldia">
                         <property name="visible">True</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="can_focus">True</property>
                         <property name="invisible_char">●</property>
                         <property name="invisible_char">●</property>
@@ -292,7 +292,7 @@
                       </packing>
                       </packing>
                     </child>
                     </child>
                     <child>
                     <child>
-                      <object class="GtkEntry" id="entry_excellonname">
+                      <object class="GtkEntry" id="entry_text_excellon_name">
                         <property name="visible">True</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="can_focus">True</property>
                         <property name="invisible_char">●</property>
                         <property name="invisible_char">●</property>
@@ -448,7 +448,7 @@
                       </packing>
                       </packing>
                     </child>
                     </child>
                     <child>
                     <child>
-                      <object class="GtkEntry" id="entry_geometryname">
+                      <object class="GtkEntry" id="entry_text_geometry_name">
                         <property name="visible">True</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="can_focus">True</property>
                         <property name="invisible_char">●</property>
                         <property name="invisible_char">●</property>
@@ -556,7 +556,7 @@
                     <property name="row_spacing">2</property>
                     <property name="row_spacing">2</property>
                     <property name="column_spacing">4</property>
                     <property name="column_spacing">4</property>
                     <child>
                     <child>
-                      <object class="GtkEntry" id="entry_geometry_cutz">
+                      <object class="GtkEntry" id="entry_eval_geometry_cutz">
                         <property name="visible">True</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="can_focus">True</property>
                         <property name="invisible_char">●</property>
                         <property name="invisible_char">●</property>
@@ -570,7 +570,7 @@
                       </packing>
                       </packing>
                     </child>
                     </child>
                     <child>
                     <child>
-                      <object class="GtkEntry" id="entry_geometry_travelz">
+                      <object class="GtkEntry" id="entry_eval_geometry_travelz">
                         <property name="visible">True</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="can_focus">True</property>
                         <property name="invisible_char">●</property>
                         <property name="invisible_char">●</property>
@@ -584,7 +584,7 @@
                       </packing>
                       </packing>
                     </child>
                     </child>
                     <child>
                     <child>
-                      <object class="GtkEntry" id="entry_geometry_feedrate">
+                      <object class="GtkEntry" id="entry_eval_geometry_feedrate">
                         <property name="visible">True</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="can_focus">True</property>
                         <property name="invisible_char">●</property>
                         <property name="invisible_char">●</property>
@@ -726,7 +726,7 @@
                       </packing>
                       </packing>
                     </child>
                     </child>
                     <child>
                     <child>
-                      <object class="GtkEntry" id="entry_gerbername">
+                      <object class="GtkEntry" id="entry_text_gerber_name">
                         <property name="visible">True</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="can_focus">True</property>
                         <property name="invisible_char">●</property>
                         <property name="invisible_char">●</property>
@@ -864,7 +864,7 @@
                       </packing>
                       </packing>
                     </child>
                     </child>
                     <child>
                     <child>
-                      <object class="GtkEntry" id="entry_gerberisotooldia">
+                      <object class="GtkEntry" id="entry_eval_gerber_isotooldia">
                         <property name="visible">True</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="can_focus">True</property>
                         <property name="margin_top">2</property>
                         <property name="margin_top">2</property>
@@ -941,7 +941,7 @@
                       </packing>
                       </packing>
                     </child>
                     </child>
                     <child>
                     <child>
-                      <object class="GtkEntry" id="entry_gerber_cutout_margin">
+                      <object class="GtkEntry" id="entry_eval_gerber_cutoutmargin">
                         <property name="visible">True</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="can_focus">True</property>
                         <property name="invisible_char">●</property>
                         <property name="invisible_char">●</property>
@@ -957,7 +957,7 @@
                       </packing>
                       </packing>
                     </child>
                     </child>
                     <child>
                     <child>
-                      <object class="GtkEntry" id="entry_gerber_cutout_gapsize">
+                      <object class="GtkEntry" id="entry_eval_gerber_cutoutgapsize">
                         <property name="visible">True</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="can_focus">True</property>
                         <property name="invisible_char">●</property>
                         <property name="invisible_char">●</property>
@@ -1123,7 +1123,7 @@
                       </packing>
                       </packing>
                     </child>
                     </child>
                     <child>
                     <child>
-                      <object class="GtkEntry" id="entry_gerber_noncopper_margin">
+                      <object class="GtkEntry" id="entry_eval_gerber_noncoppermargin">
                         <property name="visible">True</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="can_focus">True</property>
                         <property name="invisible_char">●</property>
                         <property name="invisible_char">●</property>