Parcourir la source

Added vertical and horizontal panning with scroll wheel. Started "open recent" feature.

Juan Pablo Caram il y a 12 ans
Parent
commit
5312e78f92
3 fichiers modifiés avec 273 ajouts et 9 suppressions
  1. 199 9
      FlatCAM.py
  2. 73 0
      FlatCAM.ui
  3. 1 0
      recent.json

+ 199 - 9
FlatCAM.py

@@ -9,6 +9,7 @@
 import threading
 
 # TODO: Bundle together. This is just for debugging.
+from docutils.nodes import image
 from gi.repository import Gtk
 from gi.repository import Gdk
 from gi.repository import GdkPixbuf
@@ -695,6 +696,7 @@ class App:
         self.setup_obj_classes()
         self.stuff = {}    # FlatCAMObj's by name
         self.mouse = None  # Mouse coordinates over plot
+        self.recent = []
 
         # What is selected by the user. It is
         # a key if self.stuff
@@ -755,6 +757,7 @@ class App:
         self.options.update(self.defaults)  # Copy app defaults to project options
         self.options2form()  # Populate the app defaults form
         self.units_label.set_text("[" + self.options["units"] + "]")
+        self.setup_recent_items()
 
         #### Check for updates ####
         self.version = 3
@@ -876,6 +879,44 @@ class App:
         box1.show()
         label1.show()
 
+    def setup_recent_items(self):
+
+        icons = {
+            "gerber": "share/flatcam_icon16.png",
+            "excellon": "share/drill16.png",
+            "cncjob": "share/cnc16.png",
+            "project": "share/project16.png"
+        }
+
+        try:
+            f = open('recent.json')
+        except:
+            print "ERROR: Failed to load recent item list."
+            self.info("Failed to load recent item list.")
+            return
+
+        try:
+            self.recent = json.load(f)
+        except:
+            print "ERROR: Failed to parse recent item list."
+            self.info("Failed to parse recent item list.")
+            f.close()
+            return
+        f.close()
+
+        recent_menu = Gtk.Menu()
+        for recent in self.recent:
+            filename = recent['filename'].split('/')[-1].split('\\')[-1]
+            item = Gtk.ImageMenuItem.new_with_label(filename)
+            im = Gtk.Image.new_from_file(icons[recent["kind"]])
+            item.set_image(im)
+            # def opener():
+
+            recent_menu.append(item)
+
+        self.builder.get_object('open_recent').set_submenu(recent_menu)
+        recent_menu.show_all()
+
     def info(self, text):
         """
         Show text on the status bar.
@@ -1282,6 +1323,8 @@ class App:
             f.close()
             return
 
+        self.register_recent("project", filename)
+
         # Clear the current project
         self.on_file_new(None)
 
@@ -1377,9 +1420,99 @@ class App:
     def do_nothing(self, param):
         return
 
+    def disable_plots(self, except_current=False):
+        """
+        Disables all plots with exception of the current object if specified.
+
+        :param except_current: Wether to skip the current object.
+        :rtype except_current: boolean
+        :return: None
+        """
+        # TODO: This method is very similar to replot_all. Try to merge.
+
+        self.set_progress_bar(0.1, "Re-plotting...")
+
+        def thread_func(app_obj):
+            percentage = 0.1
+            try:
+                delta = 0.9 / len(self.stuff)
+            except ZeroDivisionError:
+                GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
+                return
+            for i in self.stuff:
+                if i != app_obj.selected_item_name or not except_current:
+                    self.stuff[i].options['plot'] = False
+                    self.stuff[i].plot()
+                percentage += delta
+                GLib.idle_add(lambda: app_obj.set_progress_bar(percentage, "Re-plotting..."))
+
+            GLib.idle_add(app_obj.plotcanvas.auto_adjust_axes)
+            GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
+
+        t = threading.Thread(target=thread_func, args=(self,))
+        t.daemon = True
+        t.start()
+
+    def enable_all_plots(self, *args):
+        self.plotcanvas.clear()
+        self.set_progress_bar(0.1, "Re-plotting...")
+
+        def thread_func(app_obj):
+            percentage = 0.1
+            try:
+                delta = 0.9 / len(self.stuff)
+            except ZeroDivisionError:
+                GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
+                return
+            for i in self.stuff:
+                self.stuff[i].options['plot'] = True
+                self.stuff[i].plot()
+                percentage += delta
+                GLib.idle_add(lambda: app_obj.set_progress_bar(percentage, "Re-plotting..."))
+
+            GLib.idle_add(app_obj.plotcanvas.auto_adjust_axes)
+            GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
+
+        t = threading.Thread(target=thread_func, args=(self,))
+        t.daemon = True
+        t.start()
+
+    def register_recent(self, kind, filename):
+        record = {'kind': kind, 'filename': filename}
+
+        if record in self.recent:
+            return
+
+        self.recent.insert(0, record)
+
+        if len(self.recent) > 10:  # Limit reached
+            self.recent.pop()
+
+        try:
+            f = open('recent.json', 'w')
+        except:
+            print "ERROR: Failed to open recent items file for writing."
+            self.info('Failed to open recent files file for writing.')
+            return
+
+        try:
+            json.dump(self.recent, f)
+        except:
+            print "ERROR: Failed to write to recent items file."
+            self.info('Failed to write to recent items file.')
+            f.close()
+
+        f.close()
+
     ########################################
     ##         EVENT HANDLERS             ##
     ########################################
+    def on_disable_all_plots(self, widget):
+        self.disable_plots()
+
+    def on_disable_all_plots_not_current(self, widget):
+        self.disable_plots(except_current=True)
+
     def on_offset_object(self, widget):
         """
         Offsets the object's geometry by the vector specified
@@ -1710,6 +1843,7 @@ class App:
             self.on_file_saveprojectas(None)
         else:
             self.save_project(self.project_filename)
+            self.register_recent("project", self.project_filename)
             self.info("Project saved to: " + self.project_filename)
 
     def on_file_saveprojectas(self, param):
@@ -1726,6 +1860,7 @@ class App:
             assert isinstance(app_obj, App)
             app_obj.save_project(filename)
             self.project_filename = filename
+            self.register_recent("project", filename)
             app_obj.info("Project saved to: " + filename)
 
         self.file_chooser_save_action(on_success)
@@ -1744,6 +1879,7 @@ class App:
         def on_success(app_obj, filename):
             assert isinstance(app_obj, App)
             app_obj.save_project(filename)
+            self.register_recent("project", filename)
             app_obj.info("Project copy saved to: " + filename)
 
         self.file_chooser_save_action(on_success)
@@ -1998,10 +2134,7 @@ class App:
 
         def thread_func(app_obj):
             assert isinstance(app_obj, App)
-            #GLib.idle_add(lambda: app_obj.set_progress_bar(0.5, "Plotting..."))
-            #GLib.idle_add(lambda: app_obj.get_current().plot(app_obj.figure))
             obj.plot()
-            #GLib.idle_add(lambda: app_obj.on_zoom_fit(None))
             GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, "Idle"))
 
         t = threading.Thread(target=thread_func, args=(self,))
@@ -2526,6 +2659,7 @@ class App:
 
             name = filename.split('/')[-1].split('\\')[-1]
             app_obj.new_object("gerber", name, obj_init)
+            self.register_recent("gerber", filename)
 
             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, ""))
@@ -2557,6 +2691,7 @@ class App:
 
             name = filename.split('/')[-1].split('\\')[-1]
             app_obj.new_object("excellon", name, obj_init)
+            self.register_recent("excellon", filename)
 
             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, ""))
@@ -2603,6 +2738,7 @@ class App:
 
             name = filename.split('/')[-1].split('\\')[-1]
             app_obj.new_object("cncjob", name, obj_init)
+            self.register_recent("cncjob", filename)
 
             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, ""))
@@ -2713,12 +2849,12 @@ class App:
         '1'         Zoom-fit. Fits the axes limits to the data.
         '2'         Zoom-out.
         '3'         Zoom-in.
+        'm'         Toggle on-off the measuring tool.
         ==========  ============================================
 
         :param event: Ignored.
         :return: None
         """
-        #print 'you pressed', event.key, event.xdata, event.ydata
 
         if event.key == '1':  # 1
             self.on_zoom_fit(None)
@@ -2901,8 +3037,27 @@ class PlotCanvas:
         self.canvas.connect('configure-event', self.auto_adjust_axes)
         self.canvas.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK)
         self.canvas.connect("scroll-event", self.on_scroll)
+        self.canvas.mpl_connect('key_press_event', self.on_key_down)
+        self.canvas.mpl_connect('key_release_event', self.on_key_up)
+
+        self.mouse = [0, 0]
+        self.key = None
+
+    def on_key_down(self, event):
+        """
+
+        :param event:
+        :return:
+        """
+        self.key = event.key
 
-        self.mouse = [0,0]
+    def on_key_up(self, event):
+        """
+
+        :param event:
+        :return:
+        """
+        self.key = None
 
     def mpl_connect(self, event_name, callback):
         """
@@ -3054,6 +3209,20 @@ class PlotCanvas:
         # Re-draw
         self.canvas.queue_draw()
 
+    def pan(self, x, y):
+        xmin, xmax = self.axes.get_xlim()
+        ymin, ymax = self.axes.get_ylim()
+        width = xmax - xmin
+        height = ymax - ymin
+
+        # Adjust axes
+        for ax in self.figure.get_axes():
+            ax.set_xlim((xmin + x*width, xmax + x*width))
+            ax.set_ylim((ymin + y*height, ymax + y*height))
+
+        # Re-draw
+        self.canvas.queue_draw()
+
     def new_axes(self, name):
         """
         Creates and returns an Axes object attached to this object's Figure.
@@ -3089,10 +3258,31 @@ class PlotCanvas:
         :return: None
         """
         z, direction = event.get_scroll_direction()
-        if direction is Gdk.ScrollDirection.UP:
-            self.zoom(1.5, self.mouse)
-        else:
-            self.zoom(1/1.5, self.mouse)
+
+        if self.key is None:
+
+            if direction is Gdk.ScrollDirection.UP:
+                self.zoom(1.5, self.mouse)
+            else:
+                self.zoom(1/1.5, self.mouse)
+            return
+
+        if self.key == 'shift':
+
+            if direction is Gdk.ScrollDirection.UP:
+                self.pan(0.3, 0)
+            else:
+                self.pan(-0.3, 0)
+            return
+
+        if self.key == 'ctrl+control':
+
+            if direction is Gdk.ScrollDirection.UP:
+                self.pan(0, 0.3)
+            else:
+                self.pan(0, -0.3)
+            return
+
 
     def on_mouse_move(self, event):
         """

+ 73 - 0
FlatCAM.ui

@@ -72,11 +72,31 @@ THE SOFTWARE.</property>
     <property name="can_focus">False</property>
     <property name="stock">gtk-info</property>
   </object>
+  <object class="GtkImage" id="image17">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="pixbuf">share/clear_plot16.png</property>
+  </object>
+  <object class="GtkImage" id="image18">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="pixbuf">share/clear_plot16.png</property>
+  </object>
+  <object class="GtkImage" id="image19">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="pixbuf">share/replot16.png</property>
+  </object>
   <object class="GtkImage" id="image2">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
     <property name="stock">gtk-open</property>
   </object>
+  <object class="GtkImage" id="image20">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="stock">gtk-open</property>
+  </object>
   <object class="GtkImage" id="image3">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
@@ -3136,6 +3156,15 @@ objects and options.</property>
                         <signal name="activate" handler="on_file_new" swapped="no"/>
                       </object>
                     </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="open_recent">
+                        <property name="label" translatable="yes">Open recent</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="image">image20</property>
+                        <property name="use_stock">False</property>
+                      </object>
+                    </child>
                     <child>
                       <object class="GtkImageMenuItem" id="imagemenuitem2">
                         <property name="label">Open Gerber</property>
@@ -3289,6 +3318,50 @@ project.</property>
                 </child>
               </object>
             </child>
+            <child>
+              <object class="GtkMenuItem" id="menuitem14">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">View</property>
+                <property name="use_underline">True</property>
+                <child type="submenu">
+                  <object class="GtkMenu" id="menu7">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem14">
+                        <property name="label" translatable="yes">Disable all plots</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="image">image17</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" handler="on_disable_all_plots" swapped="no"/>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem15">
+                        <property name="label" translatable="yes">Disable all plots but this one</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="image">image18</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" handler="on_disable_all_plots_not_current" swapped="no"/>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem16">
+                        <property name="label" translatable="yes">Enable all plots</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="image">image19</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" handler="enable_all_plots" swapped="no"/>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
             <child>
               <object class="GtkMenuItem" id="menuitem3">
                 <property name="visible">True</property>

+ 1 - 0
recent.json

@@ -0,0 +1 @@
+[{"kind": "project", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\full.fcproj"}, {"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\squarerpcb\\KiCad_Squarer.drl"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\VNA\\KiCad_Squarer\\KiCad_Squarer-F_Cu.gtl"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\PLLs\\RTWO\\Project Outputs for RTWO1\\PCB1.GTL"}]