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
......
...@@ -5,6 +5,10 @@ ...@@ -5,6 +5,10 @@
<object class="GtkWindow" id="ProjectWindow"> <object class="GtkWindow" id="ProjectWindow">
<property name="title" translatable="yes">PyCAM Settings</property> <property name="title" translatable="yes">PyCAM Settings</property>
<property name="destroy_with_parent">True</property> <property name="destroy_with_parent">True</property>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child> <child>
<object class="GtkNotebook" id="Tasks"> <object class="GtkNotebook" id="Tasks">
<property name="visible">True</property> <property name="visible">True</property>
...@@ -118,7 +122,7 @@ ...@@ -118,7 +122,7 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="spacing">3</property> <property name="spacing">3</property>
<child> <child>
<object class="GtkVBox" id="vbox1"> <object class="GtkVBox" id="vbox2">
<property name="visible">True</property> <property name="visible">True</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
...@@ -498,7 +502,7 @@ ...@@ -498,7 +502,7 @@
<property name="yscale">0</property> <property name="yscale">0</property>
<property name="left_padding">8</property> <property name="left_padding">8</property>
<child> <child>
<object class="GtkVBox" id="vbox2"> <object class="GtkVBox" id="vbox3">
<property name="visible">True</property> <property name="visible">True</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
...@@ -653,7 +657,7 @@ ...@@ -653,7 +657,7 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="left_padding">8</property> <property name="left_padding">8</property>
<child> <child>
<object class="GtkVBox" id="vbox3"> <object class="GtkVBox" id="vbox6">
<property name="visible">True</property> <property name="visible">True</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
...@@ -1224,7 +1228,6 @@ ...@@ -1224,7 +1228,6 @@
<object class="GtkSpinButton" id="TorusRadiusControl"> <object class="GtkSpinButton" id="TorusRadiusControl">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Radius of the "doughnut" top of a Toroidal Cutter.</property>
<property name="invisible_char">&#x2022;</property> <property name="invisible_char">&#x2022;</property>
<property name="adjustment">torusradius</property> <property name="adjustment">torusradius</property>
<property name="digits">3</property> <property name="digits">3</property>
...@@ -1392,8 +1395,6 @@ ...@@ -1392,8 +1395,6 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">The Drop Cutter abrades a limited slice of material in each step.
It does not work with the "xy" direction and it requires the "Path Accumulator" or the "ZigZag Cutter".</property>
<property name="active">True</property> <property name="active">True</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
...@@ -1489,8 +1490,6 @@ It does not work with the "xy" direction and it requires the "Path Accumulator" ...@@ -1489,8 +1490,6 @@ It does not work with the "xy" direction and it requires the "Path Accumulator"
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Dynamic switching between x and y axis direction.
Only available for the Push Cutter.</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<property name="group">PathDirectionX</property> <property name="group">PathDirectionX</property>
</object> </object>
...@@ -1553,7 +1552,6 @@ Only available for the Push Cutter.</property> ...@@ -1553,7 +1552,6 @@ Only available for the Push Cutter.</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Practically useless path processor for testing purposes.</property>
<property name="active">True</property> <property name="active">True</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
...@@ -1568,7 +1566,6 @@ Only available for the Push Cutter.</property> ...@@ -1568,7 +1566,6 @@ Only available for the Push Cutter.</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Quite simple path processor for finishing operations.</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<property name="group">PathAccumulator</property> <property name="group">PathAccumulator</property>
</object> </object>
...@@ -1583,8 +1580,6 @@ Only available for the Push Cutter.</property> ...@@ -1583,8 +1580,6 @@ Only available for the Push Cutter.</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Practically useless path processor for testing purposes.
Only available for the Push Cutter.</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<property name="group">PathAccumulator</property> <property name="group">PathAccumulator</property>
</object> </object>
...@@ -1599,8 +1594,6 @@ Only available for the Push Cutter.</property> ...@@ -1599,8 +1594,6 @@ Only available for the Push Cutter.</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Complex path processor for first rough operations.
Only available for the Push Cutter.</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<property name="group">PathAccumulator</property> <property name="group">PathAccumulator</property>
</object> </object>
...@@ -1615,8 +1608,6 @@ Only available for the Push Cutter.</property> ...@@ -1615,8 +1608,6 @@ Only available for the Push Cutter.</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Complex path processor for semi-finish operations.
Only available for the Push Cutter.</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<property name="group">PathAccumulator</property> <property name="group">PathAccumulator</property>
</object> </object>
...@@ -1715,9 +1706,6 @@ Only available for the Push Cutter.</property> ...@@ -1715,9 +1706,6 @@ Only available for the Push Cutter.</property>
<object class="GtkSpinButton" id="MaterialAllowanceControl"> <object class="GtkSpinButton" id="MaterialAllowanceControl">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Specify the amount of material that should remain around the model.
This is useful for rough and semi-finish operations.
ODE support is currently required for this feature.</property>
<property name="invisible_char">&#x2022;</property> <property name="invisible_char">&#x2022;</property>
<property name="adjustment">MaterialAllowanceValue</property> <property name="adjustment">MaterialAllowanceValue</property>
<property name="digits">2</property> <property name="digits">2</property>
...@@ -1736,7 +1724,6 @@ ODE support is currently required for this feature.</property> ...@@ -1736,7 +1724,6 @@ ODE support is currently required for this feature.</property>
<object class="GtkSpinButton" id="MaxStepDownControl"> <object class="GtkSpinButton" id="MaxStepDownControl">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Height of each slice abraded by the Push Cutter.</property>
<property name="invisible_char">&#x2022;</property> <property name="invisible_char">&#x2022;</property>
<property name="adjustment">MaxStepDownValue</property> <property name="adjustment">MaxStepDownValue</property>
<property name="digits">2</property> <property name="digits">2</property>
...@@ -1873,7 +1860,7 @@ ODE support is currently required for this feature.</property> ...@@ -1873,7 +1860,7 @@ ODE support is currently required for this feature.</property>
<object class="GtkHBox" id="hbox6"> <object class="GtkHBox" id="hbox6">
<property name="visible">True</property> <property name="visible">True</property>
<child> <child>
<object class="GtkVBox" id="vbox6"> <object class="GtkVBox" id="vbox7">
<property name="visible">True</property> <property name="visible">True</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
...@@ -1895,8 +1882,6 @@ ODE support is currently required for this feature.</property> ...@@ -1895,8 +1882,6 @@ ODE support is currently required for this feature.</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Requires the Python bindings for ODE (Open Dynamics Engine).
Install the "python-pyode" package to enable this feature.</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
...@@ -2121,6 +2106,39 @@ Install the "python-pyode" package to enable this feature.</property> ...@@ -2121,6 +2106,39 @@ Install the "python-pyode" package to enable this feature.</property>
</packing> </packing>
</child> </child>
</object> </object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="ProgressWidget">
<child>
<object class="GtkProgressBar" id="ProgressBar">
<property name="visible">True</property>
<property name="ellipsize">end</property>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ProgressCancelButton">
<property name="label" translatable="yes">Cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
</child> </child>
</object> </object>
<object class="GtkAdjustment" id="scale factor"> <object class="GtkAdjustment" id="scale factor">
......
...@@ -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