def _point_at_cb(self, event_cb): """`point_at` callback. Args: event_cb -- coin event callback object """ event = event_cb.getEvent() if (event.getState() == coin.SoMouseButtonEvent.DOWN and event.getButton() == coin.SoMouseButtonEvent.BUTTON1): # Get point picked_point = event_cb.getPickedPoint() try: point = App.Vector(picked_point.getPoint()) except AttributeError: # No picked point (outside geometry) msg = (translate( "Render", "[Point at] Target outside geometry " "-- Aborting", ) + "\n") App.Console.PrintMessage(msg) else: # Make underlying object point at target point self.fpo.Proxy.point_at(point) msg = (translate( "Render", "[Point at] Now pointing at " "({0.x}, {0.y}, {0.z})", ) + "\n") App.Console.PrintMessage(msg.format(point)) finally: # Remove coin event catcher Gui.ActiveDocument.ActiveView.removeEventCallbackPivy( coin.SoMouseButtonEvent.getClassTypeId(), self.callback)
def on_delete_cb(self, feature, subelements): """Respond to delete object event (callback).""" delete = True if self.fpo.Group: # Project is not empty title = translate("Render", "Warning: Deleting Non-Empty Project") msg = translate( "Render", "Project is not empty. " "All its contents will be deleted too.\n\n" "Are you sure you want to continue?", ) box = QMessageBox( QMessageBox.Warning, title, msg, QMessageBox.Yes | QMessageBox.No, ) usr_confirm = box.exec() if usr_confirm == QMessageBox.Yes: subobjs = self.fpo.Proxy.all_views(include_groups=True)[1:] for obj in subobjs: obj.Document.removeObject(obj.Name) else: delete = False return delete
def message(self): """Give error message.""" msg = ( translate("Render", "[Render] Error: Renderer '%s' not found") % self.renderer ) return msg
def Activated(self): # pylint: disable=no-self-use """Respond to Activated event (callback). This code is executed when the command is run in FreeCAD. It creates a new rendering project into the active document. """ # First, split selection into objects and projects selection = Gui.Selection.getSelection() objs, projs = [], [] for item in selection: (projs if RendererHandler.is_project(item) else objs).append(item) # Then, get target project. # We first look among projects in the selection # and, if none, we fall back on active document's projects activedoc_projects = filter(RendererHandler.is_project, App.ActiveDocument.Objects) try: target_project = next(it.chain(projs, activedoc_projects)) except StopIteration: msg = (translate( "Render", "[Render] Unable to find a valid project in selection " "or document", ) + "\n") App.Console.PrintError(msg) return # Finally, add objects to target project and recompute target_project.Proxy.add_views(objs) App.ActiveDocument.recompute()
def user_select_template(renderer): """Make user select a template for a given renderer. This method opens a UI file dialog and asks user to select a template file. The returned path is the *relative* path starting from Render.TEMPLATEDIR. Args: renderer -- a renderer name (str) Returns: A string containing the (relative) path to the selected template. """ try: handler = RendererHandler(renderer) except RendererNotFoundError as err: msg = ( "[Render] Failed to open template selector - Renderer '%s' " "not found\n" ) App.Console.PrintError(msg % err.renderer) return None filefilter = handler.get_template_file_filter() filefilter += ";; All files (*.*)" caption = translate("Render", "Select template") openfilename = QFileDialog.getOpenFileName( Gui.getMainWindow(), caption, TEMPLATEDIR, filefilter ) template_path = openfilename[0] if not template_path: return None return os.path.relpath(template_path, TEMPLATEDIR)
def add_to_group(objs, group): """Add objects as views to a group. objs -- FreeCAD objects to add group -- The group (App::DocumentObjectGroup) to add to """ for obj in objs: success = False if ( hasattr(obj, "Group") and not obj.isDerivedFrom("App::Part") and not obj.isDerivedFrom("PartDesign::Body") ): assert obj != group # Just in case (infinite recursion)... label = View.view_label(obj, group, True) new_group = App.ActiveDocument.addObject( "App::DocumentObjectGroup", label ) new_group.Label = label group.addObject(new_group) add_to_group(obj.Group, new_group) success = True if RendererHandler.is_renderable(obj): View.create(source=obj, project=group) success = True if not success: msg = ( translate( "Render", "[Render] Unable to create rendering view for " "object '{o}': unhandled object type", ) + "\n" ) App.Console.PrintWarning(msg.format(o=obj.fpo.Label))
def point_at(self): """Make this object interactively point at another object. User will be requested to select an object to point at. """ msg = (translate( "Render", "[Point at] Please select target (on geometry)") + "\n") App.Console.PrintMessage(msg) self.callback = Gui.ActiveDocument.ActiveView.addEventCallbackPivy( coin.SoMouseButtonEvent.getClassTypeId(), self._point_at_cb)
def render(self): """Render project. This method calls proxy's 'render' method. """ try: self.fpo.Proxy.render() except AttributeError as err: msg = translate("Render", "[Render] Cannot render: {e}") + "\n" App.Console.PrintError(msg.format(e=err))
def get_rendering_string(self, view): """Provide a rendering string for the view of an object. This method selects the specialized rendering method adapted for 'view', according to its source object type, and calls it. Parameters: view -- the view of the object to render Returns: a rendering string in the format of the external renderer for the supplied 'view' """ try: source = view.Source objtype = getproxyattr(source, "type", "Object") name = str(source.Name) switcher = { "Object": RendererHandler._render_object, "PointLight": RendererHandler._render_pointlight, "Camera": RendererHandler._render_camera, "AreaLight": RendererHandler._render_arealight, "SunskyLight": RendererHandler._render_sunskylight, "ImageLight": RendererHandler._render_imagelight, } res = switcher[objtype](self, name, view) except (AttributeError, TypeError, AssertionError) as err: msg = ( translate( "Render", "[Render] Cannot render view '{0}': {1} (file {2}, " "line {3} in {4}). Skipping...", ) + "\n" ) _, _, exc_traceback = sys.exc_info() framestack = traceback.extract_tb(exc_traceback)[-1] App.Console.PrintWarning( msg.format( getattr(view, "Label", "<No label>"), err, framestack.filename, framestack.lineno, framestack.name, ) ) return "" else: return res
def Activated(self): App.ActiveDocument.openTransaction( translate("Render", "Create material")) Gui.Control.closeDialog() Gui.addModule("Render") cmds = [ "obj = Render.make_material()", "Gui.Selection.clearSelection()", "Gui.Selection.addSelection(obj.Document.Name, obj.Name)", "obj.ViewObject.Document.setEdit(obj.ViewObject, 0)", ] for cmd in cmds: Gui.doCommand(cmd) App.ActiveDocument.commitTransaction() App.ActiveDocument.recompute()
def __init__(self, color=QColor(127, 127, 127), use_object_color=False): """Initialize widget. Args: color -- RGB color used to initialize the color picker use_object_color -- boolean used to initialize the 'use object color' checkbox """ super().__init__() self.use_object_color = use_object_color self.colorpicker = ColorPicker(color) self.checkbox = QCheckBox() self.checkbox.setText(translate("Render", "Use object color")) self.setLayout(QHBoxLayout()) self.layout().addWidget(self.colorpicker) self.layout().addWidget(self.checkbox) self.layout().setContentsMargins(0, 0, 0, 0) QObject.connect( self.checkbox, SIGNAL("stateChanged(int)"), self.on_object_color_change, ) self.checkbox.setChecked(use_object_color)
) # =========================================================================== # Export # =========================================================================== Param = collections.namedtuple("Param", "name type default desc") # IMPORTANT: Please note that, by convention, the first parameter of each # material will be used as default color in fallback mechanisms. # Please be careful to preserve a color-typed field as first parameter of each # material, if you modify an existing material or you add a new one... STD_MATERIALS_PARAMETERS = { "Glass": [ Param("Color", "RGB", (1, 1, 1), translate("Render", "Transmitted color")), Param("IOR", "float", 1.5, translate("Render", "Index of refraction")), ], "Disney": [ Param( "BaseColor", "RGB", (0.8, 0.8, 0.8), translate("Render", "Base color"), ), Param( "Subsurface", "float", 0.0, translate("Render", "Subsurface coefficient"), ),
def Activated(self): # pylint: disable=no-self-use """Respond to Activated event (callback). This code is executed when the command is run in FreeCAD. It sets the Material property of the selected object(s). If the Material property does not exist in the object(s), it is created. """ # Get selected objects selection = Gui.Selection.getSelection() if not selection: title = translate("Render", "Empty Selection") msg = translate( "Render", "Please select object(s) before applying " "material.", ) QMessageBox.warning(None, title, msg) return # Let user pick the Material mats = [ o for o in App.ActiveDocument.Objects if o.isDerivedFrom("App::MaterialObjectPython") ] if not mats: title = translate("Render", "No Material") msg = translate( "Render", "No Material in document. Please create a " "Material before applying.", ) QMessageBox.warning(None, title, msg) return matlabels = [m.Label for m in mats] current_mats_labels = [ o.Material.Label for o in selection if hasattr(o, "Material") and hasattr(o.Material, "Label") and o.Material.Label ] current_mats = [ count for count, val in enumerate(matlabels) if val in current_mats_labels ] current_mat = current_mats[0] if len(current_mats) == 1 else 0 userinput, status = QInputDialog.getItem( None, translate("Render", "Material Applier"), translate("Render", "Choose Material to apply to selection:"), matlabels, current_mat, False, ) if not status: return material = next(m for m in mats if m.Label == userinput) # Update selected objects App.ActiveDocument.openTransaction("MaterialApplier") for obj in selection: # Add Material property to the object if it hasn't got one if "Material" not in obj.PropertiesList: obj.addProperty( "App::PropertyLink", "Material", "", QT_TRANSLATE_NOOP("App::Property", "The Material for this object"), ) try: obj.Material = material except TypeError: msg = (translate( "Render", "Cannot apply Material to object '%s': " "object's Material property is of wrong " "type", ) + "\n") App.Console.PrintError(msg % obj.Label) App.ActiveDocument.commitTransaction()
def render(self, external=True, wait_for_completion=False): """Render the project, calling an external renderer. Args: external -- flag to switch between internal/external version of renderer wait_for_completion -- flag to wait for rendering completion before return, in a blocking way (default to False) Returns: Output file path """ obj = self.fpo wait_for_completion = bool(wait_for_completion) # Get a handle to renderer module try: renderer = RendererHandler( rdrname=obj.Renderer, linear_deflection=obj.LinearDeflection, angular_deflection=obj.AngularDeflection, transparency_boost=obj.TransparencySensitivity, ) except ModuleNotFoundError: msg = ( translate( "Render", "[Render] Cannot render project: Renderer '%s' not " "found", ) + "\n" ) App.Console.PrintError(msg % obj.Renderer) return "" # Get the rendering template if obj.getTypeIdOfProperty("Template") == "App::PropertyFile": # Legacy template path (absolute path) template_path = obj.Template else: # Current template path (relative path) template_path = os.path.join(TEMPLATEDIR, obj.Template) if not os.path.isfile(template_path): msg = translate( "Render", "Cannot render project: Template not found ('%s')" ) msg = "[Render] " + (msg % template_path) + "\n" App.Console.PrintError(msg) return "" with open(template_path, "r", encoding="utf8") as template_file: template = template_file.read() # Build a default camera, to be used if no camera is present in the # scene camstr = ( Gui.ActiveDocument.ActiveView.getCamera() if App.GuiUp else DEFAULT_CAMERA_STRING ) cam = renderer.get_camsource_string(get_cam_from_coin_string(camstr)) # Get objects rendering strings (including lights, cameras...) # views = self.all_views() get_rdr_string = ( renderer.get_rendering_string if obj.DelayedBuild else attrgetter("ViewResult") ) if App.GuiUp: objstrings = [ get_rdr_string(v) for v in self.all_views() if v.Source.ViewObject.Visibility ] else: objstrings = [get_rdr_string(v) for v in self.all_views()] # Add a ground plane if required if getattr(obj, "GroundPlane", False): objstrings.append(self.write_groundplane(renderer)) # Merge all strings (cam, objects, ground plane...) into rendering # template renderobjs = "\n".join(objstrings) if "RaytracingCamera" in template: template = re.sub("(.*RaytracingCamera.*)", cam, template) template = re.sub("(.*RaytracingContent.*)", renderobjs, template) else: template = re.sub( "(.*RaytracingContent.*)", cam + "\n" + renderobjs, template ) version_major = sys.version_info.major template = template.encode("utf8") if version_major < 3 else template # Write instantiated template into a temporary file fhandle, fpath = mkstemp( prefix=obj.Name, suffix=os.path.splitext(obj.Template)[-1] ) with open(fpath, "w", encoding="utf8") as fobj: fobj.write(template) os.close(fhandle) obj.PageResult = fpath os.remove(fpath) assert obj.PageResult, "Rendering error: No page result" # Fetch the rendering parameters prefix = PARAMS.GetString("Prefix", "") if prefix: prefix += " " try: output = obj.OutputImage assert output except (AttributeError, AssertionError): output = os.path.splitext(obj.PageResult)[0] + ".png" try: width = int(obj.RenderWidth) except (AttributeError, ValueError, TypeError): width = 800 try: height = int(obj.RenderHeight) except (AttributeError, ValueError, TypeError): height = 600 # Get the renderer command on the generated temp file, with rendering # params cmd, img = renderer.render( obj, prefix, external, output, width, height ) img = img if obj.OpenAfterRender else None if not cmd: # Command is empty (perhaps lack of data in parameters) return None # Execute renderer rdr_executor = RendererExecutor(cmd, img) rdr_executor.start() if wait_for_completion: # Useful in console mode... rdr_executor.join() # And eventually return result path return img