def combine_triangles(t1, t2): unique_vertices = [] shared_vertices = [] for point in t1.get_points(): for point2 in t2.get_points(): if point == point2: shared_vertices.append(point) break else: unique_vertices.append(point) if len(shared_vertices) != 2: return None for point in t2.get_points(): for point2 in shared_vertices: if point == point2: break else: unique_vertices.append(point) if len(unique_vertices) != 2: log.error("Invalid number of vertices: %s" % unique_vertices) return None if abs(unique_vertices[0].sub(unique_vertices[1]).norm - \ shared_vertices[0].sub(shared_vertices[1]).norm) < epsilon: try: return Rectangle(unique_vertices[0], unique_vertices[1], shared_vertices[0], shared_vertices[1], normal=t1.normal) except ValueError: log.warn("Triangles not combined: %s, %s" % (unique_vertices, shared_vertices)) return None else: return None
def detect_file_type(filename, quiet=False): import pycam.Importers.DXFImporter import pycam.Importers.PSImporter import pycam.Importers.STLImporter import pycam.Importers.SVGImporter # also accept URI input uri = pycam.Utils.URIHandler(filename) filename = uri.get_path() failure = (None, None) # check all listed importers # 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: if not quiet: log.error( "Importers: Failed to detect the model type of '%s'. Is the file extension " "(stl/dxf/svg/eps/ps) correct?", filename) return failure
def combine_rectangles(r1, r2): shared_vertices = [] shared_vertices2 = [] for point in r1.get_points(): for point2 in r2.get_points(): if point == point2: shared_vertices.append(point) shared_vertices2.append(point2) break if len(shared_vertices) != 2: return None # check if the two points form an edge (and not a diagonal line) corners = [] for rectangle, vertices in ((r1, shared_vertices), (r2, shared_vertices2)): # turn the tuple into a list (".index" was introduced in Python 2.6) i1 = list(rectangle.get_points()).index(vertices[0]) i2 = list(rectangle.get_points()).index(vertices[1]) if i1 + i2 % 2 == 0: # shared vertices are at opposite corners return None # collect all non-shared vertices corners.extend([p for p in rectangle.get_points() if not p in vertices]) if len(corners) != 4: log.error("Unexpected corner count: %s / %s / %s" % (r1, r2, corners)) return None try: return Rectangle(corners[0], corners[1], corners[2], corners[3], normal=r1.normal) except ValueError: log.error("No valid rectangle found: %s" % corners) return None
def detect_file_type(filename, quiet=False): # also accept URI input uri = pycam.Utils.URIHandler(filename) filename = uri.get_path() failure = (None, None) # check all listed importers # 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: if not quiet: log.error( ( "Importers: Failed to detect the model type of '%s'. " + "Is the file extension (stl/dxf/svg/eps/ps) correct?" ) % filename ) return failure
def check_header(self): # TODO: this function is not used? # we expect "0" in the first line and "SECTION" in the second one key, value = self._read_key_value() if (key != self.KEYS["MARKER"]) or (value and (value != "SECTION")): log.error("DXFImporter: DXF file header not recognized") return None
def handle_data_drop(self, widget, drag_context, x, y, selection_data, info, timestamp): if info != 0: uris = [str(selection_data.data)] elif pycam.Utils.get_platform() == pycam.Utils.PLATFORM_WINDOWS: uris = selection_data.data.splitlines() else: uris = selection_data.get_uris() if not uris: # empty selection return True for uri in uris: if not uri or (uri == chr(0)): continue uri = pycam.Utils.URIHandler(uri) 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 load_model_file(self, widget=None, filename=None, store_filename=True): if callable(filename): filename = filename() if not filename: filename = self.settings.get("get_filename_func")( "Loading model ...", mode_load=True, type_filter=FILTER_MODEL) if filename: file_type, importer = pycam.Importers.detect_file_type(filename) if file_type and callable(importer): progress = self.settings.get("progress") progress.update(text="Loading model ...") # "cancel" is not allowed progress.disable_cancel() if self.load_model( importer(filename, program_locations=get_all_program_locations( self.settings), unit=self.settings.get("unit"), fonts_cache=self.settings.get("fonts"), callback=progress.update)): if store_filename: self.set_model_filename(filename) self.add_to_recent_file_list(filename) result = True else: result = False progress.finish() return result else: log.error("Failed to detect filetype!") return False
def save_task_settings_file(self, widget=None, filename=None): if callable(filename): filename = filename() if not isinstance(filename, (basestring, pycam.Utils.URIHandler)): # we open a dialog filename = self.settings.get("get_filename_func")( "Save settings to ...", mode_load=False, type_filter=FILTER_CONFIG, filename_templates=(self.last_task_settings_uri, self.last_model_uri)) if filename: self.last_task_settings_uri = pycam.Utils.URIHandler(filename) # no filename given -> exit if not filename: return settings = self.settings.dump_state() try: out_file = open(filename, "w") out_file.write(settings) out_file.close() log.info("Task settings written to %s" % filename) self.add_to_recent_file_list(filename) except IOError: log.error("Failed to save settings file")
def transform_by_matrix(self, matrix, transformed_list=None, callback=None): if transformed_list is None: transformed_list = [] # Prevent any kind of loops or double transformations (e.g. Points in # multiple containers (Line, Triangle, ...). # Use the 'id' builtin to prevent expensive object comparions. for item in self.next(): if isinstance(item, TransformableContainer): item.transform_by_matrix(matrix, transformed_list,callback=callback) elif not id(item) in transformed_list: # non-TransformableContainer do not care to update the # 'transformed_list'. Thus we need to do it. #transformed_list.append(id(item)) # Don't transmit the 'transformed_list' if the object is # not a TransformableContainer. It is not necessary and it # is hard to understand on the lowest level (e.g. Point). if isinstance(item, str): theval = getattr(self, item) if isinstance(theval, tuple): setattr(self, item, ptransform_by_matrix(theval, matrix)) elif isinstance(theval, list): setattr(self, item, [ptransform_by_matrix(x, matrix) for x in theval]) elif isinstance(item, tuple): log.error("ERROR!! A tuple (Point, Vector) made it into base transform_by_matrix without a back reference. Point/Vector remains unchanged.") else: item.transform_by_matrix(matrix, callback=callback) # run the callback - e.g. for a progress counter if callback and callback(): # user requesteded abort break self.reset_cache()
def ImportModel(filename, use_kdtree=True, callback=None, **kwargs): global vertices, edges, kdtree vertices = 0 edges = 0 kdtree = None normal_conflict_warning_seen = False if hasattr(filename, "read"): # make sure that the input stream can seek and has ".len" f = StringIO.StringIO(filename.read()) # useful for later error messages filename = "input stream" else: try: url_file = pycam.Utils.URIHandler(filename).open() # 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()) # TODO: the above ".read" may be incomplete - this is ugly # see http://patrakov.blogspot.com/2011/03/case-of-non-raised-exception.html # and http://stackoverflow.com/questions/1824069/urllib2-not-retrieving-entire-http-response url_file.close() except IOError, err_msg: log.error("STLImporter: Failed to read file (%s): %s" \ % (filename, err_msg)) return None
def load_model_file(self, widget=None, filename=None, store_filename=True): if callable(filename): filename = filename() if not filename: filename = self.settings.get("get_filename_func")("Loading model ...", mode_load=True, type_filter=FILTER_MODEL) if filename: file_type, importer = pycam.Importers.detect_file_type(filename) if file_type and callable(importer): progress = self.settings.get("progress") progress.update(text="Loading model ...") # "cancel" is not allowed progress.disable_cancel() if self.load_model(importer(filename, program_locations=get_all_program_locations(self.settings), unit=self.settings.get("unit"), fonts_cache=self.settings.get("fonts"), callback=progress.update)): if store_filename: self.set_model_filename(filename) self.add_to_recent_file_list(filename) result = True else: result = False progress.finish() return result else: log.error("Failed to detect filetype!") return False
def combine_rectangles(r1, r2): shared_vertices = [] shared_vertices2 = [] for point in r1.get_points(): for point2 in r2.get_points(): if point == point2: shared_vertices.append(point) shared_vertices2.append(point2) break if len(shared_vertices) != 2: return None # check if the two points form an edge (and not a diagonal line) corners = [] for rectangle, vertices in ((r1, shared_vertices), (r2, shared_vertices2)): # turn the tuple into a list (".index" was introduced in Python 2.6) i1 = list(rectangle.get_points()).index(vertices[0]) i2 = list(rectangle.get_points()).index(vertices[1]) if i1 + i2 % 2 == 0: # shared vertices are at opposite corners return None # collect all non-shared vertices corners.extend([p for p in rectangle.get_points() if p not in vertices]) if len(corners) != 4: log.error("Unexpected corner count: %s / %s / %s", r1, r2, corners) return None try: return Rectangle(corners[0], corners[1], corners[2], corners[3], normal=r1.normal) except ValueError: log.error("No valid rectangle found: %s", corners) return None
def import_font(filename, callback=None): try: infile = pycam.Utils.URIHandler(filename).open() except IOError, err_msg: log.error("CXFImporter: Failed to read file (%s): %s" \ % (filename, err_msg)) return None
def load_workspace_from_file(self, filename, remember_uri=True, default_content=None): if remember_uri: self.last_workspace_uri = pycam.Utils.URIHandler(filename) self.settings.get("set_last_filename")(filename) log.info("Loading workspace from file: %s", filename) try: with open_file_context(filename, "r", True) as in_file: content = in_file.read() except OSError as exc: if default_content: content = default_content else: log.error("Failed to read workspace file (%s): %s", filename, exc) return False try: return self.load_workspace_from_description(content) except PycamBaseException as exc: log.warning( "Failed to load workspace description from file (%s): %s", filename, exc) if default_content: log.info("Falling back to default workspace due to load error") self.load_workspace_from_description(default_content) return False
def handle_data_drop(self, widget, drag_context, x, y, selection_data, info, timestamp): if info != 0: uris = [str(selection_data.data)] elif pycam.Utils.get_platform() == pycam.Utils.OSPlatform.WINDOWS: uris = selection_data.data.splitlines() else: uris = selection_data.get_uris() if not uris: # empty selection return True for uri in uris: if not uri or (uri == chr(0)): continue detected_filetype = pycam.Importers.detect_file_type(uri, quiet=True) if detected_filetype: # looks like the file can be loaded if self.load_model_file(filename=detected_filetype.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 execute(parser, args, pycam): # try to change the process name pycam.Utils.setproctitle("pycam") if args.trace: log.setLevel(logging.DEBUG // 2) elif args.debug: log.setLevel(logging.DEBUG) elif args.quiet: log.setLevel(logging.WARNING) # disable the progress bar args.progress = "none" # silence all warnings warnings.filterwarnings("ignore") else: log.setLevel(logging.INFO) # check if server-auth-key is given -> this is mandatory for server mode if (args.enable_server or args.start_server) and not args.server_authkey: parser.error( "You need to supply a shared secret for server mode. This is supposed to prevent you " "from exposing your host to remote access without authentication.\nPlease add the " "'--server-auth-key' argument followed by a shared secret password." ) return EXIT_CODES["server_without_password"] # initialize multiprocessing try: if args.server_authkey is None: server_auth_key = None else: server_auth_key = args.server_authkey.encode("utf-8") if args.start_server: pycam.Utils.threading.init_threading( args.parallel_processes, remote=args.remote_server, run_server=True, server_credentials=server_auth_key) pycam.Utils.threading.cleanup() return EXIT_CODES["ok"] else: pycam.Utils.threading.init_threading( args.parallel_processes, enable_server=args.enable_server, remote=args.remote_server, server_credentials=server_auth_key) except socket.error as err_msg: log.error("Failed to connect to remote server: %s", err_msg) return EXIT_CODES["connection_error"] except AuthenticationError as err_msg: log.error("The remote server rejected your authentication key: %s", err_msg) return EXIT_CODES["connection_error"] try: show_gui() except InitializationError as exc: EmergencyDialog("PyCAM startup failure", str(exc)) return EXIT_CODES["requirements"]
def requirements_details_gtk(): result = {} try: import_gtk_carefully() result["gtk"] = True except ImportError, err_msg: log.error("Failed to import GTK: %s" % str(err_msg)) result["gtk"] = False
def load_from_string(self, config_text): input_text = StringIO.StringIO(config_text) try: self.reset(input_text) except ConfigParser.ParsingError, err_msg: log.error("Settings: Failed to parse config data: %s" % \ str(err_msg)) return False
def register_ui_section(self, section, add_action, clear_action): if section not in self.ui_sections: self.ui_sections[section] = UISection(None, None, []) else: log.error("Trying to register a ui section twice: %s", section) self.ui_sections[section] = UISection( add_action, clear_action, self.ui_sections[section].widgets) self._rebuild_ui_section(section)
def load_file(self, filename): uri = pycam.Utils.URIHandler(filename) try: handle = uri.open() content = handle.read() except IOError, err_msg: log.error("Settings: Failed to read config file '%s': %s" \ % (uri, err_msg)) return False
def _locate_external_program(self, widget=None, key=None): # the button was just activated location = get_external_program_location(key) if not location: log.error("Failed to locate the external program '%s'. Please install the program and " "try again.%sOr maybe you need to specify the location manually.", key, os.linesep) else: # store the new setting self.settings.set("external_program_%s" % key, location)
def _locate_external_program(self, widget=None, key=None): # the button was just activated location = get_external_program_location(key) if not location: log.error("Failed to locate the external program '%s'. " % key \ + "Please install the program and try again." \ + os.linesep \ + "Or maybe you need to specify the location manually.") else: # store the new setting self.settings.set("external_program_%s" % key, location)
def import_model(filename, color_as_height=False, fonts_cache=None, callback=None, **kwargs): if hasattr(filename, "read"): infile = filename else: try: infile = pycam.Utils.URIHandler(filename).open() except IOError, err_msg: log.error("DXFImporter: Failed to read file (%s): %s" \ % (filename, err_msg)) return None
def get_data_file_location(filename, silent=False): for base_dir in DATA_BASE_DIRS: test_path = os.path.join(base_dir, filename) if os.path.exists(test_path): return test_path if not silent: log.error( "Failed to locate a resource file (%s) in %s! " "You can extend the search path by setting the environment variable '%s'.", filename, DATA_BASE_DIRS, str(DATA_DIR_ENVIRON_KEY)) return None
def write_to_file(self, filename, tools=None, processes=None, bounds=None, tasks=None): uri = pycam.Utils.URIHandler(filename) text = self.get_config_text(tools, processes, bounds, tasks) try: handle = open(uri.get_local_path(), "w") handle.write(text) handle.close() except IOError, err_msg: log.error("Settings: Failed to write configuration to file " \ + "(%s): %s" % (filename, err_msg)) return False
def save_workspace_to_file(self, filename, remember_uri=True): from pycam.Flow.parser import dump_yaml if remember_uri: self.last_workspace_uri = pycam.Utils.URIHandler(filename) self.settings.emit_event("notify-file-opened", filename) log.info("Storing workspace in file: %s", filename) try: with open_file_context(filename, "w", True) as out_file: dump_yaml(target=out_file) except OSError as exc: log.error("Failed to store workspace in file '%s': %s", filename, exc)
def gui_activity_guard_wrapper(self, *args, **kwargs): if self.gui_is_active: return self.gui_is_active = True try: result = func(self, *args, **kwargs) except Exception: # Catch possible exceptions (except system-exit ones) and # report them. log.error(pycam.Utils.get_exception_report()) result = None self.gui_is_active = False return result
def get_output_handler(destination): if destination == "-": handler = sys.stdout closer = lambda: None else: # support paths with a tilde (~) destination = os.path.expanduser(destination) try: handler = open(destination, "w") except IOError, err_msg: log.error("Failed to open output file (%s) for writing: %s" \ % (destination, err_msg)) return None, None closer = handler.close
def get_data_file_location(filename, silent=False, priority_directories=None): if priority_directories is None: scan_dirs = DATA_BASE_DIRS else: scan_dirs = tuple(priority_directories) + tuple(DATA_BASE_DIRS) for base_dir in scan_dirs: test_path = os.path.join(base_dir, filename) if os.path.exists(test_path): return test_path if not silent: log.error("Failed to locate a resource file (%s) in %s! " "You can extend the search path by setting the environment variable '%s'.", filename, DATA_BASE_DIRS, str(DATA_DIR_ENVIRON_KEY)) return None
def get_data_file_location(filename, silent=False): for base_dir in DATA_BASE_DIRS: test_path = os.path.join(base_dir, filename) if os.path.exists(test_path): return test_path else: if not silent: lines = [] lines.append("Failed to locate a resource file (%s) in %s!" \ % (filename, DATA_BASE_DIRS)) lines.append("You can extend the search path by setting the " \ + "environment variable '%s'." % str(DATA_DIR_ENVIRON_KEY)) log.error(os.linesep.join(lines)) return None
def get_data_file_location(filename, silent=False): for base_dir in DATA_BASE_DIRS: test_path = os.path.join(base_dir, filename) if os.path.exists(test_path): return test_path else: if not silent: lines = [] lines.append("Failed to locate a resource file (%s) in %s!" % (filename, DATA_BASE_DIRS)) lines.append( "You can extend the search path by setting the " + "environment variable '%s'." % str(DATA_DIR_ENVIRON_KEY) ) log.error(os.linesep.join(lines)) return None
def load_file(self, filename): uri = pycam.Utils.URIHandler(filename) try: handle = uri.open() content = handle.read() except IOError as err_msg: log.error("Settings: Failed to read config file '%s': %s", uri, err_msg) return False try: self.reset(content) except ConfigParser.ParsingError as err_msg: log.error("Settings: Failed to parse config file '%s': %s", uri, err_msg) return False return True
def gui_activity_guard_wrapper(self, *args, **kwargs): if self.gui_is_active: return self.gui_is_active = True try: result = func(self, *args, **kwargs) except Exception: # Catch possible exceptions (except system-exit ones) and # report them. log.error(pycam.Utils.get_exception_report()) result = None self.gui_is_active = False while self._batch_queue: batch_func, batch_args, batch_kwargs = self._batch_queue[0] del self._batch_queue[0] batch_func(*batch_args, **batch_kwargs) return result
def import_font(filename, callback=None): try: infile = pycam.Utils.URIHandler(filename).open() except IOError as err_msg: log.error("CXFImporter: Failed to read file (%s): %s", filename, err_msg) return None try: parsed_font = CXFParser(infile, callback=callback) except _CXFParseError as err_msg: log.warn("CFXImporter: Skipped font defintion file '%s'. Reason: %s.", filename, err_msg) return None charset = Charset(**parsed_font.meta) for key, value in parsed_font.letters.iteritems(): charset.add_character(key, value) log.info("CXFImporter: Imported CXF font from '%s': %d letters", filename, len(parsed_font.letters)) infile.close() return charset
def transform_by_matrix(self, matrix, transformed_list=None, callback=None): if transformed_list is None: transformed_list = [] # Prevent any kind of loops or double transformations (e.g. Points in # multiple containers (Line, Triangle, ...). # Use the 'id' builtin to prevent expensive object comparions. for item in self.next(): if isinstance(item, TransformableContainer): item.transform_by_matrix(matrix, transformed_list, callback=callback) elif not id(item) in transformed_list: # non-TransformableContainer do not care to update the # 'transformed_list'. Thus we need to do it. #transformed_list.append(id(item)) # Don't transmit the 'transformed_list' if the object is # not a TransformableContainer. It is not necessary and it # is hard to understand on the lowest level (e.g. Point). if isinstance(item, str): theval = getattr(self, item) if isinstance(theval, tuple): setattr(self, item, ptransform_by_matrix(theval, matrix)) elif isinstance(theval, list): setattr( self, item, [ptransform_by_matrix(x, matrix) for x in theval]) elif isinstance(item, tuple): log.error( "ERROR!! A tuple (Point, Vector) made it into base transform_by_matrix without a back reference. Point/Vector remains unchanged." ) else: item.transform_by_matrix(matrix, callback=callback) # run the callback - e.g. for a progress counter if callback and callback(): # user requesteded abort break self.reset_cache()
def save_task_settings_file(self, widget=None, filename=None): if callable(filename): filename = filename() if not isinstance(filename, (basestring, pycam.Utils.URIHandler)): # we open a dialog filename = self.settings.get("get_filename_func")("Save settings to ...", mode_load=False, type_filter=FILTER_CONFIG, filename_templates=(self.last_task_settings_uri, self.last_model_uri)) if filename: self.last_task_settings_uri = pycam.Utils.URIHandler(filename) # no filename given -> exit if not filename: return settings = self.settings.dump_state() try: out_file = open(filename, "w") out_file.write(settings) out_file.close() log.info("Task settings written to %s" % filename) self.add_to_recent_file_list(filename) except IOError: log.error("Failed to save settings file")
def load_preferences(self): """ load all settings (see Preferences window) from a file in the user's home directory """ config = ConfigParser() try: with pycam.Gui.Settings.open_preferences_file() as in_file: config.read_file(in_file) except FileNotFoundError as exc: log.info( "No preferences file found (%s). Starting with default preferences.", exc) except OSError as exc: log.error("Failed to read preferences: %s", exc) return # report any ignored (obsolete) preference keys present in the file for item, value in config.items("DEFAULT"): if item not in PREFERENCES_DEFAULTS.keys(): log.warn("Skipping obsolete preference item: %s", str(item)) for item in PREFERENCES_DEFAULTS: if not config.has_option("DEFAULT", item): # a new preference setting is missing in the (old) file continue value_json = config.get("DEFAULT", item) try: value = json.loads(value_json) except ValueError as exc: log.warning("Failed to parse configuration setting '%s': %s", item, exc) value = PREFERENCES_DEFAULTS[item] wanted_type = type(PREFERENCES_DEFAULTS[item]) if wanted_type is float: # int is accepted for floats, too wanted_type = (float, int) if not isinstance(value, wanted_type): log.warning( "Falling back to default configuration setting for '%s' due to " "an invalid value type being parsed: %s != %s", item, type(value), wanted_type) value = PREFERENCES_DEFAULTS[item] self.settings.set(item, value)
def ImportModel(filename, use_kdtree=True, callback=None, **kwargs): global vertices, edges, kdtree vertices = 0 edges = 0 kdtree = None normal_conflict_warning_seen = False if hasattr(filename, "read"): f = filename # useful for later error messages filename = "input stream" else: try: url_file = pycam.Utils.URIHandler(filename).open() # 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: log.error("STLImporter: Failed to read file (%s): %s" \ % (filename, err_msg)) return None
def execute(parser, opts, args, pycam): # try to change the process name pycam.Utils.setproctitle("pycam") if len(args) > 0: inputfile = pycam.Utils.URIHandler(args[0]) else: inputfile = None if opts.debug: log.setLevel(logging.DEBUG) elif opts.quiet: log.setLevel(logging.WARNING) # disable the progress bar opts.progress = "none" # silence all warnings warnings.filterwarnings("ignore") else: # silence gtk warnings try: import gtk warnings.filterwarnings("ignore", category=gtk.Warning) except ImportError: pass # show version and exit if opts.show_version: if opts.quiet: # print only the bare version number print VERSION else: text = '''PyCAM %s Copyright (C) 2008-2010 Lode Leroy Copyright (C) 2010-2011 Lars Kruse License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.''' % VERSION print text return EXIT_CODES["ok"] if not opts.disable_psyco: try: import psyco psyco.full() log.info("Psyco enabled") except ImportError: log.info("Psyco is not available (performance will probably " \ + "suffer slightly)") else: log.info("Psyco was disabled via the commandline") # check if server-auth-key is given -> this is mandatory for server mode if (opts.enable_server or opts.start_server) and not opts.server_authkey: parser.error("You need to supply a shared secret for server mode. " \ + "This is supposed to prevent you from exposing your host " \ + "to remote access without authentication.\n" \ + "Please add the '--server-auth-key' argument followed by " \ + "a shared secret password.") return EXIT_CODES["server_without_password"] # initialize multiprocessing try: if opts.start_server: pycam.Utils.threading.init_threading( opts.parallel_processes, remote=opts.remote_server, run_server=True, server_credentials=opts.server_authkey) pycam.Utils.threading.cleanup() return EXIT_CODES["ok"] else: pycam.Utils.threading.init_threading( opts.parallel_processes, enable_server=opts.enable_server, remote=opts.remote_server, server_credentials=opts.server_authkey) except socket.error, err_msg: log.error("Failed to connect to remote server: %s" % err_msg) return EXIT_CODES["connection_error"]
try: if opts.start_server: pycam.Utils.threading.init_threading(opts.parallel_processes, remote=opts.remote_server, run_server=True, server_credentials=opts.server_authkey) pycam.Utils.threading.cleanup() return EXIT_CODES["ok"] else: pycam.Utils.threading.init_threading(opts.parallel_processes, enable_server=opts.enable_server, remote=opts.remote_server, server_credentials=opts.server_authkey) except socket.error, err_msg: log.error("Failed to connect to remote server: %s" % err_msg) return EXIT_CODES["connection_error"] except multiprocessing.AuthenticationError, err_msg: log.error("The remote server rejected your authentication key: %s" \ % err_msg) return EXIT_CODES["connection_error"] # initialize the progress bar progress_styles = {"none": pycam.Gui.Console.ConsoleProgressBar.STYLE_NONE, "text": pycam.Gui.Console.ConsoleProgressBar.STYLE_TEXT, "bar": pycam.Gui.Console.ConsoleProgressBar.STYLE_BAR, "dot": pycam.Gui.Console.ConsoleProgressBar.STYLE_DOT, } progress_bar = pycam.Gui.Console.ConsoleProgressBar(sys.stdout, progress_styles[opts.progress]) if opts.config_file: opts.config_file = os.path.expanduser(opts.config_file) if not opts.export_gcode and not opts.export_task_config:
def execute(parser, opts, args, pycam): # try to change the process name pycam.Utils.setproctitle("pycam") if len(args) > 0: inputfile = pycam.Utils.URIHandler(args[0]) else: inputfile = None if opts.debug: log.setLevel(logging.DEBUG) elif opts.quiet: log.setLevel(logging.WARNING) # disable the progress bar opts.progress = "none" # silence all warnings warnings.filterwarnings("ignore") else: # silence gtk warnings try: import gtk warnings.filterwarnings("ignore", category=gtk.Warning) except ImportError: pass # show version and exit if opts.show_version: if opts.quiet: # print only the bare version number print VERSION else: text = '''PyCAM %s Copyright (C) 2008-2010 Lode Leroy Copyright (C) 2010-2011 Lars Kruse License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.''' % VERSION print text return EXIT_CODES["ok"] if not opts.disable_psyco: try: import psyco psyco.full() log.info("Psyco enabled") except ImportError: log.info("Psyco is not available (performance will probably " \ + "suffer slightly)") else: log.info("Psyco was disabled via the commandline") # check if server-auth-key is given -> this is mandatory for server mode if (opts.enable_server or opts.start_server) and not opts.server_authkey: parser.error("You need to supply a shared secret for server mode. " \ + "This is supposed to prevent you from exposing your host " \ + "to remote access without authentication.\n" \ + "Please add the '--server-auth-key' argument followed by " \ + "a shared secret password.") return EXIT_CODES["server_without_password"] # initialize multiprocessing try: if opts.start_server: pycam.Utils.threading.init_threading(opts.parallel_processes, remote=opts.remote_server, run_server=True, server_credentials=opts.server_authkey) pycam.Utils.threading.cleanup() return EXIT_CODES["ok"] else: pycam.Utils.threading.init_threading(opts.parallel_processes, enable_server=opts.enable_server, remote=opts.remote_server, server_credentials=opts.server_authkey) except socket.error, err_msg: log.error("Failed to connect to remote server: %s" % err_msg) return EXIT_CODES["connection_error"]
def __init__( self, gui, settings, notify_destroy=None, accel_group=None, item_buttons=None, context_menu_actions=None ): # assume, that initialization will fail self.gui = gui self.window = self.gui.get_object("view3dwindow") if not accel_group is None: self.window.add_accel_group(accel_group) self.initialized = False self.busy = False self.settings = settings self.is_visible = False # check if the 3D view is available if GL_ENABLED: self.enabled = True else: log.error( "Failed to initialize the interactive 3D model view." + "\nPlease install 'python-gtkglext1' to enable it." ) self.enabled = False return self.mouse = { "start_pos": None, "button": None, "event_timestamp": 0, "last_timestamp": 0, "pressed_pos": None, "pressed_timestamp": 0, "pressed_button": None, } self.notify_destroy_func = notify_destroy self.window.connect("delete-event", self.destroy) self.window.set_default_size(560, 400) self._position = self.gui.get_object("ProjectWindow").get_position() self._position = (self._position[0] + 100, self._position[1] + 100) self.container = self.gui.get_object("view3dbox") self.gui.get_object("Reset View").connect("clicked", self.rotate_view, VIEWS["reset"]) self.gui.get_object("Left View").connect("clicked", self.rotate_view, VIEWS["left"]) self.gui.get_object("Right View").connect("clicked", self.rotate_view, VIEWS["right"]) self.gui.get_object("Front View").connect("clicked", self.rotate_view, VIEWS["front"]) self.gui.get_object("Back View").connect("clicked", self.rotate_view, VIEWS["back"]) self.gui.get_object("Top View").connect("clicked", self.rotate_view, VIEWS["top"]) self.gui.get_object("Bottom View").connect("clicked", self.rotate_view, VIEWS["bottom"]) # key binding self.window.connect("key-press-event", self.key_handler) # OpenGL stuff glconfig = gtk.gdkgl.Config(mode=gtk.gdkgl.MODE_RGBA | gtk.gdkgl.MODE_DEPTH | gtk.gdkgl.MODE_DOUBLE) self.area = gtk.gtkgl.DrawingArea(glconfig) # first run; might also be important when doing other fancy # gtk/gdk stuff self.area.connect_after("realize", self.paint) # called when a part of the screen is uncovered self.area.connect("expose-event", self.paint) # resize window self.area.connect("configure-event", self._resize_window) # catch mouse events self.area.set_events( gtk.gdk.MOUSE | gtk.gdk.POINTER_MOTION_HINT_MASK | gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.SCROLL_MASK ) self.area.connect("button-press-event", self.mouse_press_handler) self.area.connect("motion-notify-event", self.mouse_handler) self.area.connect("button-release-event", self.context_menu_handler) self.area.connect("scroll-event", self.scroll_handler) self.container.pack_end(self.area) self.camera = Camera(self.settings, lambda: (self.area.allocation.width, self.area.allocation.height)) # Color the dimension value according to the axes. # For "y" axis: 100% green is too bright on light background - we # reduce it a bit. for color, names in ( ( pango.AttrForeground(65535, 0, 0, 0, 100), ("model_dim_x_label", "model_dim_x", "ModelCornerXMax", "ModelCornerXMin", "ModelCornerXSpaces"), ), ( pango.AttrForeground(0, 50000, 0, 0, 100), ("model_dim_y_label", "model_dim_y", "ModelCornerYMax", "ModelCornerYMin", "ModelCornerYSpaces"), ), ( pango.AttrForeground(0, 0, 65535, 0, 100), ("model_dim_z_label", "model_dim_z", "ModelCornerZMax", "ModelCornerZMin", "ModelCornerZSpaces"), ), ): attributes = pango.AttrList() attributes.insert(color) for name in names: self.gui.get_object(name).set_attributes(attributes) # add the items buttons (for configuring visible items) if item_buttons: items_button_container = self.gui.get_object("ViewItems") for button in item_buttons: new_checkbox = gtk.ToggleToolButton() new_checkbox.set_label(button.get_label()) new_checkbox.set_active(button.get_active()) # Configure the two buttons (in "Preferences" and in the 3D view # widget) to toggle each other. This is required for a # consistent view of the setting. connect_button_handlers("toggled", button, new_checkbox) items_button_container.insert(new_checkbox, -1) items_button_container.show_all() # create the drop-down menu if context_menu_actions: action_group = gtk.ActionGroup("context") for action in context_menu_actions: action_group.add_action(action) uimanager = gtk.UIManager() # The "pos" parameter is optional since gtk 2.12 - we can # remove it later. uimanager.insert_action_group(action_group, pos=-1) uimanager_template = '<ui><popup name="context">%s</popup></ui>' uimanager_item_template = """<menuitem action="%s" />""" uimanager_text = uimanager_template % "".join( [uimanager_item_template % action.get_name() for action in context_menu_actions] ) uimanager.add_ui_from_string(uimanager_text) self.context_menu = uimanager.get_widget("/context") self.context_menu.insert(gtk.SeparatorMenuItem(), 0) else: self.context_menu = gtk.Menu() for index, button in enumerate(item_buttons): new_item = gtk.CheckMenuItem(button.get_label()) new_item.set_active(button.get_active()) connect_button_handlers("toggled", button, new_item) self.context_menu.insert(new_item, index) self.context_menu.show_all() else: self.context_menu = None # show the window self.area.show() self.container.show() self.show()
# TODO: the above ".read" may be incomplete - this is ugly # see http://patrakov.blogspot.com/2011/03/case-of-non-raised-exception.html # and http://stackoverflow.com/questions/1824069/urllib2-not-retrieving-entire-http-response url_file.close() except IOError, err_msg: log.error("STLImporter: Failed to read file (%s): %s" \ % (filename, err_msg)) return None # Read the first two lines of (potentially non-binary) input - they should # contain "solid" and "facet". header_lines = [] while len(header_lines) < 2: line = f.readline(200) if len(line) == 0: # empty line (not even a line-feed) -> EOF log.error("STLImporter: No valid lines found in '%s'" % filename) return None # ignore comment lines # note: partial comments (starting within a line) are not handled if not line.startswith(";"): header_lines.append(line) header = "".join(header_lines) # read byte 80 to 83 - they contain the "numfacets" value in binary format f.seek(80) numfacets = unpack("<I", f.read(4))[0] binary = False log.debug("STL import info: %s / %s / %s / %s" % \ (f.len, numfacets, header.find("solid"), header.find("facet"))) if f.len == (84 + 50*numfacets): binary = True
model = pycam.Geometry.Model.ContourModel() for index, line in enumerate(lines): model.append(line) # keep the GUI smooth if callback and (index % 50 == 0): callback() # z scaling is always targeted at the 0..1 range if color_as_height and (model.minz != model.maxz): # scale z to 1 scale_z = 1.0 / (model.maxz - model.minz) if callback: callback(text="Scaling height for multi-layered 2D model") log.info("DXFImporter: scaling height for multi-layered 2D model") model.scale(scale_x=1.0, scale_y=1.0, scale_z=scale_z, callback=callback) # shift the model down to z=0 if model.minz != 0: if callback: callback(text="Shifting 2D model down to to z=0") model.shift(0, 0, -model.minz, callback=callback) log.info("DXFImporter: Imported DXF model (2D): " + \ "%d lines / %d polygons" % \ (len(lines), len(model.get_polygons()))) return model else: link = "http://sf.net/apps/mediawiki/pycam/?title=SupportedFormats" log.error('DXFImporter: No supported elements found in DXF file!\n' \ + '<a href="%s">Read PyCAM\'s modelling hints.</a>' % link) return None