Commit 1630be10 authored by sumpfralle's avatar sumpfralle

added drag'n'drop feature for PyCAM's window (loading model files)

enable non-local file handling (e.g. http, ...)


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@976 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent dfbffd2b
...@@ -13,6 +13,8 @@ Version 0.4.1 - UNRELEASED ...@@ -13,6 +13,8 @@ Version 0.4.1 - UNRELEASED
* added a configuration setting for automatically loading a custom task settings file on startup * added a configuration setting for automatically loading a custom task settings file on startup
* added a simple "undo" feature for reversing model manipulations * added a simple "undo" feature for reversing model manipulations
* added a minimum step width for GCode positions to be written * added a minimum step width for GCode positions to be written
* enabled drag'n'drop for loading models
* support non-local file handling (e.g. http, ...)
* the scroll wheel now behaves similarly to Inkscape's interface (pan and zoom) * the scroll wheel now behaves similarly to Inkscape's interface (pan and zoom)
* the default filename extension for exported GCode files is now configurable * the default filename extension for exported GCode files is now configurable
* unify DropCutter behaviour for models that are higher than the defined bounding box * unify DropCutter behaviour for models that are higher than the defined bounding box
......
...@@ -6262,7 +6262,7 @@ Hotkey: &lt;p&gt;</property> ...@@ -6262,7 +6262,7 @@ Hotkey: &lt;p&gt;</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="label" translatable="yes">&lt;span foreground="red"&gt;Warning: no local processes enabled. <property name="label" translatable="yes">&lt;span foreground="red"&gt;Warning: no local processes enabled.
You will need a remote server.&lt;/span&gt;</property> You will need remote workers.&lt;/span&gt;</property>
<property name="use_markup">True</property> <property name="use_markup">True</property>
<property name="wrap">True</property> <property name="wrap">True</property>
</object> </object>
...@@ -6361,7 +6361,7 @@ PyCAM's default port is 1250.</property> ...@@ -6361,7 +6361,7 @@ PyCAM's default port is 1250.</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="tooltip_text" translatable="yes">Type in a hostname or IP address if you want to connect to a remote PyCAM server. <property name="tooltip_text" translatable="yes">Type in a hostname or IP address if you want to connect to a remote PyCAM server.
Leave this field empty for a local-only server.</property> Leave this field empty for running a local server.</property>
<property name="invisible_char">&#x25CF;</property> <property name="invisible_char">&#x25CF;</property>
<property name="width_chars">18</property> <property name="width_chars">18</property>
<property name="truncate_multiline">True</property> <property name="truncate_multiline">True</property>
...@@ -6550,7 +6550,10 @@ Please check the following list of possible reasons and fix the problem, if poss ...@@ -6550,7 +6550,10 @@ Please check the following list of possible reasons and fix the problem, if poss
1) You are using the Windows standalone executable. 1) You are using the Windows standalone executable.
Sadly this problem is not too easy to solve. Future releases of PyCAM should remove this limitation. Sadly this problem is not too easy to solve. Future releases of PyCAM should remove this limitation.
2) You are using Python 2.5 or below and the package "multiprocessing" is &lt;i&gt;not&lt;/i&gt; installed.</property> 2) You are using Python 2.5 or below and the package "multiprocessing" is &lt;i&gt;not&lt;/i&gt; installed.
See the &lt;a href="http://sf.net/apps/mediawiki/pycam/?title=Parallel_Processing_on_different_Platforms"&gt;platform feature matrix&lt;/a&gt; for details.
</property>
<property name="use_markup">True</property> <property name="use_markup">True</property>
<property name="wrap">True</property> <property name="wrap">True</property>
</object> </object>
......
...@@ -39,7 +39,7 @@ from pycam.Gui.OpenGLTools import ModelViewWindowGL ...@@ -39,7 +39,7 @@ from pycam.Gui.OpenGLTools import ModelViewWindowGL
from pycam.Geometry.Letters import TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, \ from pycam.Geometry.Letters import TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, \
TEXT_ALIGN_RIGHT TEXT_ALIGN_RIGHT
import pycam.Geometry.Model import pycam.Geometry.Model
from pycam.Utils import ProgressCounter from pycam.Utils import ProgressCounter, check_uri_exists
from pycam.Toolpath import Bounds from pycam.Toolpath import Bounds
from pycam import VERSION from pycam import VERSION
import pycam.Physics.ode_physics import pycam.Physics.ode_physics
...@@ -362,6 +362,8 @@ class ProjectGui: ...@@ -362,6 +362,8 @@ class ProjectGui:
gtk.accel_map_change_entry(accel_path, key, mod, True) gtk.accel_map_change_entry(accel_path, key, mod, True)
# no undo is allowed at the beginning # no undo is allowed at the beginning
self.gui.get_object("UndoButton").set_sensitive(False) self.gui.get_object("UndoButton").set_sensitive(False)
# configure drag-n-drop for config files and models
self.configure_drag_and_drop(self.window)
# other events # other events
self.window.connect("destroy", self.destroy) self.window.connect("destroy", self.destroy)
# the settings window # the settings window
...@@ -2131,6 +2133,8 @@ class ProjectGui: ...@@ -2131,6 +2133,8 @@ class ProjectGui:
for name in ("GeneralSettings", "Help3DView")]) for name in ("GeneralSettings", "Help3DView")])
if self.model and self.view3d.enabled: if self.model and self.view3d.enabled:
self.view3d.reset_view() self.view3d.reset_view()
# configure drag-and-drop for the 3D window
self.configure_drag_and_drop(self.view3d.window)
# disable the "toggle" button, if the 3D view does not work # disable the "toggle" button, if the 3D view does not work
toggle_3d_checkbox.set_sensitive(self.view3d.enabled) toggle_3d_checkbox.set_sensitive(self.view3d.enabled)
else: else:
...@@ -2748,6 +2752,39 @@ class ProjectGui: ...@@ -2748,6 +2752,39 @@ class ProjectGui:
def quit(self): def quit(self):
self.save_preferences() self.save_preferences()
def configure_drag_and_drop(self, obj):
obj.connect("drag-data-received", self.handle_data_drop)
flags = gtk.DEST_DEFAULT_ALL
targets = [("text/uri-list", gtk.TARGET_OTHER_APP, 0),
("text/plain", gtk.TARGET_OTHER_APP, 1)]
actions = gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_LINK | \
gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_PRIVATE | \
gtk.gdk.ACTION_ASK
obj.drag_dest_set(flags, targets, actions)
def handle_data_drop(self, widget, drag_context, x, y, selection_data, info,
timestamp):
if info != 0:
uris = [str(selection_data.data)]
else:
uris = selection_data.get_uris()
if not uris:
# empty selection
return True
for uri in uris:
file_type, importer = pycam.Importers.detect_file_type(uri,
quiet=True)
if importer:
# looks like the file can be loaded
if self.load_model_file(filename=uri):
return True
if len(uris) > 1:
log.error("Failed to open any of the given models: %s" % \
str(uris))
else:
log.error("Failed to open the model: %s" % str(uris[0]))
return False
def append_to_queue(self, func, *args, **kwargs): def append_to_queue(self, func, *args, **kwargs):
# check if gui is currently active # check if gui is currently active
if self.gui_is_active: if self.gui_is_active:
...@@ -2759,16 +2796,7 @@ class ProjectGui: ...@@ -2759,16 +2796,7 @@ class ProjectGui:
def load_recent_model_file(self, widget): def load_recent_model_file(self, widget):
uri = widget.get_current_uri() uri = widget.get_current_uri()
if uri.startswith("file://"): self.load_model_file(filename=uri)
parsed = urllib.unquote(uri[len("file://"):])
self.load_model_file(filename=parsed)
else:
message = "Sorry - PyCAM can currently load only local files."
window = gtk.MessageDialog(self.window, type=gtk.MESSAGE_WARNING,
buttons=gtk.BUTTONS_OK, message_format=message)
window.set_title("Unsupported file location specified")
window.run()
window.destroy()
@gui_activity_guard @gui_activity_guard
@progress_activity_guard @progress_activity_guard
...@@ -2796,8 +2824,12 @@ class ProjectGui: ...@@ -2796,8 +2824,12 @@ class ProjectGui:
callback=self.update_progress_bar)): callback=self.update_progress_bar)):
self.set_model_filename(filename) self.set_model_filename(filename)
self.add_to_recent_file_list(filename) self.add_to_recent_file_list(filename)
return True
else:
return False
else: else:
log.error("Failed to detect filetype!") log.error("Failed to detect filetype!")
return False
@gui_activity_guard @gui_activity_guard
def export_emc_tools(self, widget=None, filename=None): def export_emc_tools(self, widget=None, filename=None):
...@@ -3744,7 +3776,7 @@ class ProjectGui: ...@@ -3744,7 +3776,7 @@ class ProjectGui:
response = overwrite_window.run() response = overwrite_window.run()
overwrite_window.destroy() overwrite_window.destroy()
done = (response == gtk.RESPONSE_YES) done = (response == gtk.RESPONSE_YES)
elif mode_load and not os.path.isfile(filename): elif mode_load and not check_uri_exists(filename):
not_found_window = gtk.MessageDialog(self.window, type=gtk.MESSAGE_ERROR, not_found_window = gtk.MessageDialog(self.window, type=gtk.MESSAGE_ERROR,
buttons=gtk.BUTTONS_OK, buttons=gtk.BUTTONS_OK,
message_format="This file does not exist. Please choose a different filename.") message_format="This file does not exist. Please choose a different filename.")
...@@ -3763,16 +3795,21 @@ class ProjectGui: ...@@ -3763,16 +3795,21 @@ class ProjectGui:
def add_to_recent_file_list(self, filename): def add_to_recent_file_list(self, filename):
# Add the item to the recent files list - if it already exists. # Add the item to the recent files list - if it already exists.
# Otherwise it will be added later after writing the file. # Otherwise it will be added later after writing the file.
if os.path.isfile(filename): if check_uri_exists(filename):
# skip this, if the recent manager is not available (e.g. GTK 2.12.1 on Windows) # skip this, if the recent manager is not available (e.g. GTK 2.12.1 on Windows)
if self.recent_manager: if self.recent_manager:
# Convert the local path to a URI filename style. This is if os.path.exists(filename):
# specifically necessary under Windows (due to backslashs). # This is a local file.
filename_url_local = urllib.pathname2url( # Convert the local path to a URI filename style. This is
os.path.abspath(filename)) # specifically necessary under Windows (due to backslashs).
# join the "file:" scheme with the url filename_url_local = urllib.pathname2url(
filename_url = urlparse.urlunparse(("file", None, os.path.abspath(filename))
filename_url_local, None, None, None)) # join the "file:" scheme with the url
filename_url = urlparse.urlunparse(("file", None,
filename_url_local, None, None, None))
else:
# this is a remote file - or it already contains "file://"
filename_url = filename
self.recent_manager.add_item(filename_url) self.recent_manager.add_item(filename_url)
# store the directory of the last loaded file # store the directory of the last loaded file
self.last_dirname = os.path.dirname(os.path.abspath(filename)) self.last_dirname = os.path.dirname(os.path.abspath(filename))
......
...@@ -25,6 +25,7 @@ from pycam.Geometry.Line import Line ...@@ -25,6 +25,7 @@ from pycam.Geometry.Line import Line
from pycam.Geometry.Point import Point from pycam.Geometry.Point import Point
from pycam.Geometry import get_points_of_arc from pycam.Geometry import get_points_of_arc
import pycam.Utils.log import pycam.Utils.log
from pycam.Utils import open_url
log = pycam.Utils.log.get_logger() log = pycam.Utils.log.get_logger()
...@@ -173,7 +174,7 @@ class CXFParser(object): ...@@ -173,7 +174,7 @@ class CXFParser(object):
def import_font(filename, callback=None): def import_font(filename, callback=None):
try: try:
infile = open(filename,"r") infile = open_url(filename)
except IOError, err_msg: except IOError, err_msg:
log.error("CXFImporter: Failed to read file (%s): %s" \ log.error("CXFImporter: Failed to read file (%s): %s" \
% (filename, err_msg)) % (filename, err_msg))
......
...@@ -25,6 +25,7 @@ from pycam.Geometry.Line import Line ...@@ -25,6 +25,7 @@ from pycam.Geometry.Line import Line
import pycam.Geometry.Model import pycam.Geometry.Model
import pycam.Geometry import pycam.Geometry
import pycam.Utils.log import pycam.Utils.log
from pycam.Utils import open_url
log = pycam.Utils.log.get_logger() log = pycam.Utils.log.get_logger()
...@@ -351,7 +352,7 @@ class DXFParser(object): ...@@ -351,7 +352,7 @@ class DXFParser(object):
def import_model(filename, program_locations=None, unit=None, def import_model(filename, program_locations=None, unit=None,
color_as_height=False, callback=None): color_as_height=False, callback=None):
try: try:
infile = open(filename,"rb") infile = open_url(filename)
except IOError, err_msg: except IOError, err_msg:
log.error("DXFImporter: Failed to read file (%s): %s" \ log.error("DXFImporter: Failed to read file (%s): %s" \
% (filename, err_msg)) % (filename, err_msg))
......
...@@ -22,6 +22,7 @@ along with PyCAM. If not, see <http://www.gnu.org/licenses/>. ...@@ -22,6 +22,7 @@ along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
from pycam.Importers.SVGImporter import convert_eps2dxf from pycam.Importers.SVGImporter import convert_eps2dxf
import pycam.Importers.DXFImporter import pycam.Importers.DXFImporter
from pycam.Utils import check_uri_exists, retrieve_uri
import tempfile import tempfile
import os import os
...@@ -29,9 +30,23 @@ log = pycam.Utils.log.get_logger() ...@@ -29,9 +30,23 @@ log = pycam.Utils.log.get_logger()
def import_model(filename, program_locations=None, unit="mm", callback=None): def import_model(filename, program_locations=None, unit="mm", callback=None):
if not os.path.isfile(filename): if not check_uri_exists(filename):
log.error("PSImporter: file (%s) does not exist" % filename) log.error("PSImporter: file (%s) does not exist" % filename)
return None return None
if not os.path.isfile(filename):
# non-local file - write it to a temporary file first
local_file = False
uri = filename
ps_file_handle, ps_file_name = tempfile.mkstemp(suffix=".ps")
os.close(ps_file_handle)
log.debug("Retrieving PS file for local access: %s -> %s" % \
(uri, ps_file_name))
if not retrieve_uri(uri, ps_file_name, callback=callback):
log.error("PSImporter: Failed to retrieve the PS model file: " + \
"%s -> %s" % (uri, ps_file_name))
filename = ps_file_name
else:
local_file = True
if program_locations and "pstoedit" in program_locations: if program_locations and "pstoedit" in program_locations:
pstoedit_path = program_locations["pstoedit"] pstoedit_path = program_locations["pstoedit"]
...@@ -51,6 +66,8 @@ def import_model(filename, program_locations=None, unit="mm", callback=None): ...@@ -51,6 +66,8 @@ def import_model(filename, program_locations=None, unit="mm", callback=None):
os.close(dxf_file_handle) os.close(dxf_file_handle)
success = convert_eps2dxf(filename, dxf_file_name, unit=unit, success = convert_eps2dxf(filename, dxf_file_name, unit=unit,
location=pstoedit_path) location=pstoedit_path)
if not local_file:
remove_temp_file(ps_file_name)
if not success: if not success:
result = None result = None
elif callback and callback(): elif callback and callback():
......
...@@ -27,10 +27,11 @@ from pycam.Geometry.PointKdtree import PointKdtree ...@@ -27,10 +27,11 @@ from pycam.Geometry.PointKdtree import PointKdtree
from pycam.Geometry.utils import epsilon from pycam.Geometry.utils import epsilon
from pycam.Geometry.Model import Model from pycam.Geometry.Model import Model
import pycam.Utils.log import pycam.Utils.log
from pycam.Utils import open_url
from struct import unpack from struct import unpack
import StringIO
import re import re
import os
log = pycam.Utils.log.get_logger() log = pycam.Utils.log.get_logger()
...@@ -61,7 +62,11 @@ def ImportModel(filename, use_kdtree=True, program_locations=None, unit=None, ...@@ -61,7 +62,11 @@ def ImportModel(filename, use_kdtree=True, program_locations=None, unit=None,
normal_conflict_warning_seen = False normal_conflict_warning_seen = False
try: try:
f = open(filename, "rb") url_file = open_url(filename)
# urllib.urlopen objects do not support "seek" - so we need to read the
# whole file at once. This is ugly - anyone with a better idea?
f = StringIO.StringIO(url_file.read())
url_file.close()
except IOError, err_msg: except IOError, err_msg:
log.error("STLImporter: Failed to read file (%s): %s" \ log.error("STLImporter: Failed to read file (%s): %s" \
% (filename, err_msg)) % (filename, err_msg))
...@@ -85,9 +90,9 @@ def ImportModel(filename, use_kdtree=True, program_locations=None, unit=None, ...@@ -85,9 +90,9 @@ def ImportModel(filename, use_kdtree=True, program_locations=None, unit=None,
numfacets = unpack("<I", f.read(4))[0] numfacets = unpack("<I", f.read(4))[0]
binary = False binary = False
if os.path.getsize(filename) == (84 + 50*numfacets): if f.len == (84 + 50*numfacets):
binary = True binary = True
elif header.find("solid")>=0 and header.find("facet")>=0: elif header.find("solid") >= 0 and header.find("facet") >= 0:
binary = False binary = False
f.seek(0) f.seek(0)
else: else:
......
...@@ -21,6 +21,7 @@ along with PyCAM. If not, see <http://www.gnu.org/licenses/>. ...@@ -21,6 +21,7 @@ along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
""" """
import pycam.Importers.DXFImporter import pycam.Importers.DXFImporter
from pycam.Utils import check_uri_exists, retrieve_uri
import tempfile import tempfile
import subprocess import subprocess
import os import os
...@@ -81,9 +82,23 @@ def convert_eps2dxf(eps_filename, dxf_filename, location=None, unit="mm"): ...@@ -81,9 +82,23 @@ def convert_eps2dxf(eps_filename, dxf_filename, location=None, unit="mm"):
return False return False
def import_model(filename, program_locations=None, unit="mm", callback=None): def import_model(filename, program_locations=None, unit="mm", callback=None):
if not os.path.isfile(filename): if not check_uri_exists(filename):
log.error("SVGImporter: file (%s) does not exist" % filename) log.error("SVGImporter: file (%s) does not exist" % filename)
return None return None
if not os.path.isfile(filename):
# non-local file - write it to a temporary file first
local_file = False
uri = filename
svg_file_handle, svg_file_name = tempfile.mkstemp(suffix=".svg")
os.close(svg_file_handle)
log.debug("Retrieving SVG file for local access: %s -> %s" % \
(uri, svg_file_name))
if not retrieve_uri(uri, svg_file_name, callback=callback):
log.error("SVGImporter: Failed to retrieve the SVG model file: " + \
"%s -> %s" % (uri, svg_file_name))
filename = svg_file_name
else:
local_file = True
if program_locations and "inkscape" in program_locations: if program_locations and "inkscape" in program_locations:
inkscape_path = program_locations["inkscape"] inkscape_path = program_locations["inkscape"]
...@@ -112,6 +127,8 @@ def import_model(filename, program_locations=None, unit="mm", callback=None): ...@@ -112,6 +127,8 @@ def import_model(filename, program_locations=None, unit="mm", callback=None):
log.warn("SVGImporter: failed to remove temporary file " \ log.warn("SVGImporter: failed to remove temporary file " \
+ "(%s): %s" % (filename, err_msg)) + "(%s): %s" % (filename, err_msg))
# remove the temporary file # remove the temporary file
if not local_file:
remove_temp_file(svg_file_name)
if not success: if not success:
remove_temp_file(eps_file_name) remove_temp_file(eps_file_name)
return None return None
......
...@@ -23,6 +23,7 @@ along with PyCAM. If not, see <http://www.gnu.org/licenses/>. ...@@ -23,6 +23,7 @@ along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
import pycam.Gui.Settings import pycam.Gui.Settings
import pycam.Gui.Project import pycam.Gui.Project
import pycam.Utils.log import pycam.Utils.log
from pycam.Utils import open_url
import re import re
import os import os
import sys import sys
...@@ -58,7 +59,7 @@ def parse_toolpath_settings(filename): ...@@ -58,7 +59,7 @@ def parse_toolpath_settings(filename):
else: else:
# open the file # open the file
try: try:
infile = open(filename,"r") infile = open_url(filename)
except IOError, err_msg: except IOError, err_msg:
log.warn("ToolpathSettingsParser: Failed to read file (%s): %s" % \ log.warn("ToolpathSettingsParser: Failed to read file (%s): %s" % \
(filename, err_msg)) (filename, err_msg))
......
...@@ -34,25 +34,23 @@ import os ...@@ -34,25 +34,23 @@ import os
log = pycam.Utils.log.get_logger() log = pycam.Utils.log.get_logger()
def detect_file_type(filename): def detect_file_type(filename, quiet=False):
failure = (None, None) failure = (None, None)
if not os.path.isfile(filename): # check all listed importers
return failure # TODO: this should be done by evaluating the header of the file
if filename.lower().endswith(".stl"):
return ("stl", pycam.Importers.STLImporter.ImportModel)
elif filename.lower().endswith(".dxf"):
return ("dxf", pycam.Importers.DXFImporter.import_model)
elif filename.lower().endswith(".svg"):
return ("svg", pycam.Importers.SVGImporter.import_model)
elif filename.lower().endswith(".eps") \
or filename.lower().endswith(".ps"):
return ("ps", pycam.Importers.PSImporter.import_model)
else: else:
# check all listed importers if not quiet:
# TODO: this should be done by evaluating the header of the file
if filename.lower().endswith(".stl"):
return ("stl", pycam.Importers.STLImporter.ImportModel)
elif filename.lower().endswith(".dxf"):
return ("dxf", pycam.Importers.DXFImporter.import_model)
elif filename.lower().endswith(".svg"):
return ("svg", pycam.Importers.SVGImporter.import_model)
elif filename.lower().endswith(".eps") \
or filename.lower().endswith(".ps"):
return ("ps", pycam.Importers.PSImporter.import_model)
else:
log.error(("Importers: Failed to detect the model type of '%s'. " \ log.error(("Importers: Failed to detect the model type of '%s'. " \
+ "Is the file extension (stl/dxf/svg/eps/ps) correct?") \ + "Is the file extension (stl/dxf/svg/eps/ps) correct?") \
% filename) % filename)
return failure return failure
...@@ -27,6 +27,7 @@ __all__ = ["iterators", "polynomials", "ProgressCounter", "threading", ...@@ -27,6 +27,7 @@ __all__ = ["iterators", "polynomials", "ProgressCounter", "threading",
import sys import sys
import os import os
import socket import socket
import urllib
# this is imported below on demand # this is imported below on demand
#import win32com #import win32com
#import win32api #import win32api
...@@ -55,6 +56,29 @@ def get_platform(): ...@@ -55,6 +56,29 @@ def get_platform():
return PLATFORM_UNKNOWN return PLATFORM_UNKNOWN
def open_url(uri):
return urllib.urlopen(uri)
def check_uri_exists(uri):
try:
handle = open_url(uri)
handle.close()
return True
except IOError:
return False
def retrieve_uri(uri, filename, callback=None):
if callback:
download_callback = lambda current_blocks, block_size, num_of_blocks: \
callback()
else:
download_callback = None
try:
urllib.urlretrieve(uri, filename, download_callback)
return True
except IOError:
return False
def get_all_ips(): def get_all_ips():
""" try to get all IPs of this machine """ try to get all IPs of this machine
...@@ -109,7 +133,7 @@ def get_external_program_location(key): ...@@ -109,7 +133,7 @@ def get_external_program_location(key):
for one_dir in path_env.split(os.pathsep): for one_dir in path_env.split(os.pathsep):
for basename in potential_names: for basename in potential_names:
location = os.path.join(one_dir, basename) location = os.path.join(one_dir, basename)
if os.path.isfile(location): if check_uri_exists(location):
return location return location
# do a manual scan in the programs directory (only for windows) # do a manual scan in the programs directory (only for windows)
try: try:
...@@ -123,7 +147,7 @@ def get_external_program_location(key): ...@@ -123,7 +147,7 @@ def get_external_program_location(key):
for sub_dir in windows_program_directories[key]: for sub_dir in windows_program_directories[key]:
for basename in potential_names: for basename in potential_names:
location = os.path.join(program_dir, sub_dir, basename) location = os.path.join(program_dir, sub_dir, basename)
if os.path.isfile(location): if check_uri_exists(location):
return location return location
# nothing found # nothing found
return None return None
......
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