501 lines
18 KiB
Python
501 lines
18 KiB
Python
|
|
# from curve workbench
|
||
|
|
|
||
|
|
import FreeCAD
|
||
|
|
import FreeCADGui
|
||
|
|
import Part
|
||
|
|
import PySide.QtCore as QtCore
|
||
|
|
import PySide.QtGui as QtGui
|
||
|
|
from pivy import coin
|
||
|
|
|
||
|
|
from Utils import graphics
|
||
|
|
|
||
|
|
|
||
|
|
def parameterization(points, a, closed):
|
||
|
|
"""Computes a knot Sequence for a set of points
|
||
|
|
fac (0-1) : parameterization factor
|
||
|
|
fac=0 -> Uniform / fac=0.5 -> Centripetal / fac=1.0 -> Chord-Length"""
|
||
|
|
pts = points.copy()
|
||
|
|
if closed and pts[0].distanceToPoint(pts[-1]) > 1e-7: # we need to add the first point as the end point
|
||
|
|
pts.append(pts[0])
|
||
|
|
params = [0]
|
||
|
|
for i in range(1, len(pts)):
|
||
|
|
p = pts[i] - pts[i - 1]
|
||
|
|
if isinstance(p, FreeCAD.Vector):
|
||
|
|
le = p.Length
|
||
|
|
else:
|
||
|
|
le = p.length()
|
||
|
|
pl = pow(le, a)
|
||
|
|
params.append(params[-1] + pl)
|
||
|
|
return params
|
||
|
|
|
||
|
|
class ConnectionMarker(graphics.Marker):
|
||
|
|
def __init__(self, points):
|
||
|
|
super(ConnectionMarker, self).__init__(points, True)
|
||
|
|
|
||
|
|
class MarkerOnShape(graphics.Marker):
|
||
|
|
def __init__(self, points, sh=None):
|
||
|
|
super(MarkerOnShape, self).__init__(points, True)
|
||
|
|
self._shape = None
|
||
|
|
self._sublink = None
|
||
|
|
self._tangent = None
|
||
|
|
self._translate = coin.SoTranslation()
|
||
|
|
self._text_font = coin.SoFont()
|
||
|
|
self._text_font.name = "Arial:Bold"
|
||
|
|
self._text_font.size = 13.0
|
||
|
|
self._text = coin.SoText2()
|
||
|
|
self._text_switch = coin.SoSwitch()
|
||
|
|
self._text_switch.addChild(self._translate)
|
||
|
|
self._text_switch.addChild(self._text_font)
|
||
|
|
self._text_switch.addChild(self._text)
|
||
|
|
self.on_drag_start.append(self.add_text)
|
||
|
|
self.on_drag_release.append(self.remove_text)
|
||
|
|
self.addChild(self._text_switch)
|
||
|
|
if isinstance(sh, Part.Shape):
|
||
|
|
self.snap_shape = sh
|
||
|
|
elif isinstance(sh, (tuple, list)):
|
||
|
|
self.sublink = sh
|
||
|
|
|
||
|
|
def subshape_from_sublink(self, o):
|
||
|
|
name = o[1][0]
|
||
|
|
print(name, " selected")
|
||
|
|
if 'Vertex' in name:
|
||
|
|
n = eval(name.lstrip('Vertex'))
|
||
|
|
return o[0].Shape.Vertexes[n - 1]
|
||
|
|
elif 'Edge' in name:
|
||
|
|
n = eval(name.lstrip('Edge'))
|
||
|
|
return o[0].Shape.Edges[n - 1]
|
||
|
|
elif 'Face' in name:
|
||
|
|
n = eval(name.lstrip('Face'))
|
||
|
|
return o[0].Shape.Faces[n - 1]
|
||
|
|
|
||
|
|
def add_text(self):
|
||
|
|
self._text_switch.whichChild = coin.SO_SWITCH_ALL
|
||
|
|
self.on_drag.append(self.update_text)
|
||
|
|
|
||
|
|
def remove_text(self):
|
||
|
|
self._text_switch.whichChild = coin.SO_SWITCH_NONE
|
||
|
|
self.on_drag.remove(self.update_text)
|
||
|
|
|
||
|
|
def update_text(self):
|
||
|
|
p = self.points[0]
|
||
|
|
coords = ['{: 9.3f}'.format(p[0]), '{: 9.3f}'.format(p[1]), '{: 9.3f}'.format(p[2])]
|
||
|
|
self._translate.translation = p
|
||
|
|
self._text.string.setValues(0, 3, coords)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def tangent(self):
|
||
|
|
return self._tangent
|
||
|
|
|
||
|
|
@tangent.setter
|
||
|
|
def tangent(self, t):
|
||
|
|
if isinstance(t, FreeCAD.Vector):
|
||
|
|
if t.Length > 1e-7:
|
||
|
|
self._tangent = t
|
||
|
|
self._tangent.normalize()
|
||
|
|
self.marker.markerIndex = coin.SoMarkerSet.DIAMOND_FILLED_9_9
|
||
|
|
else:
|
||
|
|
self._tangent = None
|
||
|
|
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
|
||
|
|
else:
|
||
|
|
self._tangent = None
|
||
|
|
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
|
||
|
|
|
||
|
|
@property
|
||
|
|
def snap_shape(self):
|
||
|
|
return self._shape
|
||
|
|
|
||
|
|
@snap_shape.setter
|
||
|
|
def snap_shape(self, sh):
|
||
|
|
if isinstance(sh, Part.Shape):
|
||
|
|
self._shape = sh
|
||
|
|
else:
|
||
|
|
self._shape = None
|
||
|
|
self.alter_color()
|
||
|
|
|
||
|
|
@property
|
||
|
|
def sublink(self):
|
||
|
|
return self._sublink
|
||
|
|
|
||
|
|
@sublink.setter
|
||
|
|
def sublink(self, sl):
|
||
|
|
if isinstance(sl, (tuple, list)) and not (sl == self._sublink):
|
||
|
|
self._shape = self.subshape_from_sublink(sl)
|
||
|
|
self._sublink = sl
|
||
|
|
else:
|
||
|
|
self._shape = None
|
||
|
|
self._sublink = None
|
||
|
|
self.alter_color()
|
||
|
|
|
||
|
|
def alter_color(self):
|
||
|
|
if isinstance(self._shape, Part.Vertex):
|
||
|
|
self.set_color("white")
|
||
|
|
elif isinstance(self._shape, Part.Edge):
|
||
|
|
self.set_color("cyan")
|
||
|
|
elif isinstance(self._shape, Part.Face):
|
||
|
|
self.set_color("magenta")
|
||
|
|
else:
|
||
|
|
self.set_color("black")
|
||
|
|
|
||
|
|
def __repr__(self):
|
||
|
|
return "MarkerOnShape({})".format(self._shape)
|
||
|
|
|
||
|
|
def drag(self, mouse_coords, fact=1.):
|
||
|
|
if self.enabled:
|
||
|
|
pts = self.points
|
||
|
|
for i, p in enumerate(pts):
|
||
|
|
p[0] = mouse_coords[0] * fact + self._tmp_points[i][0]
|
||
|
|
p[1] = mouse_coords[1] * fact + self._tmp_points[i][1]
|
||
|
|
p[2] = mouse_coords[2] * fact + self._tmp_points[i][2]
|
||
|
|
if self._shape:
|
||
|
|
v = Part.Vertex(p[0], p[1], p[2])
|
||
|
|
proj = v.distToShape(self._shape)[1][0][1]
|
||
|
|
# FreeCAD.Console.PrintMessage("%s -> %s\n"%(p.getValue(), proj))
|
||
|
|
p[0] = proj.x
|
||
|
|
p[1] = proj.y
|
||
|
|
p[2] = proj.z
|
||
|
|
self.points = pts
|
||
|
|
for foo in self.on_drag:
|
||
|
|
foo()
|
||
|
|
|
||
|
|
|
||
|
|
class ConnectionPolygon(graphics.Polygon):
|
||
|
|
std_col = "green"
|
||
|
|
|
||
|
|
def __init__(self, markers):
|
||
|
|
super(ConnectionPolygon, self).__init__(
|
||
|
|
sum([m.points for m in markers], []), True)
|
||
|
|
self.markers = markers
|
||
|
|
|
||
|
|
for m in self.markers:
|
||
|
|
m.on_drag.append(self.updatePolygon)
|
||
|
|
|
||
|
|
def updatePolygon(self):
|
||
|
|
self.points = sum([m.points for m in self.markers], [])
|
||
|
|
|
||
|
|
@property
|
||
|
|
def drag_objects(self):
|
||
|
|
return self.markers
|
||
|
|
|
||
|
|
def check_dependency(self):
|
||
|
|
if any([m._delete for m in self.markers]):
|
||
|
|
self.delete()
|
||
|
|
|
||
|
|
class ConnectionLine(graphics.Line):
|
||
|
|
def __init__(self, markers):
|
||
|
|
super(ConnectionLine, self).__init__(
|
||
|
|
sum([m.points for m in markers], []), True)
|
||
|
|
self.markers = markers
|
||
|
|
self._linear = False
|
||
|
|
for m in self.markers:
|
||
|
|
m.on_drag.append(self.updateLine)
|
||
|
|
|
||
|
|
def updateLine(self):
|
||
|
|
self.points = sum([m.points for m in self.markers], [])
|
||
|
|
if self._linear:
|
||
|
|
p1 = self.markers[0].points[0]
|
||
|
|
p2 = self.markers[-1].points[0]
|
||
|
|
t = p2 - p1
|
||
|
|
tan = FreeCAD.Vector(t[0], t[1], t[2])
|
||
|
|
for m in self.markers:
|
||
|
|
m.tangent = tan
|
||
|
|
|
||
|
|
@property
|
||
|
|
def linear(self):
|
||
|
|
return self._linear
|
||
|
|
|
||
|
|
@linear.setter
|
||
|
|
def linear(self, b):
|
||
|
|
self._linear = bool(b)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def drag_objects(self):
|
||
|
|
return self.markers
|
||
|
|
|
||
|
|
def check_dependency(self):
|
||
|
|
if any([m._delete for m in self.markers]):
|
||
|
|
self.delete()
|
||
|
|
|
||
|
|
|
||
|
|
class Edit(object):
|
||
|
|
|
||
|
|
def __init__(self, obj=None):
|
||
|
|
self.obj = obj
|
||
|
|
self.root_inserted = False
|
||
|
|
self.root = None
|
||
|
|
|
||
|
|
self.editing = None
|
||
|
|
|
||
|
|
# event callbacks
|
||
|
|
self.selection_callback = None
|
||
|
|
self._keyPressedCB = None
|
||
|
|
self._mouseMovedCB = None
|
||
|
|
self._mousePressedCB = None
|
||
|
|
|
||
|
|
# Setup coin objects
|
||
|
|
self.guidoc = self.obj.ViewObject.Document
|
||
|
|
self.view = self.guidoc.ActiveView
|
||
|
|
self.rm = self.view.getViewer().getSoRenderManager()
|
||
|
|
self.sg = self.view.getSceneGraph()
|
||
|
|
self.setupInteractionSeparator()
|
||
|
|
|
||
|
|
# Callbacks
|
||
|
|
self.unregister_editing_callbacks()
|
||
|
|
self.register_editing_callbacks()
|
||
|
|
|
||
|
|
def setupInteractionSeparator(self):
|
||
|
|
if self.root_inserted:
|
||
|
|
self.sg.removeChild(self.root)
|
||
|
|
self.root = graphics.InteractionSeparator(self.rm)
|
||
|
|
self.root.setName("InteractionSeparator")
|
||
|
|
self.root.pick_radius = 40
|
||
|
|
|
||
|
|
# Populate root node
|
||
|
|
#self.root += self.points
|
||
|
|
#self.build_lines()
|
||
|
|
#self.root += self.lines
|
||
|
|
|
||
|
|
# set FreeCAD color scheme
|
||
|
|
'''for o in self.points + self.lines:
|
||
|
|
o.ovr_col = "yellow"
|
||
|
|
o.sel_col = "green"'''
|
||
|
|
|
||
|
|
self.root.register()
|
||
|
|
self.sg.addChild(self.root)
|
||
|
|
self.root_inserted = True
|
||
|
|
self.root.selected_objects = list()
|
||
|
|
|
||
|
|
def build_lines(self):
|
||
|
|
for i in range(len(self.points) - 1):
|
||
|
|
line = ConnectionLine([self.points[i], self.points[i + 1]])
|
||
|
|
line.set_color("blue")
|
||
|
|
self.lines.append(line)
|
||
|
|
|
||
|
|
# -------------------------------------------------------------------------
|
||
|
|
# SCENE EVENTS CALLBACKS
|
||
|
|
# -------------------------------------------------------------------------
|
||
|
|
|
||
|
|
def register_editing_callbacks(self):
|
||
|
|
""" Register editing callbacks (former action function) """
|
||
|
|
|
||
|
|
print("Registering callbacks")
|
||
|
|
|
||
|
|
if self._keyPressedCB is None:
|
||
|
|
self._keyPressedCB = self.root.events.addEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self.keyPressed)
|
||
|
|
|
||
|
|
if self._mousePressedCB is None:
|
||
|
|
self._mousePressedCB = self.root.events.addEventCallback(coin.SoMouseButtonEvent.getClassTypeId(), self.mousePressed)
|
||
|
|
|
||
|
|
if self._mouseMovedCB is None:
|
||
|
|
self._mouseMovedCB = self.root.events.addEventCallback(coin.SoLocation2Event.getClassTypeId(), self.mouseMoved)
|
||
|
|
|
||
|
|
def unregister_editing_callbacks(self):
|
||
|
|
""" Remove callbacks used during editing if they exist """
|
||
|
|
|
||
|
|
print("Unregistering")
|
||
|
|
|
||
|
|
if self._keyPressedCB:
|
||
|
|
self.root.events.removeEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self._keyPressedCB)
|
||
|
|
self._keyPressedCB = None
|
||
|
|
|
||
|
|
if self._mousePressedCB:
|
||
|
|
self.root.events.removeEventCallback(coin.SoMouseButtonEvent.getClassTypeId(), self._mousePressedCB)
|
||
|
|
self._mousePressedCB = None
|
||
|
|
|
||
|
|
if self._mouseMovedCB:
|
||
|
|
self.root.events.removeEventCallback(coin.SoLocation2Event.getClassTypeId(), self._mouseMovedCB)
|
||
|
|
self._mouseMovedCB = None
|
||
|
|
|
||
|
|
# -------------------------------------------------------------------------
|
||
|
|
# SCENE EVENT HANDLERS
|
||
|
|
# -------------------------------------------------------------------------
|
||
|
|
|
||
|
|
def keyPressed(self, attr, event_callback):
|
||
|
|
event = event_callback.getEvent()
|
||
|
|
print(event.getKey())
|
||
|
|
if event.getState() == event.UP:
|
||
|
|
#FreeCAD.Console.PrintMessage("Key pressed : %s\n"%event.getKey())
|
||
|
|
if event.getKey() == ord("i"):
|
||
|
|
self.subdivide()
|
||
|
|
elif event.getKey() == ord("q"):# or event.getKey() == ord(65307):
|
||
|
|
if self.obj:
|
||
|
|
self.obj.ViewObject.Proxy.doubleClicked(self.obj.ViewObject)
|
||
|
|
else:
|
||
|
|
self.quit()
|
||
|
|
elif event.getKey() == ord("s"):
|
||
|
|
sel = FreeCADGui.Selection.getSelectionEx()
|
||
|
|
tup = None
|
||
|
|
if len(sel) == 1:
|
||
|
|
tup = (sel[0].Object, sel[0].SubElementNames)
|
||
|
|
for i in range(len(self.root.selected_objects)):
|
||
|
|
if isinstance(self.root.selected_objects[i], MarkerOnShape):
|
||
|
|
self.root.selected_objects[i].sublink = tup
|
||
|
|
#FreeCAD.Console.PrintMessage("Snapped to {}\n".format(str(self.root.selected_objects[i].sublink)))
|
||
|
|
self.root.selected_objects[i].drag_start()
|
||
|
|
self.root.selected_objects[i].drag((0, 0, 0.))
|
||
|
|
self.root.selected_objects[i].drag_release()
|
||
|
|
elif (event.getKey() == 65535) or (event.getKey() == 65288): # Suppr or Backspace
|
||
|
|
# FreeCAD.Console.PrintMessage("Some objects have been deleted\n")
|
||
|
|
pts = list()
|
||
|
|
for o in self.root.dynamic_objects:
|
||
|
|
if isinstance(o, MarkerOnShape):
|
||
|
|
pts.append(o)
|
||
|
|
self.points = pts
|
||
|
|
self.setupInteractionSeparator()
|
||
|
|
elif event.getKey() == 65307:
|
||
|
|
self.quit()
|
||
|
|
|
||
|
|
def mousePressed(self, attr, event_callback):
|
||
|
|
""" Mouse button event handler, calls: startEditing, endEditing, addPoint, delPoint """
|
||
|
|
event = event_callback.getEvent()
|
||
|
|
if (event.getState() == coin.SoMouseButtonEvent.DOWN) and (event.getButton() == event.BUTTON1): # left click
|
||
|
|
print("Mouse button down and mouse button 1")
|
||
|
|
if not event.wasAltDown():
|
||
|
|
if self.editing is None:
|
||
|
|
''' do something'''
|
||
|
|
else:
|
||
|
|
self.endEditing(self.obj, self.editing)
|
||
|
|
|
||
|
|
elif event.wasAltDown(): # left click with ctrl down
|
||
|
|
self.display_tracker_menu(event)
|
||
|
|
|
||
|
|
elif (event.getState() == coin.SoMouseButtonEvent.DOWN) and (event.getButton() == event.BUTTON2): # right click
|
||
|
|
self.display_tracker_menu(event)
|
||
|
|
|
||
|
|
def mouseMoved(self, attr, event_callback):
|
||
|
|
""" Execute as callback for mouse movement. Update tracker position and update preview ghost. """
|
||
|
|
|
||
|
|
event = event_callback.getEvent()
|
||
|
|
pos = event.getPosition()
|
||
|
|
listObjects = FreeCADGui.ActiveDocument.ActiveView.getObjectsInfo((int(pos[0]), int(pos[1])))
|
||
|
|
if listObjects:
|
||
|
|
#{'x': 483239.09375, 'y': 783855.8125, 'z': 797695.0, 'Document': 'Salinas_II_GRG___v3',
|
||
|
|
# 'Object': 'Wire023', 'Component': 'Edge30'}]
|
||
|
|
for obj in listObjects:
|
||
|
|
print(listObjects)
|
||
|
|
if obj.Object == self.obj.Name:
|
||
|
|
print(listObjects)
|
||
|
|
|
||
|
|
'''
|
||
|
|
if self.editing is not None:
|
||
|
|
self.updateTrackerAndGhost(event)
|
||
|
|
else:
|
||
|
|
# look for a node in mouse position and highlight it
|
||
|
|
pos = event.getPosition()
|
||
|
|
node = self.getEditNode(pos)
|
||
|
|
ep = self.getEditNodeIndex(node)
|
||
|
|
if ep is not None:
|
||
|
|
if self.overNode is not None:
|
||
|
|
self.overNode.setColor(COLORS["default"])
|
||
|
|
self.trackers[str(node.objectName.getValue())][ep].setColor(COLORS["red"])
|
||
|
|
self.overNode = self.trackers[str(node.objectName.getValue())][ep]
|
||
|
|
print("show menu")
|
||
|
|
# self.display_tracker_menu(event)
|
||
|
|
else:
|
||
|
|
if self.overNode is not None:
|
||
|
|
self.overNode.setColor(COLORS["default"])
|
||
|
|
self.overNode = None
|
||
|
|
'''
|
||
|
|
|
||
|
|
def endEditing(self, obj, nodeIndex=None, v=None):
|
||
|
|
self.editing = None
|
||
|
|
|
||
|
|
|
||
|
|
# ------------------------------------------------------------------------
|
||
|
|
# DRAFT EDIT Context menu
|
||
|
|
# ------------------------------------------------------------------------
|
||
|
|
|
||
|
|
def display_tracker_menu(self, event):
|
||
|
|
self.tracker_menu = QtGui.QMenu()
|
||
|
|
self.event = event
|
||
|
|
actions = None
|
||
|
|
actions = ["add point"]
|
||
|
|
|
||
|
|
'''
|
||
|
|
if self.overNode:
|
||
|
|
# if user is over a node
|
||
|
|
doc = self.overNode.get_doc_name()
|
||
|
|
obj = App.getDocument(doc).getObject(self.overNode.get_obj_name())
|
||
|
|
ep = self.overNode.get_subelement_index()
|
||
|
|
|
||
|
|
obj_gui_tools = self.get_obj_gui_tools(obj)
|
||
|
|
if obj_gui_tools:
|
||
|
|
actions = obj_gui_tools.get_edit_point_context_menu(obj, ep)
|
||
|
|
|
||
|
|
else:
|
||
|
|
# try if user is over an edited object
|
||
|
|
pos = self.event.getPosition()
|
||
|
|
obj = self.get_selected_obj_at_position(pos)
|
||
|
|
if utils.get_type(obj) in ["Line", "Wire", "BSpline", "BezCurve"]:
|
||
|
|
actions = ["add point"]
|
||
|
|
elif utils.get_type(obj) in ["Circle"] and obj.FirstAngle != obj.LastAngle:
|
||
|
|
actions = ["invert arc"]
|
||
|
|
|
||
|
|
if actions is None:
|
||
|
|
return
|
||
|
|
'''
|
||
|
|
for a in actions:
|
||
|
|
self.tracker_menu.addAction(a)
|
||
|
|
|
||
|
|
self.tracker_menu.popup(FreeCADGui.getMainWindow().cursor().pos())
|
||
|
|
QtCore.QObject.connect(self.tracker_menu,
|
||
|
|
QtCore.SIGNAL("triggered(QAction *)"),
|
||
|
|
self.evaluate_menu_action)
|
||
|
|
|
||
|
|
def evaluate_menu_action(self, labelname):
|
||
|
|
action_label = str(labelname.text())
|
||
|
|
|
||
|
|
doc = None
|
||
|
|
obj = None
|
||
|
|
idx = None
|
||
|
|
|
||
|
|
if action_label == "add point":
|
||
|
|
self.addPoint(self.event)
|
||
|
|
|
||
|
|
del self.event
|
||
|
|
|
||
|
|
# -------------------------------------------------------------------------
|
||
|
|
# EDIT functions
|
||
|
|
# -------------------------------------------------------------------------
|
||
|
|
|
||
|
|
def addPoint(self, event):
|
||
|
|
''' add point to the end '''
|
||
|
|
pos = event.getPosition()
|
||
|
|
pts = self.points.copy()
|
||
|
|
new_select = list()
|
||
|
|
point = FreeCAD.Vector(pos)
|
||
|
|
mark = MarkerOnShape([point])
|
||
|
|
pts.append(mark)
|
||
|
|
new_select.append(mark)
|
||
|
|
self.points = pts
|
||
|
|
self.setupInteractionSeparator()
|
||
|
|
self.root.selected_objects = new_select
|
||
|
|
|
||
|
|
def subdivide(self):
|
||
|
|
# get selected lines and subdivide them
|
||
|
|
pts = list()
|
||
|
|
new_select = list()
|
||
|
|
for o in self.lines:
|
||
|
|
#FreeCAD.Console.PrintMessage("object %s\n"%str(o))
|
||
|
|
if isinstance(o, ConnectionLine):
|
||
|
|
pts.append(o.markers[0])
|
||
|
|
if o in self.root.selected_objects:
|
||
|
|
#idx = self.lines.index(o)
|
||
|
|
#FreeCAD.Console.PrintMessage("Subdividing line #{}\n".format(idx))
|
||
|
|
p1 = o.markers[0].points[0]
|
||
|
|
p2 = o.markers[1].points[0]
|
||
|
|
midpar = (FreeCAD.Vector(p1) + FreeCAD.Vector(p2)) / 2.0
|
||
|
|
mark = MarkerOnShape([midpar])
|
||
|
|
pts.append(mark)
|
||
|
|
new_select.append(mark)
|
||
|
|
pts.append(self.points[-1])
|
||
|
|
self.points = pts
|
||
|
|
self.setupInteractionSeparator()
|
||
|
|
self.root.selected_objects = new_select
|
||
|
|
return True
|
||
|
|
|
||
|
|
def quit(self):
|
||
|
|
print("Quit")
|
||
|
|
self.unregister_editing_callbacks()
|
||
|
|
self.root.unregister()
|
||
|
|
#self.sg.removeChild(self.root)
|
||
|
|
self.root_inserted = False
|