diff --git a/src/ifcopenshell-python/ifcopenshell/geom/app.py b/src/ifcopenshell-python/ifcopenshell/geom/app.py
index 4f3bafca07..f7cbd3d87c 100644
--- a/src/ifcopenshell-python/ifcopenshell/geom/app.py
+++ b/src/ifcopenshell-python/ifcopenshell/geom/app.py
@@ -1,5 +1,6 @@
from __future__ import print_function
+import os
import sys
import time
import operator
@@ -7,10 +8,17 @@ import functools
import OCC.AIS
-from collections import defaultdict, Iterable
+from collections import defaultdict, Iterable, OrderedDict
+
+os.environ['QT_API'] = 'pyqt4'
+try:
+ from pyqode.qt import QtCore
+except: pass
from PyQt4 import QtGui, QtCore
+from .code_editor_pane import code_edit
+
try: from OCC.Display.pyqt4Display import qtViewer3d
except:
import OCC.Display
@@ -27,13 +35,73 @@ except:
from .main import settings, iterator
from .occ_utils import display_shape
-from .. import open, get_supertype
+from .. import open as open_ifc_file
+from .. import get_supertype
# Depending on Python version and what not there may or may not be a QString
try:
from PyQt4.QtCore import QString
except ImportError:
QString = str
+
+class configuration(object):
+ def __init__(self):
+ try:
+ import ConfigParser
+ Cfg = ConfigParser.RawConfigParser
+ except:
+ import configparser
+ Cfg = configparser.ConfigParser(interpolation=None)
+
+ conf_file = os.path.expanduser(os.path.join("~", ".ifcopenshell", "app", "snippets.conf"))
+ if conf_file.startswith("~"):
+ conf_file = None
+ return
+
+ self.config_encode = lambda s: s.replace("\\", "\\\\").replace("\n", "\n|")
+ self.config_decode = lambda s: s.replace("\n|", "\n").replace("\\\\", "\\")
+
+ if not os.path.exists(os.path.dirname(conf_file)):
+ os.makedirs(os.path.dirname(conf_file))
+
+ if not os.path.exists(conf_file):
+ config = Cfg()
+ config.add_section("snippets")
+ config.set("snippets", "print all wall ids", self.config_encode("""
+###########################################################################
+# A simple script that iterates over all walls in the current model #
+# and prints their Globally unique IDs (GUIDS) to the console window #
+###########################################################################
+
+for wall in model.by_type("IfcWall"):
+ print ("wall with global id: "+str(wall.GlobalId))
+""".lstrip()))
+
+ config.set("snippets", "print properties of current selection", self.config_encode("""
+###########################################################################
+# A simple script that iterates over all IfcPropertySets of the currently #
+# selected object and prints them to the console #
+###########################################################################
+
+# check if something is selected
+if selection:
+ #get the IfcProduct that is stored in the global variable 'selection'
+ obj = selection
+ for relDefinesByProperties in obj.IsDefinedBy:
+ print("[{0}]".format(relDefinesByProperties.RelatingPropertyDefinition.Name))
+ for prop in relDefinesByProperties.RelatingPropertyDefinition.HasProperties:
+ print ("{:<20} :{}".format(prop.Name,prop.NominalValue.wrappedValue))
+ print ("\\n")
+""".lstrip()))
+ with open(conf_file, 'w') as configfile:
+ config.write(configfile)
+
+ self.config = Cfg()
+ self.config.read(conf_file)
+
+ def options(self, s):
+ return OrderedDict([(k, self.config_decode(self.config.get(s, k))) for k in self.config.options(s)])
+
class application(QtGui.QApplication):
@@ -168,7 +236,7 @@ class application(QtGui.QApplication):
class property_table(QtGui.QWidget):
- instanceSelected = QtCore.pyqtSignal([object])
+
def __init__(self):
QtGui.QWidget.__init__(self)
self.layout= QtGui.QVBoxLayout(self)
@@ -184,39 +252,49 @@ class application(QtGui.QApplication):
#triggered by selection event in either component of parent
def select(self, product):
+
# Clear the old contents if any
while self.scrollLayout.count():
child = self.scrollLayout.takeAt(0)
if child is not None:
if child.widget() is not None:
child.widget().deleteLater()
+
self.scroll = QtGui.QScrollArea()
self.scroll.setWidgetResizable(True)
- scrollContent = QtGui.QWidget(self.scroll)
- # print ("properties for selection {}".format(self.prop_dict.get(str(product))))
prop_sets = self.prop_dict.get(str(product))
+
if prop_sets is not None:
- for k,v in prop_sets.items():
+ for k,v in prop_sets:
group_box = QtGui.QGroupBox()
group_box.setTitle(k)
group_layout = QtGui.QVBoxLayout()
-
group_box.setLayout(group_layout)
+
for name, value in v.items():
prop_name = str(name)
- value_str = value.wrappedValue
- if isinstance(value_str,unicode):
+
+ value_str = value
+ if hasattr(value_str, "wrappedValue"):
+ value_str = value_str.wrappedValue
+
+ if isinstance(value_str, unicode):
value_str = value_str.encode('utf-8')
else:
value_str = str(value_str)
- # print (value_str, type(value_str))
- type_str = value.is_a()
- label = QtGui.QLabel(prop_name+" : "+value_str+" : "+type_str)
+
+ if hasattr(value, "is_a"):
+ type_str = " (%s)" % value.is_a()
+ else:
+ type_str = ""
+ label = QtGui.QLabel("%s: %s%s" % (prop_name, value_str, type_str))
group_layout.addWidget(label)
+
group_layout.addStretch()
self.scrollLayout.addWidget(group_box)
+
self.scrollLayout.addStretch()
else:
label = QtGui.QLabel("No IfcPropertySets asscociated with selected entity instance" )
@@ -224,31 +302,46 @@ class application(QtGui.QApplication):
def load_file(self, f, **kwargs):
- products = list(f.by_type("IfcProduct"))
- for p in products:
- propset_dict={}
+ for p in f.by_type("IfcProduct"):
+ propsets = []
+
+ def process_pset(prop_def):
+ if prop_def is not None:
+ prop_set_name = prop_def.Name
+ props = {}
+ if prop_def.is_a("IfcElementQuantity"):
+ for q in prop_def.Quantities:
+ if q.is_a("IfcPhysicalSimpleQuantity"):
+ props[q.Name]=q[3]
+ elif prop_def.is_a("IfcPropertySet"):
+ for prop in prop_def.HasProperties:
+ if prop.is_a("IfcPropertySingleValue"):
+ props[prop.Name]=prop.NominalValue
+ else:
+ # Entity introduced in IFC4
+ # prop_def.is_a("IfcPreDefinedPropertySet"):
+ for prop in range(4, len(prop_def)):
+ props[prop_def.attribute_name(prop)]=prop_def[prop]
+ return prop_set_name, props
+
try:
for is_def_by in p.IsDefinedBy:
- if not is_def_by.is_a("IfcRelDefinesByProperties"): continue
- if is_def_by.RelatingPropertyDefinition is not None:
- prop_def=is_def_by.RelatingPropertyDefinition
- prop_set_name = prop_def.Name
- props = {}
- if prop_def.is_a("IfcElementQuantity"):
- for q in prop_def.Quantities:
- if q.is_a("IfcPhysicalSimpleQuantity"):
- props[q.Name]=q[3]
- else:
- for prop in prop_def.HasProperties:
- if prop.is_a("IfcPropertySingleValue"):
- props[prop.Name]=prop.NominalValue
-
- propset_dict[prop_set_name]=props
-
+ if is_def_by.is_a("IfcRelDefinesByProperties"):
+ propsets.append(process_pset(is_def_by.RelatingPropertyDefinition))
+ elif is_def_by.is_a("IfcRelDefinesByType"):
+ type_psets = is_def_by.RelatingType.HasPropertySets
+ if type_psets is None: continue
+ for propset in type_psets:
+ propsets.append(process_pset(propset))
except Exception, e:
+ import traceback
print("failed to load properties: {}".format(e))
- self.prop_dict[str(p)]=propset_dict
- print ("property set dictionary has {} entries".format(len(propset_dict)))
+ traceback.print_exc()
+
+ if len(propsets):
+ self.prop_dict[str(p)] = propsets
+
+ print ("property set dictionary has {} entries".format(len(self.prop_dict)))
class viewer(qtViewer3d):
@@ -436,11 +529,16 @@ class application(QtGui.QApplication):
self.tabs.addTab(self.tree, 'Decomposition')
self.tabs.addTab(self.tree2, 'Types')
self.tabs.addTab(self.propview, "Properties")
- splitter.addWidget(self.canvas)
+ splitter2 = QtGui.QSplitter(QtCore.Qt.Vertical)
+ splitter2.addWidget(self.canvas)
+ self.editor = code_edit(self.canvas, configuration().options('snippets'))
+ splitter2.addWidget(self.editor)
+ splitter.addWidget(splitter2)
splitter.setSizes([200,600])
+ splitter2.setSizes([400,200])
self.window.setCentralWidget(splitter)
self.canvas.initialize()
- self.components = [self.tree, self.tree2, self.canvas, self.propview]
+ self.components = [self.tree, self.tree2, self.canvas, self.propview, self.editor]
self.files = {}
self.window.add_menu_item('File', '&Open', self.browse, shortcut='CTRL+O')
@@ -450,7 +548,6 @@ class application(QtGui.QApplication):
self.tree.instanceSelected.connect(self.makeSelectionHandler(self.tree))
self.tree2.instanceSelected.connect(self.makeSelectionHandler(self.tree2))
self.canvas.instanceSelected.connect(self.makeSelectionHandler(self.canvas))
- self.propview.instanceSelected.connect(self.makeSelectionHandler(self.propview))
for t in [self.tree, self.tree2]:
t.instanceVisibilityChanged.connect(functools.partial(self.change_visibility, t))
t.instanceDisplayModeChanged.connect(functools.partial(self.change_displaymode, t))
@@ -480,7 +577,10 @@ class application(QtGui.QApplication):
def load(self, fn):
if fn in self.files: return
- f = open(str(fn))
+ f = open_ifc_file(str(fn))
self.files[fn] = f
for c in self.components:
c.load_file(f, setting=self.settings)
+
+if __name__ == "__main__":
+ application().start()
diff --git a/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py b/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py
index f8f4fa994c..70594470cc 100644
--- a/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py
+++ b/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py
@@ -1,162 +1,152 @@
+from __future__ import print_function
+
import os
import sys
import logging
-from pyqode.core.panels import CheckerPanel
-logging.basicConfig(level=logging.CRITICAL)
-from pyqode.qt import QtCore, QtGui, QtWidgets
-print('Qt version:%s' % QtCore.__version__)
from code import InteractiveConsole
-from pyqode.core import api
-from pyqode.core import modes
-from pyqode.core import panels
-from pyqode.core.api import CodeEdit, ColorScheme
-from pyqode.python.modes import PyAutoIndentMode, PythonSH
-from pyqode.python.backend import server
-from pyqode.python import modes as pymodes, panels as pypanels, widgets
-from pyqode.python.widgets import PyInteractiveConsole
+from PyQt4 import QtCore, QtGui
+try:
+ from PyQt4 import QtWidgets
+except:
+ QtWidgets = QtGui
+try:
+ from pyqode.core.panels import CheckerPanel
+ from pyqode.core import api
+ from pyqode.core import modes
+ from pyqode.core import panels
+ from pyqode.core.api import CodeEdit, ColorScheme
+ from pyqode.python.modes import PyAutoIndentMode, PythonSH
+ from pyqode.python.backend import server
+ from pyqode.python import modes as pymodes, panels as pypanels, widgets
+ from pyqode.python.widgets import PyInteractiveConsole
+ has_pyqode = True
+except:
+ has_pyqode = False
+ CodeEdit = QtWidgets.QPlainTextEdit
+
+
+class StdoutRedirector(object):
-class StdoutRedirector(QtGui.QTextEdit):
'''A class for redirecting stdout to this Text widget.'''
+
def __init__(self, widget):
self.widget=widget
self.isError = False
+
def write(self,str):
- # print(str)
- # self.widget.append(str)
self.widget.moveCursor(QtGui.QTextCursor.End)
if self.isError:
self.widget.setTextColor(QtCore.Qt.red)
else:
- self.widget.setTextColor(QtCore.Qt.black)
+ self.widget.setTextColor(QtCore.Qt.white)
self.widget.insertPlainText(str)
self.widget.moveCursor(QtGui.QTextCursor.End)
- # scroll = self.widget.verticalScrollBar()
- # scroll.setValue(scroll.maximum())
class code_edit(QtGui.QWidget):
- class Console(InteractiveConsole):
- def __init__(*args): InteractiveConsole.__init__(*args)
+ class Console(InteractiveConsole):
+ def __init__(*args):
+ InteractiveConsole.__init__(*args)
def enter(self, source):
- source = self.preprocess(source)
self.runcode(source)
- @staticmethod
- def preprocess(source): return source
- def runCode(self):
- c =self.Console({'model':self.files[0]})
- c.enter("print (model.by_type('IfcDoor')")
-
def runCode(self):
-
sys.stdout = StdoutRedirector(self.output)
- err_redirect= StdoutRedirector(self.output)
- err_redirect.isError=True
- sys.stderr = err_redirect
+ sys.stderr = StdoutRedirector(self.output)
+ sys.stderr.isError=True
if not self.model:
- print("please load a model first")
- if not self.c:
- self.c = self.Console({'model':self.model, 'viewer':self.viewer})
+ print("please load a model first", file=sys.stderr)
else:
- # self.console.start_process(sys.executable,args= [ os.path.join(os.getcwd(), 'interactive_process.py')],env={'model':self.model})
self.c.enter(str(self.editor.toPlainText()))
+
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
def select(self,product):
self.c = self.Console({'model':self.model, 'viewer':self.viewer, 'selection':product})
- self.c.enter
def __init__(self,viewer,snippets=None):
-
- logging.basicConfig(level=logging.CRITICAL)
+
self.model=None
self.viewer = viewer
QtGui.QWidget.__init__(self)
self.layout= QtGui.QVBoxLayout(self)
self.setLayout(self.layout)
self.c = None
-
-
+
+ self.tools = QtGui.QHBoxLayout(self)
+ self.layout.addLayout(self.tools)
+
+ self.runbutton = QtGui.QPushButton("Run")
+ width = self.runbutton.fontMetrics().boundingRect("Run").width() + 20
+ self.runbutton.setMaximumWidth(width)
+ self.tools.addWidget(self.runbutton)
+ self.runbutton.clicked.connect(self.runCode)
+
editor = CodeEdit()
-
- # start the backend as soon as possible
- # editor.backend.start('server.py')
- editor.backend.start(server.__file__)
- #--- core panels
- editor.panels.append(panels.FoldingPanel())
- editor.panels.append(panels.LineNumberPanel())
- # editor.panels.append(panels.CheckerPanel())
- editor.panels.append(panels.SearchAndReplacePanel(),
- panels.SearchAndReplacePanel.Position.BOTTOM)
- editor.panels.append(panels.EncodingPanel(), api.Panel.Position.TOP)
- # add a context menu separator between editor's
- # builtin action and the python specific actions
- editor.add_separator()
-
- #--- python specific panels
- editor.panels.append(pypanels.QuickDocPanel(), api.Panel.Position.BOTTOM)
- sh = editor.modes.append(PythonSH(editor.document()))
- # sh.color_scheme = ColorScheme('monokai')
- #--- core modes
- editor.modes.append(modes.CaretLineHighlighterMode())
- editor.modes.append(modes.CodeCompletionMode())
- editor.modes.append(modes.ExtendedSelectionMode())
- editor.modes.append(modes.FileWatcherMode())
- editor.modes.append(modes.OccurrencesHighlighterMode())
- editor.modes.append(modes.RightMarginMode())
- editor.modes.append(modes.SmartBackSpaceMode())
- editor.modes.append(modes.SymbolMatcherMode())
- editor.modes.append(modes.ZoomMode())
-
- #--- python specific modes
- editor.modes.append(pymodes.CommentsMode())
- editor.modes.append(pymodes.CalltipsMode())
- auto = pymodes.PyAutoCompleteMode()
- auto.logger.setLevel(logging.CRITICAL)
- editor.modes.append(auto)
- editor.modes.append(pymodes.PyAutoIndentMode())
- editor.modes.append(pymodes.PyIndenterMode())
- editor.show()
-
+ if has_pyqode:
+ editor.backend.start(server.__file__)
+ editor.panels.append(panels.FoldingPanel())
+ editor.panels.append(panels.LineNumberPanel())
+ editor.panels.append(panels.SearchAndReplacePanel(),
+ panels.SearchAndReplacePanel.Position.BOTTOM)
+ editor.panels.append(panels.EncodingPanel(), api.Panel.Position.TOP)
+ editor.add_separator()
+ editor.panels.append(pypanels.QuickDocPanel(), api.Panel.Position.BOTTOM)
+ sh = editor.modes.append(PythonSH(editor.document()))
+ editor.modes.append(modes.CaretLineHighlighterMode())
+ editor.modes.append(modes.CodeCompletionMode())
+ editor.modes.append(modes.ExtendedSelectionMode())
+ editor.modes.append(modes.FileWatcherMode())
+ editor.modes.append(modes.OccurrencesHighlighterMode())
+ editor.modes.append(modes.RightMarginMode())
+ editor.modes.append(modes.SmartBackSpaceMode())
+ editor.modes.append(modes.SymbolMatcherMode())
+ editor.modes.append(modes.ZoomMode())
+ editor.modes.append(pymodes.CommentsMode())
+ editor.modes.append(pymodes.CalltipsMode())
+ auto = pymodes.PyAutoCompleteMode()
+ auto.logger.setLevel(logging.CRITICAL)
+ editor.modes.append(auto)
+ editor.modes.append(pymodes.PyAutoIndentMode())
+ editor.modes.append(pymodes.PyIndenterMode())
+ editor.show()
+ else:
+ editor.setStyleSheet('font-size: 10pt; font-family: Consolas, Courier;')
self.editor = editor
self.snippets = snippets
- if not self.snippets:
- self.editor.setPlainText("""print('hello from console')
-for w in model.by_type('IfcWall'):
- print ("wall with GlobalId " + str(w.GlobalId))
- ""","","")
- else:
- self.editor.setPlainText(self.snippets.values()[0],"","")
+ if self.snippets:
self.list = QtWidgets.QComboBox(self)
+ self.replace_snippet(0)
for snip_name in self.snippets.keys():
self.list.addItem(snip_name)
- self.layout.addWidget(self.list)
+ self.tools.addWidget(self.list)
QtCore.QObject.connect(self.list, QtCore.SIGNAL("currentIndexChanged(int)"), self.replace_snippet)
- # self.textedit = QtGui.QTextEdit()
- # self.scrollLayout.addWidget(self.textedit)
self.layout.addWidget(self.editor)
self.output = QtGui.QTextEdit()
- self.output.setReadOnly(False)
- self.output.setStyleSheet('font-size: 10pt; font-family: Consolas,Courier;')
+ self.output.setReadOnly(True)
+ self.output.setStyleSheet('font-size: 10pt; font-family: Consolas, Courier; background-color: #444;')
self.layout.addWidget(self.output)
- def replace_snippet (self,number):
- self.editor.setPlainText(self.snippets[self.list.currentText()],"","")
+ def replace_snippet(self, number=None):
+ snip = list(self.snippets.values())[number]
+ if has_pyqode:
+ self.editor.setPlainText(snip, "", "")
+ else:
+ self.editor.setPlainText(snip)
def load_file(self, f, **kwargs):
output = []
sys.stdout = StdoutRedirector(self.output)
self.model = f
- self.c = self.Console({'model':self.model})
- # c.enter("print (model.by_type('IfcWall'))")
- sys.stdout = sys.__stdout__
\ No newline at end of file
+ self.c = self.Console({'model':self.model, 'selection':None, 'viewer':self.viewer})
+ sys.stdout = sys.__stdout__
diff --git a/src/ifcopenshell-python/ifcopenshell/test/snippets.config b/src/ifcopenshell-python/ifcopenshell/test/snippets.config
deleted file mode 100644
index ef6d3e2699..0000000000
--- a/src/ifcopenshell-python/ifcopenshell/test/snippets.config
+++ /dev/null
@@ -1,25 +0,0 @@
-
-quickload_model = "D:\Project1.ifc"
-
-["snippets"]
-'print all wall ids' = """# a simple script that iterates over all walls in the current
-# model and prints their Globally unique IDs (GUIDS) to the console
-# window
-
-for wall in model.by_type("IfcWall"):
- print ("wall with global id: "+str(wall.GlobalId))
-"""
-'print all properties of current selection' = """# a simple script that iterates over all IfcPropertySets of the currently
-# selected object and prints them to the console
-#####################################################################
-
-# check if something is selected
-if selection:
- #get the IfcProduct that is stored in the global variable 'selection'
- obj = selection
- for relDefinesByProperties in obj.IsDefinedBy:
- print("[{0}]".format(relDefinesByProperties.RelatingPropertyDefinition.Name))
- for prop in relDefinesByProperties.RelatingPropertyDefinition.HasProperties:
- print ("{:<20} :{}".format(prop.Name,prop.NominalValue.wrappedValue))
- print ("\n")
-"""
diff --git a/src/ifcopenshell-python/ifcopenshell/test/viewer_scripting_test.py b/src/ifcopenshell-python/ifcopenshell/test/viewer_scripting_test.py
deleted file mode 100644
index df6f61de35..0000000000
--- a/src/ifcopenshell-python/ifcopenshell/test/viewer_scripting_test.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from pyqode.qt import QtCore, QtGui, QtWidgets
-from configobj import ConfigObj
-
-from ..geom.app import application
-try:
- from ..geom.code_editor_pane import code_edit
-except ImportError:
- print ("code editor or one of its dependencies (pyQode) could not be found. Please make sure to install pyQode")
-class my_app(application):
-
- class toolbar(QtGui.QToolBar):
- def __init__(self):
- QtGui.QToolBar.__init__(self)
- self.widget.append()
-
- def __init__(self):
- application.__init__(self)
- # self.window = my_app.window()
- self.window.setWindowTitle("Interactive IfcOpenShell Viewer with REPL-like funcionality")
- tb = self.window.addToolBar("File")
- self.window.resize(1024,720)
- # loadButton = QtGui.QAction(QtGui.QIcon("new.bmp"),"new",self)
- loadButton = QtGui.QAction("Quickload",self)
- loadButton.triggered.connect(self.loadExampleFile)
- loadButton.setShortcut(QtGui.QKeySequence("CTRL+L"))
- tb.addAction(loadButton)
- # zoomAllButton = QtGui.QAction(QtGui.QIcon("new.bmp"),"new",self)
- zoomAllButton = QtGui.QAction("zoom All",self)
- zoomAllButton.triggered.connect(self.zoomAll)
- tb.addAction(zoomAllButton)
- self.config = ConfigObj("snippets.config")
- print (self.config["snippets"])
-
- snippets = self.config["snippets"]
-
- self.codeedit= code_edit(self.canvas, snippets )
-
- self.components.append(self.codeedit)
- self.window.centralWidget().addWidget(self.codeedit)
- self.runButton = QtGui.QAction("execute code (Strg+P)",self)
- self.runButton.setShortcut(QtGui.QKeySequence("CTRL+P"))
- self.runButton.triggered.connect(self.codeedit.runCode)
-
- tb.addAction(self.runButton)
- def loadExampleFile(self):
- self.load(self.config["quickload_model"])
- def zoomAll(self):
- self.canvas._display.FitAll()
-
-
-my_app().start()
\ No newline at end of file