-
Notifications
You must be signed in to change notification settings - Fork 9
7. Throwing together the Tree Widget and the 3D View
Based on the basic ifc_treeviewer.py
and the qt3d_minimal.py
examples, we can throw them together, as-is, into a more complete viewer. Still basic, but combining the visualisation of the Spatial Tree and the Geometry.
We start with almost the same code as all the previous examples, but this time, we define a new Widget class called QIFCViewer
.
from Qt3D_minimal import *
from ifc_treeviewer import *
# Our Main function
def main():
app = 0
if QApplication.instance():
app = QApplication.instance()
else:
app = QApplication(sys.argv)
w = QIFCViewer()
w.resize(1280, 800)
filename = sys.argv[1]
if os.path.isfile(filename):
w.load_file(filename)
w.setWindowTitle("IFC Viewer - " + filename)
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
The QIFCViewer
is derived from a QMainWindow
which will give us a series of features more oriented towards an actual application, with a menu, toolbars, statusbar and docking widgets. It is given a reference to an IFC-file (self.ifc_file
), initially a local variable set to None
. The main()
method sends a filename
(the full path of the file, from the arguments when calling the script), which will then be loaded.
class QIFCViewer(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.ifc_file = None
def load_file(self, filename):
self.ifc_file = ifcopenshell.open(filename)
You can run this code, but it will be quite boring... Just an empty window.
We have already included the import statements for our two widgets. Now we can further prepare them in the __init__
method.
def __init__(self):
QMainWindow.__init__(self)
self.ifc_file = None
self.view_3d = View3D()
self.view_tree = ViewTree()
The Tree will be placed in a QDockWidget
which will allow us to move the widget around our interface or even hide it completely. Notice that in the constructor, we give it a name and parent it to our main window. Initially it will not be floating and we dock it to the left of the main window.
self.dock = QDockWidget('Model Tree', self)
self.dock.setWidget(self.view_tree)
self.dock.setFloating(False)
self.addDockWidget(Qt.LeftDockWidgetArea, self.dock)
And finally, we define the 3D view as the central widget.
self.setCentralWidget(self.view_3d)
Now at least there is something to see, albeit not that much. A complete application window, with a dock widget containing our (still empty) tree and the 3D view, also empty. But in the back, our model has been loaded already.
All that we have to do now, is load our file into the two widgets. And if you test the code now, you have a complete viewer, with a model tree and the geometry displayed in a 3D window, ready to navigate.
def load_file(self, filename):
self.ifc_file = ifcopenshell.open(filename)
self.view_3d.load_file(filename)
self.view_tree.load_file(filename)
But wait... Didn't we just load the model three times? Yes we did. This could be improved.
We will load the model only once and then assign the loaded model as a reference to the different widgets, so they can all collect information from it. This requires a slight adjustment in their respective load_file()
methods.
We can solve it like this, and immediately measure the time it took to load the model. Be sure to add an import time
statement to recognise this class.
import time
[...]
def load_file(self, filename):
if self.ifc_file is None:
print("Importing ", filename)
start = time.time()
self.ifc_file = ifcopenshell.open(filename)
print("Loaded ", filename, " in ", time.time() - start, " seconds")
So our new strategy is that we load the model once in the main window and then simply assign the ifc_file
variable to the other widgets. We set it directly, but we should probably use a dedicated setter function for that. And if we now call the load_file()
method in our widgets, the actual file is loaded only once. Not the prettiest code, but we can still refine it later.
class QIFCViewer(QMainWindow):
def load_file(self, filename):
[...]
self.view_3d.ifc_file = self.ifc_file
self.view_3d.load_file(filename)
self.view_tree.ifc_file = self.ifc_file
self.view_tree.load_file(filename)
With just a minor change, we can replace the ifc_treeviewer.py
with the improved version in ifc_treeviewer2.py
. Don't forget to also include the additional test for the self.ifc_file
checking, to avoid loading the model several times.
from Qt3D_minimal import *
from ifc_treeviewer2 import *
And this is the result:
A big thanks to the IfcOpenShell library and the many people contributing with code, but also examples.