def __init__(self, debug, cad_width, height): self.debug_output = Output() self.debug = debug self.cad_width = cad_width self.height = height self.display = CadqueryDisplay() widget = self.display.create(height=height, cad_width=cad_width) self.display.display(widget)
def _show(assembly, height=600, tree_width=250, cad_width=800, quality=0.5, axes=False, axes0=True, grid=False, ortho=True, transparent=False, position=None, rotation=None, zoom=None, mac_scrollbar=True, sidecar=None): def sidecar_display(sidecar, widget): sidecar.clear_output(True) with sidecar: display(widget) print("Done, using side car '%s'" % sidecar.title) d = CadqueryDisplay() widget = d.display(states=assembly.to_state(), shapes=assembly.collect_shapes(), mapping=assembly.obj_mapping(), tree=assembly.to_nav_dict(), height=height, tree_width=tree_width, cad_width=cad_width, quality=quality, axes=axes, axes0=axes0, grid=grid, ortho=ortho, transparent=transparent, position=position, rotation=rotation, zoom=zoom, mac_scrollbar=mac_scrollbar) d.info.ready_msg(d.cq_view.grid.step) if sidecar is not None: if not sidecar: # global sidecar setting overwritten by False display(widget) else: # use provided sidecar sidecar_display(sidecar, widget) else: if SIDECAR is not None: # use global sidecar sidecar_display(SIDECAR, widget) else: display(widget) return d
def __init__(self, quality, deviation, angular_tolerance, edge_accuracy, debug, cad_width, height): self.debug_output = Output() self.quality = quality self.deviation = deviation self.angular_tolerance = angular_tolerance self.edge_accuracy = edge_accuracy self.debug = debug self.cad_width = cad_width self.height = height self.display = CadqueryDisplay() widget = self.display.create(height=height, cad_width=cad_width) self.display.display(widget)
def _show(assembly, height=600, tree_width=250, cad_width=800, quality=0.5, axes=False, axes0=True, grid=False, ortho=True, transparent=False, position=None, rotation=None, zoom=None, mac_scrollbar=True, sidecar=None): d = CadqueryDisplay() widget = d.create(states=assembly.to_state(), shapes=assembly.collect_shapes(), mapping=assembly.obj_mapping(), tree=assembly.to_nav_dict(), height=height, tree_width=tree_width, cad_width=cad_width, quality=quality, axes=axes, axes0=axes0, grid=grid, ortho=ortho, transparent=transparent, position=position, rotation=rotation, zoom=zoom, mac_scrollbar=mac_scrollbar) d.info.ready_msg(d.cq_view.grid.step) d.display(widget, sidecar) return d
def _show(assembly, **kwargs): d = CadqueryDisplay() params = d.defaults.copy() for k, v in kwargs.items(): if params.get(k, None) is None: raise KeyError("Paramater %s is not a valid argument for show()" % k) else: params[k] = v widget = d.create( states=assembly.to_state(), shapes=assembly.collect_shapes(), mapping=assembly.obj_mapping(), tree=assembly.to_nav_dict(), height=params["height"], tree_width=params["tree_width"], cad_width=params["cad_width"], quality=params["quality"], angular_tolerance=params["angular_tolerance"], edge_accuracy=params["edge_accuracy"], axes=params["axes"], axes0=params["axes0"], grid=params["grid"], ortho=params["ortho"], transparent=params["transparent"], position=params["position"], rotation=params["rotation"], zoom=params["zoom"], mac_scrollbar=params["mac_scrollbar"], timeit=params["timeit"], ) d.info.ready_msg(d.cq_view.grid.step) d.display(widget, params["sidecar"]) return d
def _show(assembly, **kwargs): for k in kwargs: if get_default(k) is None: raise KeyError("Paramater %s is not a valid argument for show()" % k) mapping = assembly.to_state() shapes = assembly.collect_mapped_shapes(mapping) tree = tree = assembly.to_nav_dict() d = CadqueryDisplay() widget = d.create(**kwargs) d.display(widget) d.add_shapes(shapes=shapes, mapping=mapping, tree=tree) d.info.ready_msg(d.cq_view.grid.step) return d
class Replay(object): def __init__(self, quality, deviation, angular_tolerance, edge_accuracy, debug, cad_width, height): self.debug_output = Output() self.quality = quality self.deviation = deviation self.angular_tolerance = angular_tolerance self.edge_accuracy = edge_accuracy self.debug = debug self.cad_width = cad_width self.height = height self.display = CadqueryDisplay() widget = self.display.create(height=height, cad_width=cad_width) self.display.display(widget) def format_steps(self, raw_steps): def to_code(step, results): def to_name(obj): if isinstance(obj, cq.Workplane): name = results.get(obj, None) else: name = str(obj) return obj if name is None else name if step.func != "": if step.func == "newObject": args = ("...", ) else: args = tuple([to_name(arg) for arg in step.args]) code = "%s%s%s" % ("| " * step.level, step.func, args) code = code[:-2] if len(step.args) == 1 else code[:-1] if len(step.args) > 0 and len(step.kwargs) > 0: code += "," if step.kwargs != {}: code += ", ".join( ["%s=%s" % (k, v) for k, v in step.kwargs.items()]) code += ")" if step.result_name != "": code += " => %s" % step.result_name elif step.var != "": code = "%s%s" % ("| " * step.level, step.var) else: code = "ERROR" return code steps = [] entries = [] obj_index = 1 results = {step.result_obj: None for step in raw_steps} for i in range(len(raw_steps)): step = raw_steps[i] next_level = step.level if i == (len(raw_steps) - 1) else raw_steps[i + 1].level # level change, so add/use the variable name if step.level > 0 and step.level != next_level and step.result_name == "": obj_name = "_v%d" % obj_index obj_index += 1 step.result_name = obj_name steps.append(step) for step in steps: if results[step.result_obj] is None: # first occurence, take note and keep results[step.result_obj] = step.result_name else: # next occurences remove function and add variable name step.var = results[step.result_obj] step.clear_func() last_level = 1000000 for step in reversed(steps): if step.level < last_level: last_level = 1000000 entries.insert(0, (to_code(step, results), step.result_obj)) if step.var != "": last_level = step.level return entries def to_array(self, workplane, level=0, result_name=""): def walk(caller, level=0, result_name=""): stack = [ Step( level, func=caller["func"], args=caller["args"], kwargs=caller["kwargs"], result_name=result_name, result_obj=caller["obj"], ) ] for child in reversed(caller["children"]): stack = walk(child, level + 1) + stack for arg in child["args"]: if isinstance(arg, cq.Workplane): result_name = getattr(arg, "name", None) stack = self.to_array(arg, level=level + 2, result_name=result_name) + stack return stack stack = [] obj = workplane while obj is not None: caller = getattr(obj, "_caller", None) result_name = getattr(obj, "name", "") if caller is not None: stack = walk(caller, level, result_name) + stack for arg in caller["args"]: if isinstance(arg, cq.Workplane): result_name = getattr(arg, "name", "") stack = self.to_array(arg, level=level + 1, result_name=result_name) + stack obj = obj.parent return stack def select_handler(self, change): with self.debug_output: if change["name"] == "index": self.select(change["new"]) def select(self, indexes): self.debug_output.clear_output() with self.debug_output: self.indexes = indexes cad_objs = [self.stack[i][1] for i in self.indexes] # Add hidden result to start with final size and allow for comparison if not isinstance(self.stack[-1][1].val(), cq.Vector): result = Part(self.stack[-1][1], "Result", show_faces=False, show_edges=False) objs = [result] + cad_objs else: objs = cad_objs with self.debug_output: assembly = to_assembly(*objs) mapping = assembly.to_state() shapes = assembly.collect_mapped_shapes( mapping, quality=self.quality, deviation=self.deviation, angular_tolerance=self.angular_tolerance, edge_accuracy=self.edge_accuracy, render_edges=get_default("render_edges"), render_normals=get_default("render_normals"), ) tree = assembly.to_nav_dict() self.display.add_shapes(shapes=shapes, mapping=mapping, tree=tree, bb=_combined_bb(shapes), reset_camera=False)
class Viewer: def __init__(self, zmq_port): self.zmq_port = zmq_port self.cad_display = None self.interactive = None self.zmq_server = None self.root_group = None self.log_output = widgets.Output( layout=widgets.Layout(height="400px", overflow="scroll")) self.log_output.add_class("mac-scrollbar") def _display(self, data, logo=False): mesh_data = data["data"] config = data["config"] info(mesh_data["bb"]) if logo or config.get("cad_width") is None: config["cad_width"] = get_default("cad_width") else: if config.get("cad_width") < 640: warn("cad_width has to be >= 640, setting to 640") config["cad_width"] = 640 if logo or config.get("height") is None: config["height"] = get_default("height") else: if config.get("height") < 400: warn("height has to be >= 400, setting to 400") config["height"] = 400 if logo or config.get("tree_width") is not None: config["tree_width"] = get_default("tree_width") else: if config.get("tree_width") < 200: warn("tree_width has to be >= 200, setting to 200") config["tree_width"] = 200 width = config["cad_width"] + config["tree_width"] + 6 if self.interactive is not None: self.interactive.layout.width = px(width - 30) if self.log_output is not None: self.log_output.layout.width = px(width - 30) self.log_view.layout.width = px(width) # Force reset of camera to not inhereit splash settings for first object if self.cad_display.splash: config["reset_camera"] = True self.cad_display.splash = False self.cad_display.set_size(config.get("tree_width"), config.get("cad_width"), config.get("height")) self.cad_display.init_progress(data.get("count", 1)) create_args, add_shape_args = split_args(config) self.cad_display._update_settings(**create_args) self.cad_display.add_shapes(**mesh_data, **add_shape_args) info(create_args, add_shape_args) self.cad_display.info.ready_msg(self.cad_display.cq_view.grid.step) self.root_group = self.cad_display.root_group def start_viewer(self, cad_width, cad_height, theme): info(f"zmq_port: {self.zmq_port}") info(f"theme: {theme}") info(f"cad_width: {cad_width}") info(f"cad_height: {cad_height}") set_defaults(theme=theme, cad_width=cad_width, height=cad_height) self.interactive = widgets.Output(layout=widgets.Layout(height="0px")) self.cad_display = CadqueryDisplay() cad_view = self.cad_display.create() # remove jupyter cadquery start message clear_output() self.log_view = widgets.Accordion(children=[self.log_output]) self.log_view.set_title(0, "Log") self.log_view.selected_index = None display(widgets.VBox([cad_view, self.interactive, self.log_view])) logo = pickle.loads(base64.b64decode(LOGO_DATA)) self._display(logo, True) self.cad_display.splash = True stop_viewer() context = zmq.Context() socket = context.socket(zmq.REP) for i in range(5): try: socket.bind(f"tcp://*:{self.zmq_port}") break except Exception as ex: print(f"{ex}: retrying ... ") time.sleep(1) self.zmq_server = socket info("zmq started\n") def return_error(error_msg): error(error_msg) socket.send_json({"result": "error", "msg": error_msg}) def return_success(t): info(f"duration: {time.time() - t:7.2f}") socket.send_json({"result": "success"}) def msg_handler(): while True: msg = socket.recv() try: data = pickle.loads(msg) except Exception as ex: return_error(str(ex)) continue self.interactive.outputs = () self.interactive.layout.height = f"0px" if data.get("type") == "data": try: t = time.time() self._display(data) return_success(t) except Exception as ex: error_msg = f"{type(ex).__name__}: {ex}" return_error(error_msg) elif data.get("type") == "animation": try: t = time.time() animation = Animation(self.root_group) for track in data["tracks"]: animation.add_track(*track) widget = animation.animate(data["speed"], data["autoplay"]) # With INTERACTIVE: display(widget) does not work, see # https://ipywidgets.readthedocs.io/en/7.6.3/examples/Output%20Widget.html#Interacting-with-output-widgets-from-background-threads # # INTERACTIVE.addpend_display_data(widget) doesn't work either, see https://github.com/jupyter-widgets/ipywidgets/issues/1811 # Should be solved, however isn't # mime_data = { "output_type": "display_data", "data": { "text/plain": "AnimationAction", "application/vnd.jupyter.widget-view+json": { "version_major": 2, "version_minor": 0, "model_id": widget.model_id, }, }, "metadata": {}, } self.interactive.outputs = (mime_data, ) self.interactive.layout.height = f"40px" return_success(t) except Exception as ex: error_msg = f"{type(ex).__name__}: {ex}" return_error(error_msg) else: return_error(f"Wrong message type {data.get('type')}") thread = threading.Thread(target=msg_handler) thread.setDaemon(True) thread.start() self.cad_display.info.add_html("<b>zmq server started</b>") def stop_viewer(self): if self.zmq_server is not None: try: self.zmq_server.close() info("zmq stopped") if self.cad_display is not None and self.cad_display.info is not None: self.cad_display.info.add_html("<b>HTTP zmq stopped</b>") self.zmq_server = None time.sleep(0.5) except Exception as ex: error("Exception %s" % ex)
def start_viewer(self, cad_width, cad_height, theme): info(f"zmq_port: {self.zmq_port}") info(f"theme: {theme}") info(f"cad_width: {cad_width}") info(f"cad_height: {cad_height}") set_defaults(theme=theme, cad_width=cad_width, height=cad_height) self.interactive = widgets.Output(layout=widgets.Layout(height="0px")) self.cad_display = CadqueryDisplay() cad_view = self.cad_display.create() # remove jupyter cadquery start message clear_output() self.log_view = widgets.Accordion(children=[self.log_output]) self.log_view.set_title(0, "Log") self.log_view.selected_index = None display(widgets.VBox([cad_view, self.interactive, self.log_view])) logo = pickle.loads(base64.b64decode(LOGO_DATA)) self._display(logo, True) self.cad_display.splash = True stop_viewer() context = zmq.Context() socket = context.socket(zmq.REP) for i in range(5): try: socket.bind(f"tcp://*:{self.zmq_port}") break except Exception as ex: print(f"{ex}: retrying ... ") time.sleep(1) self.zmq_server = socket info("zmq started\n") def return_error(error_msg): error(error_msg) socket.send_json({"result": "error", "msg": error_msg}) def return_success(t): info(f"duration: {time.time() - t:7.2f}") socket.send_json({"result": "success"}) def msg_handler(): while True: msg = socket.recv() try: data = pickle.loads(msg) except Exception as ex: return_error(str(ex)) continue self.interactive.outputs = () self.interactive.layout.height = f"0px" if data.get("type") == "data": try: t = time.time() self._display(data) return_success(t) except Exception as ex: error_msg = f"{type(ex).__name__}: {ex}" return_error(error_msg) elif data.get("type") == "animation": try: t = time.time() animation = Animation(self.root_group) for track in data["tracks"]: animation.add_track(*track) widget = animation.animate(data["speed"], data["autoplay"]) # With INTERACTIVE: display(widget) does not work, see # https://ipywidgets.readthedocs.io/en/7.6.3/examples/Output%20Widget.html#Interacting-with-output-widgets-from-background-threads # # INTERACTIVE.addpend_display_data(widget) doesn't work either, see https://github.com/jupyter-widgets/ipywidgets/issues/1811 # Should be solved, however isn't # mime_data = { "output_type": "display_data", "data": { "text/plain": "AnimationAction", "application/vnd.jupyter.widget-view+json": { "version_major": 2, "version_minor": 0, "model_id": widget.model_id, }, }, "metadata": {}, } self.interactive.outputs = (mime_data, ) self.interactive.layout.height = f"40px" return_success(t) except Exception as ex: error_msg = f"{type(ex).__name__}: {ex}" return_error(error_msg) else: return_error(f"Wrong message type {data.get('type')}") thread = threading.Thread(target=msg_handler) thread.setDaemon(True) thread.start() self.cad_display.info.add_html("<b>zmq server started</b>")