Commit 2c37320f authored by sumpfralle's avatar sumpfralle

external program locations (inkscape/pstoedit) are now configurable via the GUI


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@584 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent 833dd869
...@@ -30,6 +30,7 @@ import pycam.Toolpath.Generator ...@@ -30,6 +30,7 @@ import pycam.Toolpath.Generator
import pycam.Toolpath import pycam.Toolpath
import pycam.Importers import pycam.Importers
import pycam.Utils.log import pycam.Utils.log
import pycam.Utils
from pycam.Geometry.utils import sqrt from pycam.Geometry.utils import sqrt
from pycam.Gui.OpenGLTools import ModelViewWindowGL from pycam.Gui.OpenGLTools import ModelViewWindowGL
from pycam.Toolpath import Bounds from pycam.Toolpath import Bounds
...@@ -87,6 +88,8 @@ PREFERENCES_DEFAULTS = { ...@@ -87,6 +88,8 @@ PREFERENCES_DEFAULTS = {
"view_polygon": True, "view_polygon": True,
"simulation_details_level": 3, "simulation_details_level": 3,
"drill_progress_max_fps": 2, "drill_progress_max_fps": 2,
"external_program_inkscape": "",
"external_program_pstoedit": "",
} }
""" the listed items will be loaded/saved via the preferences file in the """ the listed items will be loaded/saved via the preferences file in the
user's home directory on startup/shutdown""" user's home directory on startup/shutdown"""
...@@ -266,6 +269,8 @@ class ProjectGui: ...@@ -266,6 +269,8 @@ class ProjectGui:
# Calculate the "minx, ..." settings based on a (potentially) selected # Calculate the "minx, ..." settings based on a (potentially) selected
# bounds setting. # bounds setting.
def get_absolute_limit(key): def get_absolute_limit(key):
if self.model is None:
return None
bounds = self.settings.get("current_bounds") bounds = self.settings.get("current_bounds")
if bounds is None: if bounds is None:
return getattr(self.model, key) return getattr(self.model, key)
...@@ -581,6 +586,21 @@ class ProjectGui: ...@@ -581,6 +586,21 @@ class ProjectGui:
obj = self.gui.get_object(objname) obj = self.gui.get_object(objname)
self._task_property_signals.append((obj, self._task_property_signals.append((obj,
obj.connect("changed", self._handle_task_setting_change))) obj.connect("changed", self._handle_task_setting_change)))
# configure locations of external programs
for auto_control_name, location_control_name, browse_button, key in (
("ExternalProgramInkscapeAuto",
"ExternalProgramInkscapeControl",
"ExternalProgramInkscapeBrowse", "inkscape"),
("ExternalProgramPstoeditAuto",
"ExternalProgramPstoeditControl",
"ExternalProgramPstoeditBrowse", "pstoedit")):
self.gui.get_object(auto_control_name).connect("clicked",
self._locate_external_program, key)
location_control = self.gui.get_object(location_control_name)
self.settings.add_item("external_program_%s" % key,
location_control.get_text, location_control.set_text)
self.gui.get_object(browse_button).connect("clicked",
self._browse_external_program_location, key)
# menu bar # menu bar
uimanager = gtk.UIManager() uimanager = gtk.UIManager()
self._accel_group = uimanager.get_accel_group() self._accel_group = uimanager.get_accel_group()
...@@ -821,6 +841,23 @@ class ProjectGui: ...@@ -821,6 +841,23 @@ class ProjectGui:
else: else:
self.grid_adjustment_selector.set_active(-1) self.grid_adjustment_selector.set_active(-1)
def _browse_external_program_location(self, widget=None, key=None):
location = self.get_filename_via_dialog(title="Select the executable " \
+ "for '%s'" % key, mode_load=True)
if not location is None:
self.settings.set("external_program_%s" % key, location)
def _locate_external_program(self, widget=None, key=None):
# the button was just activated
location = pycam.Utils.get_external_program_location(key)
if location is None:
log.error("Failed to locate the external program '%s'. " % key \
+ "Please install the program and try again.\nOr maybe" \
+ "you need to specify the location manually.")
else:
# store the new setting
self.settings.set("external_program_%s" % key, location)
@gui_activity_guard @gui_activity_guard
def adjust_bounds(self, widget, axis, change): def adjust_bounds(self, widget, axis, change):
...@@ -1169,6 +1206,11 @@ class ProjectGui: ...@@ -1169,6 +1206,11 @@ class ProjectGui:
def add_log_message(self, title, message, record=None): def add_log_message(self, title, message, record=None):
timestamp = datetime.datetime.fromtimestamp(record.created).strftime("%H:%M") timestamp = datetime.datetime.fromtimestamp(record.created).strftime("%H:%M")
try:
message = message.encode("utf-8")
except UnicodeDecodeError:
# remove all non-ascii characters
message = "".join([char for char in message if ord(char) < 128])
self.log_model.append((timestamp, title, message)) self.log_model.append((timestamp, title, message))
@gui_activity_guard @gui_activity_guard
...@@ -1612,9 +1654,10 @@ class ProjectGui: ...@@ -1612,9 +1654,10 @@ class ProjectGui:
continue continue
value_raw = config.get("DEFAULT", item) value_raw = config.get("DEFAULT", item)
old_value = self.settings.get(item) old_value = self.settings.get(item)
if isinstance(old_value, basestring): value_type = type(PREFERENCES_DEFAULTS[item])
if isinstance(value_type(), basestring):
# keep strings as they are # keep strings as they are
value = value_raw value = str(value_raw)
else: else:
# parse tuples, integers, bools, ... # parse tuples, integers, bools, ...
value = eval(value_raw) value = eval(value_raw)
...@@ -1777,10 +1820,16 @@ class ProjectGui: ...@@ -1777,10 +1820,16 @@ class ProjectGui:
filename = self.get_filename_via_dialog("Loading model ...", filename = self.get_filename_via_dialog("Loading model ...",
mode_load=True, type_filter=FILTER_MODEL) mode_load=True, type_filter=FILTER_MODEL)
if filename: if filename:
# import all external program locations into a dict
program_locations = {}
prefix = "external_program_"
for key in self.settings.get_keys():
if key.startswith(prefix) and self.settings.get(key):
program_locations[key[len(prefix):]] = self.settings.get(key)
file_type, importer = pycam.Importers.detect_file_type(filename) file_type, importer = pycam.Importers.detect_file_type(filename)
if file_type and callable(importer): if file_type and callable(importer):
# TODO: get the "program_locations" self.load_model(importer(filename,
self.load_model(importer(filename, program_locations=None)) program_locations=program_locations))
self.set_model_filename(filename) self.set_model_filename(filename)
else: else:
log.error("Failed to detect filetype!") log.error("Failed to detect filetype!")
......
...@@ -113,6 +113,9 @@ class Settings: ...@@ -113,6 +113,9 @@ class Settings:
result[key] = self.get(key) result[key] = self.get(key)
return str(result) return str(result)
def get_keys(self):
return self.items.keys()
class ProcessSettings: class ProcessSettings:
......
...@@ -4712,6 +4712,18 @@ upon interesting bugs and weird results.</property> ...@@ -4712,6 +4712,18 @@ upon interesting bugs and weird results.</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="spacing">2</property> <property name="spacing">2</property>
<child>
<object class="GtkImage" id="logo">
<property name="visible">True</property>
<property name="ypad">2</property>
<property name="pixbuf">logo_gui.png</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child> <child>
<object class="GtkNotebook" id="notebook1"> <object class="GtkNotebook" id="notebook1">
<property name="visible">True</property> <property name="visible">True</property>
...@@ -5384,7 +5396,7 @@ It is significantly faster, but the current release of ODE contains a nasty bug ...@@ -5384,7 +5396,7 @@ It is significantly faster, but the current release of ODE contains a nasty bug
<object class="GtkTable" id="table11"> <object class="GtkTable" id="table11">
<property name="visible">True</property> <property name="visible">True</property>
<property name="n_rows">2</property> <property name="n_rows">2</property>
<property name="n_columns">3</property> <property name="n_columns">4</property>
<property name="column_spacing">3</property> <property name="column_spacing">3</property>
<child> <child>
<object class="GtkLinkButton" id="ExternalProgramInkscapeLink"> <object class="GtkLinkButton" id="ExternalProgramInkscapeLink">
...@@ -5403,23 +5415,59 @@ It is significantly faster, but the current release of ODE contains a nasty bug ...@@ -5403,23 +5415,59 @@ It is significantly faster, but the current release of ODE contains a nasty bug
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkFileChooserButton" id="ExternalProgramInkscapeChooser"> <object class="GtkLinkButton" id="ExternalProgramPstoeditLink">
<property name="label" translatable="yes">pstoedit</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="title" translatable="yes">Choose location of Inkscape</property> <property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">pstoedit is a postscript conversion tool. It can be used to convert 2D SVG files into DXF contour models.</property>
<property name="relief">none</property>
<property name="xalign">0</property>
<property name="uri">http://sourceforge.net/apps/mediawiki/pycam/index.php?title=Requirements#Inkscape</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="top_attach">1</property>
<property name="right_attach">2</property> <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property> <property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkCheckButton" id="ExternalProgramInkscapeAuto"> <object class="GtkButton" id="ExternalProgramInkscapeAuto">
<property name="label" translatable="yes">auto</property> <property name="label">Detect</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">True</property>
<property name="draw_indicator">True</property> </object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ExternalProgramPstoeditAuto">
<property name="label">Detect</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ExternalProgramInkscapeBrowse">
<property name="label" translatable="yes">Browse</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">2</property> <property name="left_attach">2</property>
...@@ -5429,17 +5477,15 @@ It is significantly faster, but the current release of ODE contains a nasty bug ...@@ -5429,17 +5477,15 @@ It is significantly faster, but the current release of ODE contains a nasty bug
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLinkButton" id="ExternalProgramPstoeditLink"> <object class="GtkButton" id="ExternalProgramPstoeditBrowse">
<property name="label" translatable="yes">pstoedit</property> <property name="label" translatable="yes">Browse</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">True</property> <property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">pstoedit is a postscript conversion tool. It can be used to convert 2D SVG files into DXF contour models.</property>
<property name="relief">none</property>
<property name="xalign">0</property>
<property name="uri">http://sourceforge.net/apps/mediawiki/pycam/index.php?title=Requirements#Inkscape</property>
</object> </object>
<packing> <packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>
<property name="bottom_attach">2</property> <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property> <property name="x_options">GTK_FILL</property>
...@@ -5447,32 +5493,28 @@ It is significantly faster, but the current release of ODE contains a nasty bug ...@@ -5447,32 +5493,28 @@ It is significantly faster, but the current release of ODE contains a nasty bug
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkFileChooserButton" id="ExternalProgramPstoeditChooser"> <object class="GtkEntry" id="ExternalProgramInkscapeControl">
<property name="visible">True</property> <property name="visible">True</property>
<property name="title" translatable="yes">Choose location of pstoedit</property> <property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property> <property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property> <property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkCheckButton" id="ExternalProgramPstoeditAuto"> <object class="GtkEntry" id="ExternalProgramPstoeditControl">
<property name="label" translatable="yes">auto</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="invisible_char">&#x25CF;</property>
<property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
<property name="left_attach">2</property> <property name="left_attach">1</property>
<property name="right_attach">3</property> <property name="right_attach">2</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>
<property name="bottom_attach">2</property> <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property> <property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
...@@ -5504,7 +5546,7 @@ It is significantly faster, but the current release of ODE contains a nasty bug ...@@ -5504,7 +5546,7 @@ It is significantly faster, but the current release of ODE contains a nasty bug
</child> </child>
</object> </object>
<packing> <packing>
<property name="position">1</property> <property name="position">3</property>
</packing> </packing>
</child> </child>
<child internal-child="action_area"> <child internal-child="action_area">
......
...@@ -22,6 +22,40 @@ along with PyCAM. If not, see <http://www.gnu.org/licenses/>. ...@@ -22,6 +22,40 @@ along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
__all__ = [ "iterators", "polynomials", "ProgressCounter"] __all__ = [ "iterators", "polynomials", "ProgressCounter"]
import os
# this is imported below on demand
#import win32.com
def get_external_program_location(key):
extensions = ["", ".exe"]
potential_names = ["%s%s" % (key, ext) for ext in extensions]
windows_program_directories = {'inkscape': ['Inkscape'],
'pstoedit': ['pstoedit']}
# go through the PATH environment variable
if "PATH" in os.environ:
path_env = os.environ["PATH"]
for one_dir in path_env.split(os.pathsep):
for basename in potential_names:
location = os.path.join(one_dir, basename)
if os.path.isfile(location):
return location
# do a manual scan in the programs directory (only for windows)
try:
from win32com.shell import shellcon, shell
program_dir = shell.SHGetFolderPath(0, shellcon.CSIDL_PROGRAM_FILES, 0, 0)
except ImportError:
# no other options for non-windows systems
return None
# scan the program directory
for subdir in windows_program_directories[key]:
for basename in potential_names:
location = os.path.join(program_dir, sub_dir, basename)
if os.path.isfile(location):
return location
# nothing found
return None
class ProgressCounter: class ProgressCounter:
......
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