Commit 4ad99e71 authored by sumpfralle's avatar sumpfralle

added a progress bar for drill operations (multi-threaded)


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@168 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent 62a21957
...@@ -17,6 +17,8 @@ import OpenGL.GLUT as GLUT ...@@ -17,6 +17,8 @@ import OpenGL.GLUT as GLUT
#import gtk.gtkgl #import gtk.gtkgl
import pygtk import pygtk
import gtk import gtk
import gobject
import threading
import time import time
import os import os
import sys import sys
...@@ -262,11 +264,14 @@ class ProjectGui: ...@@ -262,11 +264,14 @@ class ProjectGui:
def __init__(self, master=None, no_dialog=False): def __init__(self, master=None, no_dialog=False):
""" TODO: remove "master" above when the Tk interface is abandoned""" """ TODO: remove "master" above when the Tk interface is abandoned"""
gtk.gdk.threads_init()
self.settings = pycam.Gui.Settings.Settings() self.settings = pycam.Gui.Settings.Settings()
self.gui_is_active = False self.gui_is_active = False
self.view3d = None self.view3d = None
self.no_dialog = no_dialog self.no_dialog = no_dialog
self._batch_queue = [] self._batch_queue = []
self._progress_running = False
self._progress_cancel_requested = threading.Event()
self.gui = gtk.Builder() self.gui = gtk.Builder()
self.gui.add_from_file(GTKBUILD_FILE) self.gui.add_from_file(GTKBUILD_FILE)
self.window = self.gui.get_object("ProjectWindow") self.window = self.gui.get_object("ProjectWindow")
...@@ -433,11 +438,28 @@ class ProjectGui: ...@@ -433,11 +438,28 @@ class ProjectGui:
filter.add_pattern("*.conf") filter.add_pattern("*.conf")
self.processing_file_selector.add_filter(filter) self.processing_file_selector.add_filter(filter)
self.processing_file_selector.set_filter(filter) self.processing_file_selector.set_filter(filter)
# progress bar and task pane
self.progress_bar = self.gui.get_object("ProgressBar")
self.progress_widget = self.gui.get_object("ProgressWidget")
self.task_pane = self.gui.get_object("Tasks")
self.gui.get_object("ProgressCancelButton").connect("clicked", self.cancel_progress)
# make sure that the toolpath settings are consistent # make sure that the toolpath settings are consistent
self.disable_invalid_toolpath_settings() self.disable_invalid_toolpath_settings()
if not self.no_dialog: if not self.no_dialog:
self.window.show() self.window.show()
def progress_activity_guard(func):
def wrapper(self, *args, **kwargs):
if self._progress_running:
return
self._progress_running = True
self._progress_cancel_requested.clear()
self.toggle_progress_bar(True)
func(self, *args, **kwargs)
self.toggle_progress_bar(False)
self._progress_running = False
return wrapper
def gui_activity_guard(func): def gui_activity_guard(func):
def wrapper(self, *args, **kwargs): def wrapper(self, *args, **kwargs):
if self.gui_is_active: if self.gui_is_active:
...@@ -709,22 +731,55 @@ class ProjectGui: ...@@ -709,22 +731,55 @@ class ProjectGui:
if not self.processing_settings.write_to_file(filename) and not no_dialog: if not self.processing_settings.write_to_file(filename) and not no_dialog:
show_error_dialog(self.window, "Failed to save processing settings file") show_error_dialog(self.window, "Failed to save processing settings file")
def toggle_progress_bar(self, status):
if status:
self.task_pane.set_sensitive(False)
self.update_progress_bar()
self.progress_widget.show()
else:
self.progress_widget.hide()
self.task_pane.set_sensitive(True)
def update_progress_bar(self, text=None, percent=None):
if not percent is None:
percent = min(max(percent, 0.0), 100.0)
self.progress_bar.set_fraction(percent/100.0)
if not text is None:
self.progress_bar.set_text(text)
def cancel_progress(self, widget=None):
self._progress_cancel_requested.set()
@gui_activity_guard @gui_activity_guard
def generate_toolpath(self, widget=None, data=None): def generate_toolpath(self, widget=None, data=None):
thread = threading.Thread(target=self.generate_toolpath_threaded)
thread.start()
@progress_activity_guard
def generate_toolpath_threaded(self):
start_time = time.time() start_time = time.time()
parent = self
class UpdateView: class UpdateView:
def __init__(self, func, max_fps=1): def __init__(self, func, max_fps=1, event=None):
self.last_update = time.time() self.last_update = time.time()
self.max_fps = max_fps self.max_fps = max_fps
self.func = func self.func = func
def update(self): self.event = event
def update(self, text=None, percent=None):
gobject.idle_add(parent.update_progress_bar, text, percent)
if (time.time() - self.last_update) > 1.0/self.max_fps: if (time.time() - self.last_update) > 1.0/self.max_fps:
self.last_update = time.time() self.last_update = time.time()
self.func() if self.func:
gobject.idle_add(self.func)
# return if the shared event was set
return self.event and self.event.isSet()
if self.settings.get("show_drill_progress"): if self.settings.get("show_drill_progress"):
draw_callback = UpdateView(self.update_view, self.settings.get("drill_progress_max_fps")).update callback = self.update_view
else: else:
draw_callback = None callback = None
draw_callback = UpdateView(callback,
max_fps=self.settings.get("drill_progress_max_fps"),
event=self._progress_cancel_requested).update
radius = self.settings.get("tool_radius") radius = self.settings.get("tool_radius")
cuttername = self.settings.get("cutter_shape") cuttername = self.settings.get("cutter_shape")
pathgenerator = self.settings.get("path_generator") pathgenerator = self.settings.get("path_generator")
...@@ -743,7 +798,7 @@ class ProjectGui: ...@@ -743,7 +798,7 @@ class ProjectGui:
self.update_physics() self.update_physics()
# TODO: check, why this offset is used # this offset allows to cut a model with a minimal boundary box correctly
offset = radius/2 offset = radius/2
minx = float(self.settings.get("minx"))-offset minx = float(self.settings.get("minx"))-offset
...@@ -803,7 +858,7 @@ class ProjectGui: ...@@ -803,7 +858,7 @@ class ProjectGui:
elif direction == "xy": elif direction == "xy":
self.toolpath = self.pathgenerator.GenerateToolPath(minx, maxx, miny, maxy, minz, maxz, dy, dy, dz, draw_callback) self.toolpath = self.pathgenerator.GenerateToolPath(minx, maxx, miny, maxy, minz, maxz, dy, dy, dz, draw_callback)
print "Time elapsed: %f" % (time.time() - start_time) print "Time elapsed: %f" % (time.time() - start_time)
self.update_view() gobject.idle_add(self.update_view)
# for compatibility with old pycam GUI (see pycam.py) # for compatibility with old pycam GUI (see pycam.py)
# TODO: remove it in v0.2 # TODO: remove it in v0.2
......
This diff is collapsed.
...@@ -3,6 +3,7 @@ from pycam.Geometry import * ...@@ -3,6 +3,7 @@ from pycam.Geometry import *
from pycam.Geometry.intersection import intersect_lines from pycam.Geometry.intersection import intersect_lines
import pycam.PathGenerators import pycam.PathGenerators
from pycam.Geometry.utils import INFINITE from pycam.Geometry.utils import INFINITE
import math
import sys import sys
...@@ -49,6 +50,9 @@ class DropCutter: ...@@ -49,6 +50,9 @@ class DropCutter:
self._boundary_warning_already_shown = False self._boundary_warning_already_shown = False
last_outer_loop = False last_outer_loop = False
num_of_lines = math.ceil((dims[1].max - dims[1].min) / d1)
current_line = 0
while not finished_plane: while not finished_plane:
last_inner_loop = False last_inner_loop = False
finished_line = False finished_line = False
...@@ -56,6 +60,13 @@ class DropCutter: ...@@ -56,6 +60,13 @@ class DropCutter:
pa.new_scanline() pa.new_scanline()
self._triangle_last = None self._triangle_last = None
self._cut_last = None self._cut_last = None
if draw_callback and draw_callback(text="DropCutter: processing line %d/%d" \
% (current_line, num_of_lines),
percent=(100.0 * current_line / num_of_lines)):
# cancel requested
finished_plane = True
while not finished_line: while not finished_line:
if self.physics: if self.physics:
points = self.get_max_height_with_ode(dims[x], dims[y], dim_height, order=dim_attrs[:]) points = self.get_max_height_with_ode(dims[x], dims[y], dim_height, order=dim_attrs[:])
...@@ -65,8 +76,8 @@ class DropCutter: ...@@ -65,8 +76,8 @@ class DropCutter:
for next_point in points: for next_point in points:
pa.append(next_point) pa.append(next_point)
self.cutter.moveto(next_point) self.cutter.moveto(next_point)
if draw_callback: if draw_callback and draw_callback():
draw_callback() finished_line = True
dims[0].shift(d0) dims[0].shift(d0)
...@@ -89,6 +100,9 @@ class DropCutter: ...@@ -89,6 +100,9 @@ class DropCutter:
else: else:
finished_plane = True finished_plane = True
# update progress
current_line += 1
pa.end_direction() pa.end_direction()
pa.finish() pa.finish()
......
...@@ -46,12 +46,22 @@ class PushCutter: ...@@ -46,12 +46,22 @@ class PushCutter:
paths = [] paths = []
current_layer = 0
num_of_layers = math.ceil((maxz - minz) / dz)
if self.physics is None: if self.physics is None:
GenerateToolPathSlice = self.GenerateToolPathSlice_triangles GenerateToolPathSlice = self.GenerateToolPathSlice_triangles
else: else:
GenerateToolPathSlice = self.GenerateToolPathSlice_ode GenerateToolPathSlice = self.GenerateToolPathSlice_ode
while z >= minz: while z >= minz:
# update the progress bar and check, if we should cancel the process
if draw_callback and draw_callback(text="PushCutter: processing layer %d/%d" \
% (current_layer, num_of_layers),
percent=(100.0 * current_layer / num_of_layers)):
# cancel immediately
z = minz - 1
if dy > 0: if dy > 0:
self.pa.new_direction(0) self.pa.new_direction(0)
GenerateToolPathSlice(minx, maxx, miny, maxy, z, 0, dy, draw_callback) GenerateToolPathSlice(minx, maxx, miny, maxy, z, 0, dy, draw_callback)
...@@ -70,6 +80,8 @@ class PushCutter: ...@@ -70,6 +80,8 @@ class PushCutter:
# never skip the outermost bounding limit - reduce the step size if required # never skip the outermost bounding limit - reduce the step size if required
z = minz z = minz
current_layer += 1
if DEBUG_PUSHCUTTER2: if DEBUG_PUSHCUTTER2:
self.svg.fill('none') self.svg.fill('none')
self.svg.stroke('black') self.svg.stroke('black')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment