Exemple #1
0
    def loadedItem(self, fn, obj):
        if fn in self.active:
            print("klampt_browser: Re-loaded item", fn,
                  "so I'm first removing it")
            self.remove(fn)
        assert fn not in self.active
        item = ResourceItem(obj)
        self.active[fn] = item
        item.plugin = GLVisualizationPlugin()
        basename = os.path.basename(fn)

        #determine whether it's being animated
        if isinstance(obj, Trajectory) and len(obj.milestones) > 0:
            d = len(obj.milestones[0])
            if self.world.numRobots() > 0 and d == self.world.robot(
                    0).numLinks():
                obj = RobotTrajectory(self.world.robot(0), obj.times,
                                      obj.milestones)
                robotpath = ('world', self.world.robot(0).getName())
                item.animationBuddy = robotpath
            elif d == 3:
                item.plugin.add("anim_point", [0, 0, 0])
                item.animationBuddy = "anim_point"
            elif d == 12:
                item.plugin.add("anim_xform", se3.identity())
                item.animationBuddy = "anim_xform"
            else:
                print("klampt_browser: Can't interpret trajectory of length",
                      d)
        elif isinstance(obj, MultiPath):
            if self.world.numRobots() > 0:
                robotpath = ('world', self.world.robot(0).getName())
                item.animationBuddy = robotpath

        item.plugin.add("world", self.world)
        item.plugin.add(basename, obj)
        item.plugin.addText("label", basename, position=(10, 10))
        try:
            type = vis.objectToVisType(obj, self.world)
        except:
            type = 'unknown'
        if type in robot_override_types:
            if self.world.numRobots() > 0:
                path = ('world', self.world.robot(0).getName())
                item.plugin.hide(path)
        item.plugin.initialize()
Exemple #2
0
    def __init__(self, glwindow=None, parent=None):
        QtWidgets.QMainWindow.__init__(self, parent)
        # Splitter to show 2 views in same widget easily.
        self.splitter = QtWidgets.QSplitter()
        # The model.
        self.model = QtWidgets.QFileSystemModel()
        # You can setRootPath to any path.
        self.model.setRootPath(QtCore.QDir.rootPath())
        # Add filters
        filters = []
        print("ALLOWABLE FILE EXTENSIONS")
        for k, v in loader.extensionToTypes.items():
            filters.append("*" + k)
            print(" ", k)
        filters.append("*.xml")
        filters.append("*.json")
        filters.append("*.txt")
        filters.append("*.obj")
        filters.append("*.rob")
        filters.append("*.urdf")
        filters.append("*.env")
        self.model.setNameFilters(filters)

        # Create the view in the splitter.
        self.view = QtWidgets.QTreeView()
        # Set the model of the view.
        self.view.setModel(self.model)
        #nicer size for columns
        self.view.header().resizeSection(0, 200)
        self.view.header().resizeSection(1, 75)
        self.view.header().resizeSection(2, 75)
        self.view.header().resizeSection(3, 150)
        # Set the root index of the view as the user's home directory.
        #self.view.setRootIndex(self.model.index(QtCore.QDir.homePath()))
        self.view.setRootIndex(self.model.index(os.getcwd()))
        self.view.setSelectionMode(
            QtWidgets.QAbstractItemView.ExtendedSelection)

        self.world = WorldModel()
        self.tempWorld = WorldModel()
        self.active = dict()
        self.emptyVisPlugin = GLVisualizationPlugin()
        self.emptyVisPlugin.add("world", self.world)
        self.emptyVisProgram = None
        self.selected = set()
        self.visCache = []
        self.modified = set()

        self.left = QtWidgets.QFrame()
        self.right = QtWidgets.QFrame()
        self.leftLayout = QtWidgets.QVBoxLayout()
        self.left.setLayout(self.leftLayout)

        self.upButton = QtWidgets.QPushButton("Up")
        self.leftLayout.addWidget(self.upButton)
        self.leftLayout.addWidget(self.view)
        #visualization configuration
        vbuttonLayout = QtWidgets.QHBoxLayout()
        self.autoFitCameraButton = QtWidgets.QCheckBox("Auto-fit cameras")
        self.lockCameraCheck = QtWidgets.QCheckBox("Lock cameras")
        #self.overlayCheck = QtWidgets.QCheckBox("Overlay")
        self.maxGridItemsLabel = QtWidgets.QLabel("Grid width")
        self.maxGridItems = QtWidgets.QSpinBox()
        self.maxGridItems.setRange(3, 15)
        self.autoFitCameraButton.setToolTip(
            "If checked, the camera is automatically fit the the items in the scene"
        )
        self.lockCameraCheck.setToolTip(
            "If checked, all cameras are navigated simultaneously")
        #self.overlayCheck.setTooltip("If checked, all items are drawn on top of one another")
        self.maxGridItems.setToolTip(
            "Max # height/width in the visualization pane")
        vbuttonLayout.addWidget(self.autoFitCameraButton)
        vbuttonLayout.addWidget(self.lockCameraCheck)
        #vbuttonLayout.addWidget(self.overlayCheck)
        vbuttonLayout.addWidget(self.maxGridItemsLabel)
        vbuttonLayout.addWidget(self.maxGridItems)
        self.leftLayout.addLayout(vbuttonLayout)

        #playback
        self.timeDriver = QtWidgets.QSlider()
        self.timeDriver.setOrientation(QtCore.Qt.Horizontal)
        self.timeDriver.setRange(0, 1000)
        #self.timeDriver.setSizeHint()
        self.playButton = QtWidgets.QPushButton("Play")
        self.playButton.setCheckable(True)
        self.stopButton = QtWidgets.QPushButton("Stop")
        self.playButton.setToolTip(
            "Starts/pauses playing any selected animations")
        self.stopButton.setToolTip("Stops playing any selected animations")
        label = QtWidgets.QLabel("Time")
        label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        vbuttonLayout = QtWidgets.QHBoxLayout()
        vbuttonLayout.addWidget(label)
        vbuttonLayout.addWidget(self.timeDriver)
        vbuttonLayout.addWidget(self.playButton)
        vbuttonLayout.addWidget(self.stopButton)
        self.leftLayout.addLayout(vbuttonLayout)

        #editing
        vbuttonLayout = QtWidgets.QHBoxLayout()
        self.editButton = QtWidgets.QPushButton("Edit item")
        self.saveButton = QtWidgets.QPushButton("Save item")
        self.editButton.setToolTip(
            "Pops up a dialog to edit the selected item, if available")
        self.saveButton.setToolTip("Saves changes to edited items")
        self.saveButton.setEnabled(False)
        self.createComboBox = QtWidgets.QComboBox()
        self.createComboBox.addItem("Create new item...")
        for n in create_types:
            self.createComboBox.addItem(n)
        vbuttonLayout.addWidget(self.editButton)
        vbuttonLayout.addWidget(self.saveButton)
        vbuttonLayout.addWidget(self.createComboBox)
        self.leftLayout.addLayout(vbuttonLayout)

        #world configuration
        vbuttonLayout = QtWidgets.QHBoxLayout()
        self.addButton = QtWidgets.QPushButton("Add to world")
        self.clearButton = QtWidgets.QPushButton("Clear world")
        self.addButton.setToolTip(
            "Adds the selected item(s) to the reference world")
        self.clearButton.setToolTip("Clears the reference world")
        vbuttonLayout.addWidget(self.addButton)
        vbuttonLayout.addWidget(self.clearButton)
        self.leftLayout.addLayout(vbuttonLayout)
        self.splitter.addWidget(self.left)
        self.splitter.addWidget(self.right)
        self.splitter.setHandleWidth(7)
        self.setCentralWidget(self.splitter)

        self.rightLayout = QtWidgets.QVBoxLayout()
        self.right.setLayout(self.rightLayout)
        if glwindow is None:
            self.glwidget = QtGLWindow("viewport")
        else:
            self.glwidget = glwindow
        self.rightLayout.addWidget(self.glwidget)
        self.glviewportManager = MyMultiViewportProgram()
        self.glwidget.setProgram(self.glviewportManager)
        self.glwidget.setParent(self.splitter)
        self.glviewportManager.sizePolicy = 'squeeze'
        self.glviewportManager.addView(self.emptyVisPlugin)
        self.glviewportManager.items = self.active
        self.emptyVisProgram = self.glviewportManager.views[-1]
        self.glwidget.setFixedSize(QtWidgets.QWIDGETSIZE_MAX,
                                   QtWidgets.QWIDGETSIZE_MAX)
        self.glwidget.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
                                  QtWidgets.QSizePolicy.Expanding))
        self.glwidget.adjustSize()
        self.glwidget.refresh()

        self.upButton.clicked.connect(self.onUpClicked)
        self.view.selectionModel().selectionChanged.connect(
            self.selection_changed)
        self.view.doubleClicked.connect(self.onViewDoubleClick)
        self.autoFitCameraButton.clicked.connect(self.onAutoFitCamera)
        self.maxGridItems.valueChanged.connect(self.maxGridItemsChanged)
        self.lockCameraCheck.toggled.connect(self.onLockCamerasToggled)
        self.timeDriver.valueChanged.connect(self.timeDriverChanged)
        self.playButton.toggled.connect(self.togglePlay)
        self.stopButton.clicked.connect(self.stopPlay)
        self.editButton.clicked.connect(self.onEditClicked)
        self.saveButton.clicked.connect(self.onSaveClicked)
        self.createComboBox.currentIndexChanged.connect(
            self.onCreateIndexChanged)
        self.addButton.clicked.connect(self.onAddClicked)
        self.clearButton.clicked.connect(self.onClearClicked)
Exemple #3
0
class ResourceBrowser(QtWidgets.QMainWindow):
    def __init__(self, glwindow=None, parent=None):
        QtWidgets.QMainWindow.__init__(self, parent)
        # Splitter to show 2 views in same widget easily.
        self.splitter = QtWidgets.QSplitter()
        # The model.
        self.model = QtWidgets.QFileSystemModel()
        # You can setRootPath to any path.
        self.model.setRootPath(QtCore.QDir.rootPath())
        # Add filters
        filters = []
        print("ALLOWABLE FILE EXTENSIONS")
        for k, v in loader.extensionToTypes.items():
            filters.append("*" + k)
            print(" ", k)
        filters.append("*.xml")
        filters.append("*.json")
        filters.append("*.txt")
        filters.append("*.obj")
        filters.append("*.rob")
        filters.append("*.urdf")
        filters.append("*.env")
        self.model.setNameFilters(filters)

        # Create the view in the splitter.
        self.view = QtWidgets.QTreeView()
        # Set the model of the view.
        self.view.setModel(self.model)
        #nicer size for columns
        self.view.header().resizeSection(0, 200)
        self.view.header().resizeSection(1, 75)
        self.view.header().resizeSection(2, 75)
        self.view.header().resizeSection(3, 150)
        # Set the root index of the view as the user's home directory.
        #self.view.setRootIndex(self.model.index(QtCore.QDir.homePath()))
        self.view.setRootIndex(self.model.index(os.getcwd()))
        self.view.setSelectionMode(
            QtWidgets.QAbstractItemView.ExtendedSelection)

        self.world = WorldModel()
        self.tempWorld = WorldModel()
        self.active = dict()
        self.emptyVisPlugin = GLVisualizationPlugin()
        self.emptyVisPlugin.add("world", self.world)
        self.emptyVisProgram = None
        self.selected = set()
        self.visCache = []
        self.modified = set()

        self.left = QtWidgets.QFrame()
        self.right = QtWidgets.QFrame()
        self.leftLayout = QtWidgets.QVBoxLayout()
        self.left.setLayout(self.leftLayout)

        self.upButton = QtWidgets.QPushButton("Up")
        self.leftLayout.addWidget(self.upButton)
        self.leftLayout.addWidget(self.view)
        #visualization configuration
        vbuttonLayout = QtWidgets.QHBoxLayout()
        self.autoFitCameraButton = QtWidgets.QCheckBox("Auto-fit cameras")
        self.lockCameraCheck = QtWidgets.QCheckBox("Lock cameras")
        #self.overlayCheck = QtWidgets.QCheckBox("Overlay")
        self.maxGridItemsLabel = QtWidgets.QLabel("Grid width")
        self.maxGridItems = QtWidgets.QSpinBox()
        self.maxGridItems.setRange(3, 15)
        self.autoFitCameraButton.setToolTip(
            "If checked, the camera is automatically fit the the items in the scene"
        )
        self.lockCameraCheck.setToolTip(
            "If checked, all cameras are navigated simultaneously")
        #self.overlayCheck.setTooltip("If checked, all items are drawn on top of one another")
        self.maxGridItems.setToolTip(
            "Max # height/width in the visualization pane")
        vbuttonLayout.addWidget(self.autoFitCameraButton)
        vbuttonLayout.addWidget(self.lockCameraCheck)
        #vbuttonLayout.addWidget(self.overlayCheck)
        vbuttonLayout.addWidget(self.maxGridItemsLabel)
        vbuttonLayout.addWidget(self.maxGridItems)
        self.leftLayout.addLayout(vbuttonLayout)

        #playback
        self.timeDriver = QtWidgets.QSlider()
        self.timeDriver.setOrientation(QtCore.Qt.Horizontal)
        self.timeDriver.setRange(0, 1000)
        #self.timeDriver.setSizeHint()
        self.playButton = QtWidgets.QPushButton("Play")
        self.playButton.setCheckable(True)
        self.stopButton = QtWidgets.QPushButton("Stop")
        self.playButton.setToolTip(
            "Starts/pauses playing any selected animations")
        self.stopButton.setToolTip("Stops playing any selected animations")
        label = QtWidgets.QLabel("Time")
        label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        vbuttonLayout = QtWidgets.QHBoxLayout()
        vbuttonLayout.addWidget(label)
        vbuttonLayout.addWidget(self.timeDriver)
        vbuttonLayout.addWidget(self.playButton)
        vbuttonLayout.addWidget(self.stopButton)
        self.leftLayout.addLayout(vbuttonLayout)

        #editing
        vbuttonLayout = QtWidgets.QHBoxLayout()
        self.editButton = QtWidgets.QPushButton("Edit item")
        self.saveButton = QtWidgets.QPushButton("Save item")
        self.editButton.setToolTip(
            "Pops up a dialog to edit the selected item, if available")
        self.saveButton.setToolTip("Saves changes to edited items")
        self.saveButton.setEnabled(False)
        self.createComboBox = QtWidgets.QComboBox()
        self.createComboBox.addItem("Create new item...")
        for n in create_types:
            self.createComboBox.addItem(n)
        vbuttonLayout.addWidget(self.editButton)
        vbuttonLayout.addWidget(self.saveButton)
        vbuttonLayout.addWidget(self.createComboBox)
        self.leftLayout.addLayout(vbuttonLayout)

        #world configuration
        vbuttonLayout = QtWidgets.QHBoxLayout()
        self.addButton = QtWidgets.QPushButton("Add to world")
        self.clearButton = QtWidgets.QPushButton("Clear world")
        self.addButton.setToolTip(
            "Adds the selected item(s) to the reference world")
        self.clearButton.setToolTip("Clears the reference world")
        vbuttonLayout.addWidget(self.addButton)
        vbuttonLayout.addWidget(self.clearButton)
        self.leftLayout.addLayout(vbuttonLayout)
        self.splitter.addWidget(self.left)
        self.splitter.addWidget(self.right)
        self.splitter.setHandleWidth(7)
        self.setCentralWidget(self.splitter)

        self.rightLayout = QtWidgets.QVBoxLayout()
        self.right.setLayout(self.rightLayout)
        if glwindow is None:
            self.glwidget = QtGLWindow("viewport")
        else:
            self.glwidget = glwindow
        self.rightLayout.addWidget(self.glwidget)
        self.glviewportManager = MyMultiViewportProgram()
        self.glwidget.setProgram(self.glviewportManager)
        self.glwidget.setParent(self.splitter)
        self.glviewportManager.sizePolicy = 'squeeze'
        self.glviewportManager.addView(self.emptyVisPlugin)
        self.glviewportManager.items = self.active
        self.emptyVisProgram = self.glviewportManager.views[-1]
        self.glwidget.setFixedSize(QtWidgets.QWIDGETSIZE_MAX,
                                   QtWidgets.QWIDGETSIZE_MAX)
        self.glwidget.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
                                  QtWidgets.QSizePolicy.Expanding))
        self.glwidget.adjustSize()
        self.glwidget.refresh()

        self.upButton.clicked.connect(self.onUpClicked)
        self.view.selectionModel().selectionChanged.connect(
            self.selection_changed)
        self.view.doubleClicked.connect(self.onViewDoubleClick)
        self.autoFitCameraButton.clicked.connect(self.onAutoFitCamera)
        self.maxGridItems.valueChanged.connect(self.maxGridItemsChanged)
        self.lockCameraCheck.toggled.connect(self.onLockCamerasToggled)
        self.timeDriver.valueChanged.connect(self.timeDriverChanged)
        self.playButton.toggled.connect(self.togglePlay)
        self.stopButton.clicked.connect(self.stopPlay)
        self.editButton.clicked.connect(self.onEditClicked)
        self.saveButton.clicked.connect(self.onSaveClicked)
        self.createComboBox.currentIndexChanged.connect(
            self.onCreateIndexChanged)
        self.addButton.clicked.connect(self.onAddClicked)
        self.clearButton.clicked.connect(self.onClearClicked)

    def closeEvent(self, event):
        if len(self.modified) > 0:
            reply = QtWidgets.QMessageBox.question(
                self, "Unsaved changes", "Would you like to save changes to " +
                ', '.join(self.modified) + "?",
                QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
            if reply == QtWidgets.QMessageBox.Yes:
                self.onSaveClicked()
        vis.show(False)

    def onViewDoubleClick(self):
        indices = self.view.selectedIndexes()
        if len(indices) == 0: return
        item = indices[0]
        name = str(self.model.filePath(item))
        oldselected = self.selected
        self.selected = set([name])
        self.onAddClicked()
        self.selected = oldselected

    def onUpClicked(self):
        currentRoot = self.view.rootIndex()
        self.view.setRootIndex(currentRoot.parent())

    def selection_changed(self, newSelection, deselected):
        #print "klampt_browser: Selection changed!"
        for i in newSelection.indexes():
            if i.column() == 0:
                fn = str(i.model().filePath(i))
                self.selected.add(fn)
                self.add(fn)
                #print "  value:",fn
        #print "klampt_browser: Deselected:"
        for i in deselected.indexes():
            if i.column() == 0:
                fn = str(i.model().filePath(i))
                self.selected.remove(fn)
                self.remove(fn)
                #klampt_browser: print "  value:",fn
        self.refresh()

    def onAutoFitCamera(self):
        if self.autoFitCameraButton.isChecked():
            for (k, item) in self.active.items():
                vis.autoFitViewport(item.program.view, [self.world, item.obj])

    def onLockCamerasToggled(self, on):
        self.glviewportManager.broadcast = on
        if on:
            self.lockCameras()

    def lockCameras(self):
        view0 = self.glviewportManager.views[0].view
        cam0 = view0.camera
        for p in self.glviewportManager.views[1:]:
            cam = p.view.camera
            copyCamera(cam0, cam)

    def timeDriverChanged(self, value):
        u = value * 0.001
        animTrajectoryTime = u * self.glviewportManager.animationDuration
        for (k, item) in self.active.items():
            obj = item.obj
            plugin = item.plugin
            if item.animationBuddy is not None:
                path = item.animationBuddy
                if plugin.getItem(path).animation is None:
                    plugin.animate(path, obj, endBehavior='halt')
                else:
                    anim = plugin.getItem(path).animation
            plugin.pauseAnimation(False)
        self.glviewportManager.setAnimTime(animTrajectoryTime)

    def togglePlay(self, value):
        self.animating = value
        self.glviewportManager.refresh()
        if value:
            for (k, item) in self.active.items():
                obj = item.obj
                plugin = item.plugin
                if item.animationBuddy is not None:
                    plugin.animate(item.animationBuddy,
                                   obj,
                                   endBehavior='halt')
                plugin.pauseAnimation(False)
            self.glviewportManager.startAnim()
            self.glviewportManager.refresh()
        else:
            #pause
            self.glviewportManager.stopAnim()
            #self.idlesleep(float('inf'))

    def stopPlay(self):
        self.playButton.setChecked(False)
        #revert to no animations
        for (k, item) in self.active.items():
            obj = item.obj
            plugin = item.plugin
            if isinstance(obj, (Trajectory, MultiPath)):
                robotpath = ('world', self.world.robot(0).getName())
                plugin.animate(robotpath, None)
            plugin.pauseAnimation()

    def onEditClicked(self):
        if len(self.active) == 0:
            QtWidgets.QMessageBox.warning(self.splitter, "Invalid item",
                                          "No item selected, can't edit")
            return
        fn = sorted(self.active.keys())[0]

        def doedit():
            print("klampt_browser: Launching resource.edit", fn, "...")
            try:
                (save, obj) = resource.edit(name=fn,
                                            value=self.active[fn].obj,
                                            world=self.world)
            except Exception as e:
                print("klampt_browser: Exception raised during resource.edit:",
                      e)
                QtWidgets.QMessageBox.warning(
                    self.splitter, "Editing not available",
                    "Unable to edit item of type " +
                    self.active[fn].obj.__class__.__name__)
                return
            if save and obj is not None:
                self.active[fn].obj = obj
                #mark it as modified and re-add it to the visualization
                basename = os.path.basename(fn)
                self.active[fn].plugin.add(basename, obj)
                self.modified.add(fn)
                self.saveButton.setEnabled(True)

        QtCore.QTimer.singleShot(0, doedit)

    def onSaveClicked(self):
        for fn in self.modified:
            if not save(self.active[fn].obj, fn):
                print("klampt_browser: Error saving file", fn)
        self.modified = set()
        self.saveButton.setEnabled(False)

    def onCreateIndexChanged(self, item):
        if item == 0: return
        type = create_types[item - 1]
        robot = None
        if self.world.numRobots() > 0:
            robot = self.world.robot(0)
        try:
            (save, obj) = resource.edit("untitled",
                                        types.make(type, robot),
                                        type=type,
                                        world=self.world)
        except Exception as e:
            print("klampt_browser: Exception raised during resource.edit():",
                  e)
            QtWidgets.QMessageBox.warning(
                self.splitter, "Creation not available",
                "Unable to create item of type " + type +
                ", did you remember to add items to the reference world?")
            return
        if obj is not None and save:
            fn = resource.save(obj, type, directory='')
            if fn is not None:
                self.loadedItem(fn, obj)
                #TODO: should we add to selection in tree view?
        self.createComboBox.setCurrentIndex(0)

    def onAddClicked(self):
        if self.world.numIDs() == 0:
            for name in self.selected:
                if name not in self.active: continue
                copyCamera(self.active[name].program.view.camera,
                           self.emptyVisProgram.view.camera)
                break
        todel = []
        for name in self.selected:
            if name not in self.active: continue
            s = self.active[name].obj
            if isinstance(s, (RobotModel, RigidObjectModel, TerrainModel)):
                self.world.add(s.getName(), s)
                self.tempWorld.remove(s)
                todel.append(name)
            elif isinstance(s, WorldModel):
                for i in range(s.numRobots()):
                    self.world.add(s.robot(i).getName(), s.robot(i))
                for i in range(s.numRigidObjects()):
                    self.world.add(
                        s.rigidObject(i).getName(), s.rigidObject(i))
                for i in range(s.numTerrains()):
                    self.world.add(s.terrain(i).getName(), s.terrain(i))
                for k, item in self.active.items():
                    item.plugin.add("world", self.world)
                todel.append(name)
            elif isinstance(s, (TriangleMesh, PointCloud, GeometricPrimitive)):
                t = self.world.makeTerrain(name)
                t.geometry().set(Geometry3D(s))
                todel.append(name)
            elif isinstance(s, Geometry3D):
                t = self.world.makeTerrain(name)
                t.geometry().set(s.clone())
                todel.append(name)
        for name in todel:
            self.remove(name)
        if len(todel) > 0:
            self.refresh()

    def onClearClicked(self):
        self.world = WorldModel()
        self.tempWorld = WorldModel()
        self.active = dict()
        self.visCache = []
        self.emptyVisPlugin.add("world", self.world)
        self.refresh()

    def add(self, fn, openDir=True, warn=True):
        #assert fn not in self.active
        if fn in self.active:
            print("add(): Warning, file", fn, "is already active")
            return
        for i, (cfn, citem) in enumerate(self.visCache):
            if cfn == fn:
                print()
                print("klampt_browser: PULLED", fn, "FROM CACHE")
                print()
                self.active[fn] = citem
                return True
        if len(self.active) >= MAX_VIS_ITEMS:
            return
        if os.path.isdir(fn):
            if openDir:
                failures = []
                successes = []
                for f in os.listdir(fn):
                    if f not in ['.', '..'] and os.path.splitext(f)[1] != '':
                        if not self.add(os.path.join(fn, f),
                                        openDir=False,
                                        warn=False):
                            failures.append(f)
                        else:
                            successes.append(f)
                if len(failures) != 0 and len(successes) != 0:
                    QtWidgets.QMessageBox.warning(
                        self.splitter, "Invalid items",
                        "Could not load files " + ', '.join(failures) +
                        " as Klamp't elements")
                return True
            else:
                return False
        path, ext = os.path.splitext(fn)
        #print "Extension is",ext
        if ext in world_item_extensions:
            try:
                worldid = self.tempWorld.loadElement(fn)
            except Exception:
                if warn:
                    QtWidgets.QMessageBox.warning(
                        self.splitter, "Invalid item",
                        "Could not load " + fn + " as a Klamp't world element")
                return False
            if worldid < 0:
                if warn:
                    QtWidgets.QMessageBox.warning(
                        self.splitter, "Invalid item",
                        "Could not load " + fn + " as a Klamp't world element")
                return False
            obj = None
            for i in range(self.tempWorld.numRobots()):
                if self.tempWorld.robot(i).getID() == worldid:
                    obj = self.tempWorld.robot(i)
                    break
            for i in range(self.tempWorld.numRigidObjects()):
                if self.tempWorld.rigidObject(i).getID() == worldid:
                    obj = self.tempWorld.rigidObject(i)
                    break
            for i in range(self.tempWorld.numTerrains()):
                if self.tempWorld.terrain(i).getID() == worldid:
                    obj = self.tempWorld.terrain(i)
                    break
            assert obj is not None, "Hmm... couldn't find world id %d in world?" % (
                worldid, )
            self.loadedItem(fn, obj)
            return True
        try:
            type = loader.filenameToType(fn)
        except RuntimeError:
            if warn:
                QtWidgets.QMessageBox.warning(
                    self.splitter, "Invalid item",
                    "Could not load file " + fn + " as a known Klamp't type")
            return False
        if type == 'xml':
            #try loading a world
            try:
                world = WorldModel()
                res = world.readFile(fn)
                if not res:
                    try:
                        obj = loader.load('MultiPath', fn)
                    except Exception as e:
                        if warn:
                            print(
                                "klampt_browser: Trying MultiPath load, got exception",
                                e)
                            import traceback
                            traceback.print_exc()
                            QtWidgets.QMessageBox.warning(
                                self.splitter, "Invalid WorldModel",
                                "Could not load " + fn +
                                " as a world XML file")
                        return False
                    self.loadedItem(fn, obj)
                    return True
            except IOError:
                if warn:
                    QtWidgets.QMessageBox.warning(
                        self.splitter, "Invalid WorldModel",
                        "Could not load " + fn + " as a world XML file")
                return False
            self.loadedItem(fn, world)
            return
        elif type == 'json':
            import json
            f = open(fn, 'r')
            jsonobj = json.load(f)
            try:
                obj = loader.fromJson(jsonobj)
            except Exception:
                if warn:
                    QtWidgets.QMessageBox.warning(
                        self.splitter, "Invalid JSON", "Could not recognize " +
                        fn + " as a known Klamp't type")
                return False
        else:
            try:
                obj = loader.load(type, fn)
            except Exception as e:
                if warn:
                    QtWidgets.QMessageBox.warning(
                        self.splitter, "Invalid item",
                        "Error while loading file " + fn + ": " + str(e))
                return False
        self.loadedItem(fn, obj)
        return True

    def loadedItem(self, fn, obj):
        if fn in self.active:
            print("klampt_browser: Re-loaded item", fn,
                  "so I'm first removing it")
            self.remove(fn)
        assert fn not in self.active
        item = ResourceItem(obj)
        self.active[fn] = item
        item.plugin = GLVisualizationPlugin()
        basename = os.path.basename(fn)

        #determine whether it's being animated
        if isinstance(obj, Trajectory) and len(obj.milestones) > 0:
            d = len(obj.milestones[0])
            if self.world.numRobots() > 0 and d == self.world.robot(
                    0).numLinks():
                obj = RobotTrajectory(self.world.robot(0), obj.times,
                                      obj.milestones)
                robotpath = ('world', self.world.robot(0).getName())
                item.animationBuddy = robotpath
            elif d == 3:
                item.plugin.add("anim_point", [0, 0, 0])
                item.animationBuddy = "anim_point"
            elif d == 12:
                item.plugin.add("anim_xform", se3.identity())
                item.animationBuddy = "anim_xform"
            else:
                print("klampt_browser: Can't interpret trajectory of length",
                      d)
        elif isinstance(obj, MultiPath):
            if self.world.numRobots() > 0:
                robotpath = ('world', self.world.robot(0).getName())
                item.animationBuddy = robotpath

        item.plugin.add("world", self.world)
        item.plugin.add(basename, obj)
        item.plugin.addText("label", basename, position=(10, 10))
        try:
            type = vis.objectToVisType(obj, self.world)
        except:
            type = 'unknown'
        if type in robot_override_types:
            if self.world.numRobots() > 0:
                path = ('world', self.world.robot(0).getName())
                item.plugin.hide(path)
        item.plugin.initialize()

    def remove(self, fn, openDir=True):
        if os.path.isdir(fn):
            if openDir:
                for f in os.listdir(fn):
                    if f not in ['.', '..'] and os.path.splitext(f)[1] != '':
                        self.remove(os.path.join(fn, f), openDir=False)
            return
        if fn not in self.active:
            return
        if fn in self.modified:
            reply = QtWidgets.QMessageBox.question(
                self, "Unsaved changes", "Would you like to save changes to " +
                ', '.join(self.modified) + "?",
                QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
            if reply == QtWidgets.QMessageBox.Yes:
                save(self.active[fn], fn)
                self.modified.remove(fn)
        s = self.active[fn]
        del self.active[fn]
        if s.program is not None:
            copyCamera(s.program.view.camera, self.emptyVisProgram.view.camera)
        print()
        print("klampt_browser: ADDING", fn, "TO CACHE")
        print()
        self.visCache.append((fn, s))
        if len(self.visCache) > MAX_VIS_CACHE:
            self.visCache.pop(0)
        cleartemp = isinstance(s.obj,
                               (RobotModel, RigidObjectModel, TerrainModel))
        if cleartemp:
            for (k, v) in self.active.items():
                if isinstance(v.obj,
                              (RobotModel, RigidObjectModel, TerrainModel)):
                    cleartemp = False
                    break
        if cleartemp:
            if self.tempWorld.numRobots() + self.tempWorld.numRigidObjects(
            ) + self.tempWorld.numTerrains() > 10:
                print("klampt_browser: Clearing temp world...")
                self.tempWorld = WorldModel()
                self.visCache = [
                    (fn, s) for (fn, s) in self.visCache
                    if not isinstance(s.obj, (RobotModel, RigidObjectModel,
                                              TerrainModel))
                ]

    def maxGridItemsChanged(self):
        self.refresh()

    def refresh(self):
        self.glviewportManager.clearViews()
        if len(self.active) == 0:
            self.glviewportManager.addView(self.emptyVisProgram)
        else:
            for k in sorted(self.active.keys()):
                item = self.active[k]
                if item.program is not None:
                    item.program.view.w, item.program.view.h = (640, 480)
                    self.glviewportManager.addView(item.program)
                else:
                    #new view
                    self.glviewportManager.addView(item.plugin)
                    item.program = self.glviewportManager.views[-1]
                    if self.autoFitCameraButton.isChecked():
                        vis.autoFitViewport(item.program.view,
                                            [self.world, item.obj])
                    else:
                        copyCamera(self.emptyVisProgram.view.camera,
                                   item.program.view.camera)
                if len(self.glviewportManager.views
                       ) >= self.maxGridItems.value()**2:
                    break
            if self.glviewportManager.broadcast:  #locking cameras
                self.lockCameras()
        self.glviewportManager.animationDuration = 0
        for (k, item) in self.active.items():
            obj = item.obj
            if isinstance(obj, (Trajectory, MultiPath)):
                self.glviewportManager.animationDuration = max(
                    self.glviewportManager.animationDuration, obj.duration())
                print("klampt_browser: Setting animation duration to",
                      self.glviewportManager.animationDuration)
        self.glviewportManager.refresh()