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

This commit is contained in:
Juan Pablo Caram
2014-03-15 00:56:31 -04:00
parent 26c32acd81
commit 5312e78f92
3 changed files with 273 additions and 9 deletions

View File

@@ -9,6 +9,7 @@
import threading import threading
# TODO: Bundle together. This is just for debugging. # TODO: Bundle together. This is just for debugging.
from docutils.nodes import image
from gi.repository import Gtk from gi.repository import Gtk
from gi.repository import Gdk from gi.repository import Gdk
from gi.repository import GdkPixbuf from gi.repository import GdkPixbuf
@@ -695,6 +696,7 @@ class App:
self.setup_obj_classes() self.setup_obj_classes()
self.stuff = {} # FlatCAMObj's by name self.stuff = {} # FlatCAMObj's by name
self.mouse = None # Mouse coordinates over plot self.mouse = None # Mouse coordinates over plot
self.recent = []
# What is selected by the user. It is # What is selected by the user. It is
# a key if self.stuff # a key if self.stuff
@@ -755,6 +757,7 @@ class App:
self.options.update(self.defaults) # Copy app defaults to project options self.options.update(self.defaults) # Copy app defaults to project options
self.options2form() # Populate the app defaults form self.options2form() # Populate the app defaults form
self.units_label.set_text("[" + self.options["units"] + "]") self.units_label.set_text("[" + self.options["units"] + "]")
self.setup_recent_items()
#### Check for updates #### #### Check for updates ####
self.version = 3 self.version = 3
@@ -876,6 +879,44 @@ class App:
box1.show() box1.show()
label1.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): def info(self, text):
""" """
Show text on the status bar. Show text on the status bar.
@@ -1282,6 +1323,8 @@ class App:
f.close() f.close()
return return
self.register_recent("project", filename)
# Clear the current project # Clear the current project
self.on_file_new(None) self.on_file_new(None)
@@ -1377,9 +1420,99 @@ class App:
def do_nothing(self, param): def do_nothing(self, param):
return 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 ## ## 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): def on_offset_object(self, widget):
""" """
Offsets the object's geometry by the vector specified Offsets the object's geometry by the vector specified
@@ -1710,6 +1843,7 @@ class App:
self.on_file_saveprojectas(None) self.on_file_saveprojectas(None)
else: else:
self.save_project(self.project_filename) self.save_project(self.project_filename)
self.register_recent("project", self.project_filename)
self.info("Project saved to: " + self.project_filename) self.info("Project saved to: " + self.project_filename)
def on_file_saveprojectas(self, param): def on_file_saveprojectas(self, param):
@@ -1726,6 +1860,7 @@ class App:
assert isinstance(app_obj, App) assert isinstance(app_obj, App)
app_obj.save_project(filename) app_obj.save_project(filename)
self.project_filename = filename self.project_filename = filename
self.register_recent("project", filename)
app_obj.info("Project saved to: " + filename) app_obj.info("Project saved to: " + filename)
self.file_chooser_save_action(on_success) self.file_chooser_save_action(on_success)
@@ -1744,6 +1879,7 @@ class App:
def on_success(app_obj, filename): def on_success(app_obj, filename):
assert isinstance(app_obj, App) assert isinstance(app_obj, App)
app_obj.save_project(filename) app_obj.save_project(filename)
self.register_recent("project", filename)
app_obj.info("Project copy saved to: " + filename) app_obj.info("Project copy saved to: " + filename)
self.file_chooser_save_action(on_success) self.file_chooser_save_action(on_success)
@@ -1998,10 +2134,7 @@ class App:
def thread_func(app_obj): def thread_func(app_obj):
assert isinstance(app_obj, App) 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() 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")) GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, "Idle"))
t = threading.Thread(target=thread_func, args=(self,)) t = threading.Thread(target=thread_func, args=(self,))
@@ -2526,6 +2659,7 @@ class App:
name = filename.split('/')[-1].split('\\')[-1] name = filename.split('/')[-1].split('\\')[-1]
app_obj.new_object("gerber", name, obj_init) 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.idle_add(lambda: app_obj.set_progress_bar(1.0, "Done!"))
GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, "")) 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] name = filename.split('/')[-1].split('\\')[-1]
app_obj.new_object("excellon", name, obj_init) 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.idle_add(lambda: app_obj.set_progress_bar(1.0, "Done!"))
GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, "")) 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] name = filename.split('/')[-1].split('\\')[-1]
app_obj.new_object("cncjob", name, obj_init) 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.idle_add(lambda: app_obj.set_progress_bar(1.0, "Done!"))
GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, "")) 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. '1' Zoom-fit. Fits the axes limits to the data.
'2' Zoom-out. '2' Zoom-out.
'3' Zoom-in. '3' Zoom-in.
'm' Toggle on-off the measuring tool.
========== ============================================ ========== ============================================
:param event: Ignored. :param event: Ignored.
:return: None :return: None
""" """
#print 'you pressed', event.key, event.xdata, event.ydata
if event.key == '1': # 1 if event.key == '1': # 1
self.on_zoom_fit(None) self.on_zoom_fit(None)
@@ -2901,8 +3037,27 @@ class PlotCanvas:
self.canvas.connect('configure-event', self.auto_adjust_axes) self.canvas.connect('configure-event', self.auto_adjust_axes)
self.canvas.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK) self.canvas.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK)
self.canvas.connect("scroll-event", self.on_scroll) 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.mouse = [0, 0]
self.key = None
def on_key_down(self, event):
"""
:param event:
:return:
"""
self.key = event.key
def on_key_up(self, event):
"""
:param event:
:return:
"""
self.key = None
def mpl_connect(self, event_name, callback): def mpl_connect(self, event_name, callback):
""" """
@@ -3054,6 +3209,20 @@ class PlotCanvas:
# Re-draw # Re-draw
self.canvas.queue_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): def new_axes(self, name):
""" """
Creates and returns an Axes object attached to this object's Figure. Creates and returns an Axes object attached to this object's Figure.
@@ -3089,10 +3258,31 @@ class PlotCanvas:
:return: None :return: None
""" """
z, direction = event.get_scroll_direction() z, direction = event.get_scroll_direction()
if direction is Gdk.ScrollDirection.UP:
self.zoom(1.5, self.mouse) if self.key is None:
else:
self.zoom(1/1.5, self.mouse) 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): def on_mouse_move(self, event):
""" """

View File

@@ -72,11 +72,31 @@ THE SOFTWARE.</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="stock">gtk-info</property> <property name="stock">gtk-info</property>
</object> </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"> <object class="GtkImage" id="image2">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="stock">gtk-open</property> <property name="stock">gtk-open</property>
</object> </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"> <object class="GtkImage" id="image3">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
@@ -3136,6 +3156,15 @@ objects and options.</property>
<signal name="activate" handler="on_file_new" swapped="no"/> <signal name="activate" handler="on_file_new" swapped="no"/>
</object> </object>
</child> </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> <child>
<object class="GtkImageMenuItem" id="imagemenuitem2"> <object class="GtkImageMenuItem" id="imagemenuitem2">
<property name="label">Open Gerber</property> <property name="label">Open Gerber</property>
@@ -3289,6 +3318,50 @@ project.</property>
</child> </child>
</object> </object>
</child> </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> <child>
<object class="GtkMenuItem" id="menuitem3"> <object class="GtkMenuItem" id="menuitem3">
<property name="visible">True</property> <property name="visible">True</property>

1
recent.json Normal file
View File

@@ -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"}]