more testing of pyside6

This commit is contained in:
jdegenstein
2023-10-11 12:11:37 -05:00
parent 8fca86e2c9
commit 960c9fe5ca
13 changed files with 1090 additions and 1035 deletions

View File

@@ -1,23 +1,23 @@
from PySide6.QtWidgets import QApplication
from PySide6.QtCore import pyqtSlot
from PySide6.QtCore import Slot as pyqtSlot
from qtconsole.rich_jupyter_widget import RichJupyterWidget
from qtconsole.inprocess import QtInProcessKernelManager
from ..mixins import ComponentMixin
class ConsoleWidget(RichJupyterWidget,ComponentMixin):
name = 'Console'
class ConsoleWidget(RichJupyterWidget, ComponentMixin):
name = "Console"
def __init__(self, customBanner=None, namespace=dict(), *args, **kwargs):
super(ConsoleWidget, self).__init__(*args, **kwargs)
# if not customBanner is None:
# self.banner = customBanner
# if not customBanner is None:
# self.banner = customBanner
self.font_size = 6
self.style_sheet = '''<style>
self.style_sheet = """<style>
QPlainTextEdit, QTextEdit {
background-color: #3f3f3f;
background-clip: padding;
@@ -34,14 +34,14 @@ class ConsoleWidget(RichJupyterWidget,ComponentMixin):
.in-prompt { color: navy; }
.out-prompt { color: darkred; }
</style>
'''
self.syntax_style = 'zenburn' #CHANGES FOR DARKMODE
"""
self.syntax_style = "zenburn" # CHANGES FOR DARKMODE
self.kernel_manager = kernel_manager = QtInProcessKernelManager()
kernel_manager.start_kernel(show_banner=False)
kernel_manager.kernel.gui = 'qt'
kernel_manager.kernel.gui = "qt"
kernel_manager.kernel.shell.banner1 = ""
self.kernel_client = kernel_client = self._kernel_manager.client()
kernel_client.start_channels()
@@ -51,9 +51,9 @@ class ConsoleWidget(RichJupyterWidget,ComponentMixin):
QApplication.instance().exit()
self.exit_requested.connect(stop)
self.clear()
self.push_vars(namespace)
@pyqtSlot(dict)
@@ -70,7 +70,6 @@ class ConsoleWidget(RichJupyterWidget,ComponentMixin):
"""
self._control.clear()
def print_text(self, text):
"""
Prints some plain text to the console
@@ -82,20 +81,17 @@ class ConsoleWidget(RichJupyterWidget,ComponentMixin):
Execute a command in the frame of the console widget
"""
self._execute(command, False)
def _banner_default(self):
return ''
def _banner_default(self):
return ""
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
console = ConsoleWidget(customBanner='IPython console test')
console = ConsoleWidget(customBanner="IPython console test")
console.show()
sys.exit(app.exec_())

View File

@@ -1,6 +1,6 @@
from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem, QAction
from PySide6.QtCore import Qt, pyqtSlot, pyqtSignal
from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem
from PySide6.QtCore import Qt, Slot as pyqtSlot, Signal as pyqtSignal
from PySide6.QtGui import QAction
from OCP.AIS import AIS_ColoredShape
from OCP.gp import gp_Ax3
@@ -10,63 +10,62 @@ from ..mixins import ComponentMixin
from ..icons import icon
class CQChildItem(QTreeWidgetItem):
def __init__(self,cq_item,**kwargs):
super(CQChildItem,self).\
__init__([type(cq_item).__name__,str(cq_item)],**kwargs)
def __init__(self, cq_item, **kwargs):
super(CQChildItem, self).__init__(
[type(cq_item).__name__, str(cq_item)], **kwargs
)
self.cq_item = cq_item
class CQStackItem(QTreeWidgetItem):
def __init__(self,name,workplane=None,**kwargs):
super(CQStackItem,self).__init__([name,''],**kwargs)
def __init__(self, name, workplane=None, **kwargs):
super(CQStackItem, self).__init__([name, ""], **kwargs)
self.workplane = workplane
class CQObjectInspector(QTreeWidget,ComponentMixin):
name = 'CQ Object Inspector'
class CQObjectInspector(QTreeWidget, ComponentMixin):
name = "CQ Object Inspector"
sigRemoveObjects = pyqtSignal(list)
sigDisplayObjects = pyqtSignal(list,bool)
sigShowPlane = pyqtSignal([bool],[bool,float])
sigDisplayObjects = pyqtSignal(list, bool)
sigShowPlane = pyqtSignal(bool)
sigShowPlane2 = pyqtSignal(bool, float)
sigChangePlane = pyqtSignal(gp_Ax3)
def __init__(self,parent):
super(CQObjectInspector,self).__init__(parent)
def __init__(self, parent):
super(CQObjectInspector, self).__init__(parent)
self.setHeaderHidden(False)
self.setRootIsDecorated(True)
self.setContextMenuPolicy(Qt.ActionsContextMenu)
self.setColumnCount(2)
self.setHeaderLabels(['Type','Value'])
self.setHeaderLabels(["Type", "Value"])
self.root = self.invisibleRootItem()
self.inspected_items = []
self._toolbar_actions = \
[QAction(icon('inspect'),'Inspect CQ object',self,\
toggled=self.inspect,checkable=True)]
self._toolbar_actions = [
QAction(
icon("inspect"),
"Inspect CQ object",
self,
toggled=self.inspect,
checkable=True,
)
]
self.addActions(self._toolbar_actions)
def menuActions(self):
return {'Tools' : self._toolbar_actions}
return {"Tools": self._toolbar_actions}
def toolbarActions(self):
return self._toolbar_actions
@pyqtSlot(bool)
def inspect(self,value):
def inspect(self, value):
if value:
self.itemSelectionChanged.connect(self.handleSelection)
self.itemSelectionChanged.emit()
@@ -74,56 +73,52 @@ class CQObjectInspector(QTreeWidget,ComponentMixin):
self.itemSelectionChanged.disconnect(self.handleSelection)
self.sigRemoveObjects.emit(self.inspected_items)
self.sigShowPlane.emit(False)
@pyqtSlot()
@pyqtSlot()
def handleSelection(self):
inspected_items = self.inspected_items
self.sigRemoveObjects.emit(inspected_items)
inspected_items.clear()
items = self.selectedItems()
if len(items) == 0:
return
item = items[-1]
if type(item) is CQStackItem:
cq_plane = item.workplane.plane
dim = item.workplane.largestDimension()
plane = gp_Ax3(cq_plane.origin.toPnt(),
cq_plane.zDir.toDir(),
cq_plane.xDir.toDir())
plane = gp_Ax3(
cq_plane.origin.toPnt(), cq_plane.zDir.toDir(), cq_plane.xDir.toDir()
)
self.sigChangePlane.emit(plane)
self.sigShowPlane[bool,float].emit(True,dim)
self.sigShowPlane[bool, float].emit(True, dim)
for child in (item.child(i) for i in range(item.childCount())):
obj = child.cq_item
if hasattr(obj,'wrapped') and type(obj) != Vector:
if hasattr(obj, "wrapped") and type(obj) != Vector:
ais = AIS_ColoredShape(obj.wrapped)
inspected_items.append(ais)
else:
self.sigShowPlane.emit(False)
obj = item.cq_item
if hasattr(obj,'wrapped') and type(obj) != Vector:
if hasattr(obj, "wrapped") and type(obj) != Vector:
ais = AIS_ColoredShape(obj.wrapped)
inspected_items.append(ais)
self.sigDisplayObjects.emit(inspected_items,False)
self.sigDisplayObjects.emit(inspected_items, False)
@pyqtSlot(object)
def setObject(self,cq_obj):
def setObject(self, cq_obj):
self.root.takeChildren()
# iterate through parent objects if they exist
while getattr(cq_obj, 'parent', None):
current_frame = CQStackItem(str(cq_obj.plane.origin),workplane=cq_obj)
while getattr(cq_obj, "parent", None):
current_frame = CQStackItem(str(cq_obj.plane.origin), workplane=cq_obj)
self.root.addChild(current_frame)
for obj in cq_obj.objects:
current_frame.addChild(CQChildItem(obj))
cq_obj = cq_obj.parent

View File

@@ -9,12 +9,13 @@ from PySide6 import QtCore
from PySide6.QtCore import (
Qt,
QObject,
pyqtSlot,
pyqtSignal,
Slot as pyqtSlot,
Signal as pyqtSignal,
QEventLoop,
QAbstractTableModel,
)
from PySide6.QtWidgets import QAction, QTableView
from PySide6.QtWidgets import QTableView
from PySide6.QtGui import QAction
from logbook import info
from path import Path
from pyqtgraph.parametertree import Parameter

View File

@@ -2,9 +2,11 @@ import os
from modulefinder import ModuleFinder
from spyder.plugins.editor.widgets.codeeditor import CodeEditor
from PySide6.QtCore import pyqtSignal, QFileSystemWatcher, QTimer
from PySide6.QtWidgets import QAction, QFileDialog
from PySide6.QtGui import QFontDatabase
from PySide6.QtCore import Signal as pyqtSignal
from PySide6.QtCore import QFileSystemWatcher, QTimerEvent, QTimer
from PySide6.QtWidgets import QFileDialog
from PySide6.QtGui import QFontDatabase, QAction
from path import Path
import sys
@@ -16,91 +18,104 @@ from ..utils import get_save_filename, get_open_filename, confirm
from ..icons import icon
class Editor(CodeEditor,ComponentMixin):
name = 'Code Editor'
class Editor(CodeEditor, ComponentMixin):
name = "Code Editor"
# This signal is emitted whenever the currently-open file changes and
# autoreload is enabled.
triggerRerender = pyqtSignal(bool)
sigFilenameChanged = pyqtSignal(str)
preferences = Parameter.create(name='Preferences',children=[
{'name': 'Font size', 'type': 'int', 'value': 12},
{'name': 'Autoreload', 'type': 'bool', 'value': False},
{'name': 'Autoreload delay', 'type': 'int', 'value': 50},
{'name': 'Autoreload: watch imported modules', 'type': 'bool', 'value': False},
{'name': 'Line wrap', 'type': 'bool', 'value': False},
{'name': 'Color scheme', 'type': 'list',
'values': ['Spyder','Monokai','Zenburn'], 'value': 'Spyder'}])
preferences = Parameter.create(
name="Preferences",
children=[
{"name": "Font size", "type": "int", "value": 12},
{"name": "Autoreload", "type": "bool", "value": False},
{"name": "Autoreload delay", "type": "int", "value": 50},
{
"name": "Autoreload: watch imported modules",
"type": "bool",
"value": False,
},
{"name": "Line wrap", "type": "bool", "value": False},
{
"name": "Color scheme",
"type": "list",
"values": ["Spyder", "Monokai", "Zenburn"],
"value": "Spyder",
},
],
)
EXTENSIONS = 'py'
def __init__(self,parent=None):
EXTENSIONS = "py"
def __init__(self, parent=None):
self._watched_file = None
super(Editor,self).__init__(parent)
super(Editor, self).__init__(parent)
ComponentMixin.__init__(self)
self.setup_editor(linenumbers=True,
markers=True,
edge_line=False,
tab_mode=False,
show_blanks=True,
font=QFontDatabase.systemFont(QFontDatabase.FixedFont),
language='Python',
filename='')
self.setup_editor(
linenumbers=True,
markers=True,
edge_line=False,
tab_mode=False,
show_blanks=True,
font=QFontDatabase.systemFont(QFontDatabase.FixedFont),
language="Python",
filename="",
)
self._actions = \
{'File' : [QAction(icon('new'),
'New',
self,
shortcut='ctrl+N',
triggered=self.new),
QAction(icon('open'),
'Open',
self,
shortcut='ctrl+O',
triggered=self.open),
QAction(icon('save'),
'Save',
self,
shortcut='ctrl+S',
triggered=self.save),
QAction(icon('save_as'),
'Save as',
self,
shortcut='ctrl+shift+S',
triggered=self.save_as),
QAction(icon('autoreload'),
'Automatic reload and preview',
self,triggered=self.autoreload,
checkable=True,
checked=False,
objectName='autoreload'),
]}
self._actions = {
"File": [
QAction(
icon("new"), "New", self, shortcut="ctrl+N", triggered=self.new
),
QAction(
icon("open"), "Open", self, shortcut="ctrl+O", triggered=self.open
),
QAction(
icon("save"), "Save", self, shortcut="ctrl+S", triggered=self.save
),
QAction(
icon("save_as"),
"Save as",
self,
shortcut="ctrl+shift+S",
triggered=self.save_as,
),
QAction(
icon("autoreload"),
"Automatic reload and preview",
self,
triggered=self.autoreload,
checkable=True,
checked=False,
objectName="autoreload",
),
]
}
for a in self._actions.values():
self.addActions(a)
self._fixContextMenu()
# autoreload support
self._file_watcher = QFileSystemWatcher(self)
# we wait for 50ms after a file change for the file to be written completely
self._file_watch_timer = QTimer(self)
self._file_watch_timer.setInterval(self.preferences['Autoreload delay'])
self._file_watch_timer.setInterval(self.preferences["Autoreload delay"])
self._file_watch_timer.setSingleShot(True)
self._file_watcher.fileChanged.connect(
lambda val: self._file_watch_timer.start())
lambda val: self._file_watch_timer.start()
)
self._file_watch_timer.timeout.connect(self._file_changed)
self.updatePreferences()
def _fixContextMenu(self):
menu = self.menu
menu.removeAction(self.run_cell_action)
@@ -108,68 +123,66 @@ class Editor(CodeEditor,ComponentMixin):
menu.removeAction(self.run_selection_action)
menu.removeAction(self.re_run_last_cell_action)
def updatePreferences(self,*args):
self.set_color_scheme(self.preferences['Color scheme'])
def updatePreferences(self, *args):
self.set_color_scheme(self.preferences["Color scheme"])
font = self.font()
font.setPointSize(self.preferences['Font size'])
font.setPointSize(self.preferences["Font size"])
self.set_font(font)
self.findChild(QAction, 'autoreload') \
.setChecked(self.preferences['Autoreload'])
self.findChild(QAction, "autoreload").setChecked(self.preferences["Autoreload"])
self._file_watch_timer.setInterval(self.preferences['Autoreload delay'])
self._file_watch_timer.setInterval(self.preferences["Autoreload delay"])
self.toggle_wrap_mode(self.preferences['Line wrap'])
self.toggle_wrap_mode(self.preferences["Line wrap"])
self._clear_watched_paths()
self._watch_paths()
def confirm_discard(self):
if self.modified:
rv = confirm(self,'Please confirm','Current document is not saved - do you want to continue?')
rv = confirm(
self,
"Please confirm",
"Current document is not saved - do you want to continue?",
)
else:
rv = True
return rv
def new(self):
if not self.confirm_discard():
return
if not self.confirm_discard(): return
self.set_text('')
self.filename = ''
self.set_text("")
self.filename = ""
self.reset_modified()
def open(self):
if not self.confirm_discard(): return
if not self.confirm_discard():
return
curr_dir = Path(self.filename).abspath().dirname()
fname = get_open_filename(self.EXTENSIONS, curr_dir)
if fname != '':
if fname != "":
self.load_from_file(fname)
def load_from_file(self,fname):
def load_from_file(self, fname):
self.set_text_from_file(fname)
self.filename = fname
self.reset_modified()
def save(self):
if self._filename != '':
if self.preferences['Autoreload']:
if self._filename != "":
if self.preferences["Autoreload"]:
self._file_watcher.blockSignals(True)
self._file_watch_timer.stop()
with open(self._filename, 'w') as f:
with open(self._filename, "w") as f:
f.write(self.toPlainText())
if self.preferences['Autoreload']:
if self.preferences["Autoreload"]:
self._file_watcher.blockSignals(False)
self.triggerRerender.emit(True)
@@ -179,20 +192,25 @@ class Editor(CodeEditor,ComponentMixin):
self.save_as()
def save_as(self):
fname = get_save_filename(self.EXTENSIONS)
if fname != '':
with open(fname,'w') as f:
if fname != "":
with open(fname, "w") as f:
f.write(self.toPlainText())
self.filename = fname
self.reset_modified()
def _update_filewatcher(self):
if self._watched_file and (self._watched_file != self.filename or not self.preferences['Autoreload']):
if self._watched_file and (
self._watched_file != self.filename or not self.preferences["Autoreload"]
):
self._clear_watched_paths()
self._watched_file = None
if self.preferences['Autoreload'] and self.filename and self.filename != self._watched_file:
if (
self.preferences["Autoreload"]
and self.filename
and self.filename != self._watched_file
):
self._watched_file = self._filename
self._watch_paths()
@@ -214,8 +232,8 @@ class Editor(CodeEditor,ComponentMixin):
def _watch_paths(self):
if Path(self._filename).exists():
self._file_watcher.addPath(self._filename)
if self.preferences['Autoreload: watch imported modules']:
module_paths = self.get_imported_module_paths(self._filename)
if self.preferences["Autoreload: watch imported modules"]:
module_paths = self.get_imported_module_paths(self._filename)
if module_paths:
self._file_watcher.addPaths(module_paths)
@@ -228,51 +246,45 @@ class Editor(CodeEditor,ComponentMixin):
# Turn autoreload on/off.
def autoreload(self, enabled):
self.preferences['Autoreload'] = enabled
self.preferences["Autoreload"] = enabled
self._update_filewatcher()
def reset_modified(self):
self.document().setModified(False)
@property
def modified(self):
return self.document().isModified()
def saveComponentState(self,store):
def saveComponentState(self, store):
if self.filename != "":
store.setValue(self.name + "/state", self.filename)
if self.filename != '':
store.setValue(self.name+'/state',self.filename)
def restoreComponentState(self, store):
filename = store.value(self.name + "/state")
def restoreComponentState(self,store):
filename = store.value(self.name+'/state')
if filename and self.filename == '':
if filename and self.filename == "":
try:
self.load_from_file(filename)
except IOError:
self._logger.warning(f'could not open {filename}')
self._logger.warning(f"could not open {filename}")
def get_imported_module_paths(self, module_path):
finder = ModuleFinder([os.path.dirname(module_path)])
imported_modules = []
try:
finder.run_script(module_path)
except SyntaxError as err:
self._logger.warning(f'Syntax error in {module_path}: {err}')
self._logger.warning(f"Syntax error in {module_path}: {err}")
except Exception as err:
self._logger.warning(
f'Cannot determine imported modules in {module_path}: {type(err).__name__} {err}'
f"Cannot determine imported modules in {module_path}: {type(err).__name__} {err}"
)
else:
for module_name, module in finder.modules.items():
if module_name != '__main__':
path = getattr(module, '__file__', None)
if module_name != "__main__":
path = getattr(module, "__file__", None)
if path is not None and os.path.isfile(path):
imported_modules.append(path)
@@ -280,7 +292,6 @@ class Editor(CodeEditor,ComponentMixin):
if __name__ == "__main__":
from PySide6.QtWidgets import QApplication
app = QApplication(sys.argv)

View File

@@ -1,12 +1,12 @@
from PySide6.QtWidgets import (
QTreeWidget,
QTreeWidgetItem,
QAction,
QMenu,
QWidget,
QAbstractItemView,
)
from PySide6.QtCore import Qt, pyqtSlot, pyqtSignal
from PySide6.QtGui import QAction
from PySide6.QtCore import Qt, Slot as pyqtSlot, Signal as pyqtSignal
from pyqtgraph.parametertree import Parameter, ParameterTree
from OCP.AIS import AIS_Line
@@ -114,7 +114,8 @@ class ObjectTree(QWidget, ComponentMixin):
],
)
sigObjectsAdded = pyqtSignal([list], [list, bool])
sigObjectsAdded = pyqtSignal(list)
sigObjectsAdded2 = pyqtSignal(list, bool)
sigObjectsRemoved = pyqtSignal(list)
sigCQObjectSelected = pyqtSignal(object)
sigAISObjectsSelected = pyqtSignal(list)

View File

@@ -2,7 +2,7 @@ from sys import platform
from PySide6.QtWidgets import QWidget, QApplication
from PySide6.QtCore import pyqtSlot, pyqtSignal, Qt, QEvent
from PySide6.QtCore import Slot as pyqtSlot, Signal as pyqtSignal, Qt, QEvent
import OCP
@@ -15,161 +15,144 @@ from OCP.Quantity import Quantity_Color, Quantity_TOC_RGB as TOC_RGB
ZOOM_STEP = 0.9
class OCCTWidget(QWidget):
sigObjectSelected = pyqtSignal(list)
def __init__(self,parent=None):
super(OCCTWidget,self).__init__(parent)
def __init__(self, parent=None):
super(OCCTWidget, self).__init__(parent)
self.setAttribute(Qt.WA_NativeWindow)
self.setAttribute(Qt.WA_PaintOnScreen)
self.setAttribute(Qt.WA_NoSystemBackground)
self._initialized = False
self._needs_update = False
#OCCT secific things
# OCCT secific things
self.display_connection = Aspect_DisplayConnection()
self.graphics_driver = OpenGl_GraphicDriver(self.display_connection)
self.viewer = V3d_Viewer(self.graphics_driver)
self.view = self.viewer.CreateView()
self.context = AIS_InteractiveContext(self.viewer)
#Trihedorn, lights, etc
# Trihedorn, lights, etc
self.prepare_display()
def prepare_display(self):
view = self.view
params = view.ChangeRenderingParams()
params.NbMsaaSamples = 8
params.IsAntialiasingEnabled = True
view.TriedronDisplay(
Aspect_TypeOfTriedronPosition.Aspect_TOTP_RIGHT_LOWER,
Quantity_Color(), 0.1)
view.ZBufferTriedronSetup(Quantity_Color(*(0.2, 0.0, 0.0), TOC_RGB))
Aspect_TypeOfTriedronPosition.Aspect_TOTP_RIGHT_LOWER, Quantity_Color(), 0.1
)
view.ZBufferTriedronSetup(Quantity_Color(*(0.2, 0.0, 0.0), TOC_RGB))
viewer = self.viewer
viewer.SetDefaultLights()
viewer.SetLightOn()
ctx = self.context
ctx.SetDisplayMode(AIS_DisplayMode.AIS_Shaded, True)
ctx.DefaultDrawer().SetFaceBoundaryDraw(True)
def wheelEvent(self, event):
delta = event.angleDelta().y()
factor = ZOOM_STEP if delta<0 else 1/ZOOM_STEP
factor = ZOOM_STEP if delta < 0 else 1 / ZOOM_STEP
self.view.SetZoom(factor)
def mousePressEvent(self,event):
def mousePressEvent(self, event):
pos = event.pos()
if event.button() == Qt.LeftButton:
self.view.StartRotation(pos.x(), pos.y())
elif event.button() == Qt.RightButton:
self.view.StartZoomAtPoint(pos.x(), pos.y())
self.old_pos = pos
def mouseMoveEvent(self,event):
def mouseMoveEvent(self, event):
pos = event.pos()
x,y = pos.x(),pos.y()
x, y = pos.x(), pos.y()
if event.buttons() == Qt.LeftButton:
self.view.Rotation(x,y)
self.view.Rotation(x, y)
elif event.buttons() == Qt.MiddleButton:
self.view.Pan(x - self.old_pos.x(),
self.old_pos.y() - y, theToStart=True)
self.view.Pan(x - self.old_pos.x(), self.old_pos.y() - y, theToStart=True)
elif event.buttons() == Qt.RightButton:
self.view.Pan(x - self.old_pos.x(),
self.old_pos.y() - y, theToStart=True)
self.view.Pan(x - self.old_pos.x(), self.old_pos.y() - y, theToStart=True)
self.old_pos = pos
def mouseReleaseEvent(self,event):
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
pos = event.pos()
x,y = pos.x(),pos.y()
self.context.MoveTo(x,y,self.view,True)
x, y = pos.x(), pos.y()
self.context.MoveTo(x, y, self.view, True)
self._handle_selection()
def _handle_selection(self):
self.context.Select(True)
self.context.InitSelected()
selected = []
if self.context.HasSelectedShape():
selected.append(self.context.SelectedShape())
self.sigObjectSelected.emit(selected)
def paintEngine(self):
return None
def paintEvent(self, event):
if not self._initialized:
self._initialize()
else:
self.view.Redraw()
def showEvent(self, event):
super(OCCTWidget,self).showEvent(event)
def resizeEvent(self, event):
super(OCCTWidget,self).resizeEvent(event)
self.view.MustBeResized()
def _initialize(self):
super(OCCTWidget, self).showEvent(event)
def resizeEvent(self, event):
super(OCCTWidget, self).resizeEvent(event)
self.view.MustBeResized()
def _initialize(self):
wins = {
'darwin' : self._get_window_osx,
'linux' : self._get_window_linux,
'win32': self._get_window_win
"darwin": self._get_window_osx,
"linux": self._get_window_linux,
"win32": self._get_window_win,
}
self.view.SetWindow(wins.get(platform,self._get_window_linux)(self.winId()))
self.view.SetWindow(wins.get(platform, self._get_window_linux)(self.winId()))
self._initialized = True
def _get_window_win(self,wid):
def _get_window_win(self, wid):
from OCP.WNT import WNT_Window
print(wid)
return WNT_Window(wid.ascapsule())
def _get_window_linux(self,wid):
def _get_window_linux(self, wid):
from OCP.Xw import Xw_Window
return Xw_Window(self.display_connection,int(wid))
def _get_window_osx(self,wid):
return Xw_Window(self.display_connection, int(wid))
def _get_window_osx(self, wid):
from OCP.Cocoa import Cocoa_Window
return Cocoa_Window(wid.ascapsule())

View File

@@ -1,97 +1,89 @@
from traceback import extract_tb, format_exception_only
from PySide6.QtWidgets import (QWidget, QTreeWidget, QTreeWidgetItem, QAction,
QLabel)
from PySide6.QtCore import Qt, pyqtSlot, pyqtSignal
from PySide6.QtWidgets import QWidget, QTreeWidget, QTreeWidgetItem, QLabel
from PySide6.QtGui import QAction
from PySide6.QtCore import Qt, Slot as pyqtSlot, Signal as pyqtSignal
from ..mixins import ComponentMixin
from ..utils import layout
class TracebackTree(QTreeWidget):
name = 'Traceback Viewer'
def __init__(self,parent):
super(TracebackTree,self).__init__(parent)
class TracebackTree(QTreeWidget):
name = "Traceback Viewer"
def __init__(self, parent):
super(TracebackTree, self).__init__(parent)
self.setHeaderHidden(False)
self.setItemsExpandable(False)
self.setRootIsDecorated(False)
self.setContextMenuPolicy(Qt.ActionsContextMenu)
self.setColumnCount(3)
self.setHeaderLabels(['File','Line','Code'])
self.setHeaderLabels(["File", "Line", "Code"])
self.root = self.invisibleRootItem()
class TracebackPane(QWidget,ComponentMixin):
class TracebackPane(QWidget, ComponentMixin):
sigHighlightLine = pyqtSignal(int)
def __init__(self,parent):
super(TracebackPane,self).__init__(parent)
def __init__(self, parent):
super(TracebackPane, self).__init__(parent)
self.tree = TracebackTree(self)
self.current_exception = QLabel(self)
self.current_exception.setStyleSheet(\
"QLabel {color : red; }");
layout(self,
(self.current_exception,
self.tree),
self)
self.current_exception.setStyleSheet("QLabel {color : red; }")
layout(self, (self.current_exception, self.tree), self)
self.tree.currentItemChanged.connect(self.handleSelection)
@pyqtSlot(object,str)
def addTraceback(self,exc_info,code):
@pyqtSlot(object, str)
def addTraceback(self, exc_info, code):
self.tree.clear()
if exc_info:
t,exc,tb = exc_info
t, exc, tb = exc_info
root = self.tree.root
code = code.splitlines()
tb = [t for t in extract_tb(tb) if '<string>' in t.filename] #ignore highest frames (debug, exec)
tb = [
t for t in extract_tb(tb) if "<string>" in t.filename
] # ignore highest frames (debug, exec)
for el in tb:
#workaround of the traceback module
if el.line == '':
line = code[el.lineno-1].strip()
# workaround of the traceback module
if el.line == "":
line = code[el.lineno - 1].strip()
else:
line = el.line
root.addChild(QTreeWidgetItem([el.filename,
str(el.lineno),
line]))
root.addChild(QTreeWidgetItem([el.filename, str(el.lineno), line]))
exc_name = t.__name__
exc_msg = str(exc)
exc_msg = exc_msg.replace('<', '&lt;').replace('>', '&gt;') #replace <>
exc_msg = exc_msg.replace("<", "&lt;").replace(">", "&gt;") # replace <>
self.current_exception.setText("<b>{}</b>: {}".format(exc_name, exc_msg))
self.current_exception.\
setText('<b>{}</b>: {}'.format(exc_name,exc_msg))
# handle the special case of a SyntaxError
if t is SyntaxError:
root.addChild(QTreeWidgetItem(
[exc.filename,
str(exc.lineno),
exc.text.strip() if exc.text else '']
))
if t is SyntaxError:
root.addChild(
QTreeWidgetItem(
[
exc.filename,
str(exc.lineno),
exc.text.strip() if exc.text else "",
]
)
)
else:
self.current_exception.setText('')
self.current_exception.setText("")
@pyqtSlot(QTreeWidgetItem,QTreeWidgetItem)
def handleSelection(self,item,*args):
@pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
def handleSelection(self, item, *args):
if item:
f,line = item.data(0,0),int(item.data(1,0))
if '<string>' in f:
f, line = item.data(0, 0), int(item.data(1, 0))
if "<string>" in f:
self.sigHighlightLine.emit(line)

View File

@@ -1,14 +1,21 @@
from PySide6.QtWidgets import QWidget, QDialog, QTreeWidgetItem, QApplication, QAction
from PySide6.QtWidgets import QWidget, QDialog, QTreeWidgetItem, QApplication
from PySide6.QtCore import pyqtSlot, pyqtSignal
from PySide6.QtGui import QIcon
from PySide6.QtCore import Slot as pyqtSlot, Signal as pyqtSignal
from PySide6.QtGui import QIcon, QAction
from OCP.Graphic3d import Graphic3d_Camera, Graphic3d_StereoMode, Graphic3d_NOM_JADE,\
Graphic3d_MaterialAspect
from OCP.AIS import AIS_Shaded,AIS_WireFrame, AIS_ColoredShape, AIS_Axis
from OCP.Graphic3d import (
Graphic3d_Camera,
Graphic3d_StereoMode,
Graphic3d_NOM_JADE,
Graphic3d_MaterialAspect,
)
from OCP.AIS import AIS_Shaded, AIS_WireFrame, AIS_ColoredShape, AIS_Axis
from OCP.Aspect import Aspect_GDM_Lines, Aspect_GT_Rectangular
from OCP.Quantity import Quantity_NOC_BLACK as BLACK, Quantity_TOC_RGB as TOC_RGB,\
Quantity_Color
from OCP.Quantity import (
Quantity_NOC_BLACK as BLACK,
Quantity_TOC_RGB as TOC_RGB,
Quantity_Color,
)
from OCP.Geom import Geom_Axis1Placement
from OCP.gp import gp_Ax3, gp_Dir, gp_Pnt, gp_Ax1
@@ -25,30 +32,66 @@ import qtawesome as qta
DEFAULT_EDGE_COLOR = Quantity_Color(BLACK)
DEFAULT_EDGE_WIDTH = 2
class OCCViewer(QWidget,ComponentMixin):
name = '3D Viewer'
class OCCViewer(QWidget, ComponentMixin):
name = "3D Viewer"
preferences = Parameter.create(name='Pref',children=[
{'name': 'Fit automatically', 'type': 'bool', 'value': True},
{'name': 'Use gradient', 'type': 'bool', 'value': False},
{'name': 'Background color', 'type': 'color', 'value': (95,95,95)},
{'name': 'Background color (aux)', 'type': 'color', 'value': (30,30,30)},
{'name': 'Default object color', 'type': 'color', 'value': "#FF0"},
{'name': 'Deviation', 'type': 'float', 'value': 1e-5, 'dec': True, 'step': 1},
{'name': 'Angular deviation', 'type': 'float', 'value': 0.1, 'dec': True, 'step': 1},
{'name': 'Projection Type', 'type': 'list', 'value': 'Orthographic',
'values': ['Orthographic', 'Perspective', 'Stereo', 'MonoLeftEye', 'MonoRightEye']},
{'name': 'Stereo Mode', 'type': 'list', 'value': 'QuadBuffer',
'values': ['QuadBuffer', 'Anaglyph', 'RowInterlaced', 'ColumnInterlaced',
'ChessBoard', 'SideBySide', 'OverUnder']}])
IMAGE_EXTENSIONS = 'png'
preferences = Parameter.create(
name="Pref",
children=[
{"name": "Fit automatically", "type": "bool", "value": True},
{"name": "Use gradient", "type": "bool", "value": False},
{"name": "Background color", "type": "color", "value": (95, 95, 95)},
{"name": "Background color (aux)", "type": "color", "value": (30, 30, 30)},
{"name": "Default object color", "type": "color", "value": "#FF0"},
{
"name": "Deviation",
"type": "float",
"value": 1e-5,
"dec": True,
"step": 1,
},
{
"name": "Angular deviation",
"type": "float",
"value": 0.1,
"dec": True,
"step": 1,
},
{
"name": "Projection Type",
"type": "list",
"value": "Orthographic",
"values": [
"Orthographic",
"Perspective",
"Stereo",
"MonoLeftEye",
"MonoRightEye",
],
},
{
"name": "Stereo Mode",
"type": "list",
"value": "QuadBuffer",
"values": [
"QuadBuffer",
"Anaglyph",
"RowInterlaced",
"ColumnInterlaced",
"ChessBoard",
"SideBySide",
"OverUnder",
],
},
],
)
IMAGE_EXTENSIONS = "png"
sigObjectSelected = pyqtSignal(list)
def __init__(self,parent=None):
super(OCCViewer,self).__init__(parent)
def __init__(self, parent=None):
super(OCCViewer, self).__init__(parent)
ComponentMixin.__init__(self)
self.canvas = OCCTWidget()
@@ -56,15 +99,18 @@ class OCCViewer(QWidget,ComponentMixin):
self.create_actions(self)
self.layout_ = layout(self,
[self.canvas,],
top_widget=self,
margin=0)
self.setup_default_drawer() #misspelled in original
self.layout_ = layout(
self,
[
self.canvas,
],
top_widget=self,
margin=0,
)
self.setup_default_drawer() # misspelled in original
self.updatePreferences()
def setup_default_drawer(self):
def setup_default_drawer(self):
# set the default color and material
material = Graphic3d_MaterialAspect(Graphic3d_NOM_JADE)
@@ -76,99 +122,129 @@ class OCCViewer(QWidget,ComponentMixin):
line_aspect = self.canvas.context.DefaultDrawer().FaceBoundaryAspect()
line_aspect.SetWidth(DEFAULT_EDGE_WIDTH)
line_aspect.SetColor(DEFAULT_EDGE_COLOR)
def updatePreferences(self,*args):
color1 = to_occ_color(self.preferences['Background color'])
color2 = to_occ_color(self.preferences['Background color (aux)'])
def updatePreferences(self, *args):
color1 = to_occ_color(self.preferences["Background color"])
color2 = to_occ_color(self.preferences["Background color (aux)"])
if not self.preferences['Use gradient']:
if not self.preferences["Use gradient"]:
color2 = color1
self.canvas.view.SetBgGradientColors(color1,color2,theToUpdate=True)
self.canvas.view.SetBgGradientColors(color1, color2, theToUpdate=True)
self.canvas.update()
ctx = self.canvas.context
ctx.SetDeviationCoefficient(self.preferences['Deviation'])
ctx.SetDeviationAngle(self.preferences['Angular deviation'])
ctx.SetDeviationCoefficient(self.preferences["Deviation"])
ctx.SetDeviationAngle(self.preferences["Angular deviation"])
v = self._get_view()
camera = v.Camera()
projection_type = self.preferences['Projection Type']
camera.SetProjectionType(getattr(Graphic3d_Camera, f'Projection_{projection_type}',
Graphic3d_Camera.Projection_Orthographic))
projection_type = self.preferences["Projection Type"]
camera.SetProjectionType(
getattr(
Graphic3d_Camera,
f"Projection_{projection_type}",
Graphic3d_Camera.Projection_Orthographic,
)
)
# onle relevant for stereo projection
stereo_mode = self.preferences['Stereo Mode']
stereo_mode = self.preferences["Stereo Mode"]
params = v.ChangeRenderingParams()
params.StereoMode = getattr(Graphic3d_StereoMode, f'Graphic3d_StereoMode_{stereo_mode}',
Graphic3d_StereoMode.Graphic3d_StereoMode_QuadBuffer)
params.StereoMode = getattr(
Graphic3d_StereoMode,
f"Graphic3d_StereoMode_{stereo_mode}",
Graphic3d_StereoMode.Graphic3d_StereoMode_QuadBuffer,
)
def create_actions(self,parent):
self._actions = \
{'View' : [QAction(qta.icon('fa.arrows-alt'),
'Fit (Shift+F1)',
parent,
shortcut='shift+F1',
triggered=self.fit),
QAction(QIcon(':/images/icons/isometric_view.svg'),
'Iso (Shift+F2)',
parent,
shortcut='shift+F2',
triggered=self.iso_view),
QAction(QIcon(':/images/icons/top_view.svg'),
'Top (Shift+F3)',
parent,
shortcut='shift+F3',
triggered=self.top_view),
QAction(QIcon(':/images/icons/bottom_view.svg'),
'Bottom (Shift+F4)',
parent,
shortcut='shift+F4',
triggered=self.bottom_view),
QAction(QIcon(':/images/icons/front_view.svg'),
'Front (Shift+F5)',
parent,
shortcut='shift+F5',
triggered=self.front_view),
QAction(QIcon(':/images/icons/back_view.svg'),
'Back (Shift+F6)',
parent,
shortcut='shift+F6',
triggered=self.back_view),
QAction(QIcon(':/images/icons/left_side_view.svg'),
'Left (Shift+F7)',
parent,
shortcut='shift+F7',
triggered=self.left_view),
QAction(QIcon(':/images/icons/right_side_view.svg'),
'Right (Shift+F8)',
parent,
shortcut='shift+F8',
triggered=self.right_view),
QAction(qta.icon('fa.square-o'),
'Wireframe (Shift+F9)',
parent,
shortcut='shift+F9',
triggered=self.wireframe_view),
QAction(qta.icon('fa.square'),
'Shaded (Shift+F10)',
parent,
shortcut='shift+F10',
triggered=self.shaded_view)],
'Tools' : [QAction(icon('screenshot'),
'Screenshot',
parent,
triggered=self.save_screenshot)]}
def create_actions(self, parent):
self._actions = {
"View": [
QAction(
qta.icon("fa.arrows-alt"),
"Fit (Shift+F1)",
parent,
shortcut="shift+F1",
triggered=self.fit,
),
QAction(
QIcon(":/images/icons/isometric_view.svg"),
"Iso (Shift+F2)",
parent,
shortcut="shift+F2",
triggered=self.iso_view,
),
QAction(
QIcon(":/images/icons/top_view.svg"),
"Top (Shift+F3)",
parent,
shortcut="shift+F3",
triggered=self.top_view,
),
QAction(
QIcon(":/images/icons/bottom_view.svg"),
"Bottom (Shift+F4)",
parent,
shortcut="shift+F4",
triggered=self.bottom_view,
),
QAction(
QIcon(":/images/icons/front_view.svg"),
"Front (Shift+F5)",
parent,
shortcut="shift+F5",
triggered=self.front_view,
),
QAction(
QIcon(":/images/icons/back_view.svg"),
"Back (Shift+F6)",
parent,
shortcut="shift+F6",
triggered=self.back_view,
),
QAction(
QIcon(":/images/icons/left_side_view.svg"),
"Left (Shift+F7)",
parent,
shortcut="shift+F7",
triggered=self.left_view,
),
QAction(
QIcon(":/images/icons/right_side_view.svg"),
"Right (Shift+F8)",
parent,
shortcut="shift+F8",
triggered=self.right_view,
),
QAction(
qta.icon("fa.square-o"),
"Wireframe (Shift+F9)",
parent,
shortcut="shift+F9",
triggered=self.wireframe_view,
),
QAction(
qta.icon("fa.square"),
"Shaded (Shift+F10)",
parent,
shortcut="shift+F10",
triggered=self.shaded_view,
),
],
"Tools": [
QAction(
icon("screenshot"),
"Screenshot",
parent,
triggered=self.save_screenshot,
)
],
}
def toolbarActions(self):
return self._actions['View']
return self._actions["View"]
def clear(self):
self.displayed_shapes = []
self.displayed_ais = []
self.canvas.context.EraseAll(True)
@@ -176,199 +252,167 @@ class OCCViewer(QWidget,ComponentMixin):
context.PurgeDisplay()
context.RemoveAll(True)
def _display(self,shape):
def _display(self, shape):
ais = make_AIS(shape)
self.canvas.context.Display(shape,True)
self.canvas.context.Display(shape, True)
self.displayed_shapes.append(shape)
self.displayed_ais.append(ais)
#self.canvas._display.Repaint()
# self.canvas._display.Repaint()
@pyqtSlot(object)
def display(self,ais):
def display(self, ais):
context = self._get_context()
context.Display(ais,True)
context.Display(ais, True)
if self.preferences['Fit automatically']: self.fit()
if self.preferences["Fit automatically"]:
self.fit()
@pyqtSlot(list)
@pyqtSlot(list,bool)
def display_many(self,ais_list,fit=None):
@pyqtSlot(list, bool)
def display_many(self, ais_list, fit=None):
context = self._get_context()
for ais in ais_list:
context.Display(ais,True)
context.Display(ais, True)
if self.preferences['Fit automatically'] and fit is None:
if self.preferences["Fit automatically"] and fit is None:
self.fit()
elif fit:
self.fit()
@pyqtSlot(QTreeWidgetItem,int)
def update_item(self,item,col):
@pyqtSlot(QTreeWidgetItem, int)
def update_item(self, item, col):
ctx = self._get_context()
if item.checkState(0):
ctx.Display(item.ais,True)
ctx.Display(item.ais, True)
else:
ctx.Erase(item.ais,True)
ctx.Erase(item.ais, True)
@pyqtSlot(list)
def remove_items(self,ais_items):
def remove_items(self, ais_items):
ctx = self._get_context()
for ais in ais_items: ctx.Erase(ais,True)
for ais in ais_items:
ctx.Erase(ais, True)
@pyqtSlot()
def redraw(self):
self._get_viewer().Redraw()
def fit(self):
self.canvas.view.FitAll()
def iso_view(self):
v = self._get_view()
v.SetProj(1,-1,1)
v.SetProj(1, -1, 1)
v.SetTwist(0)
def bottom_view(self):
v = self._get_view()
v.SetProj(0,0,-1)
v.SetProj(0, 0, -1)
v.SetTwist(0)
def top_view(self):
v = self._get_view()
v.SetProj(0,0,1)
v.SetProj(0, 0, 1)
v.SetTwist(0)
def front_view(self):
v = self._get_view()
v.SetProj(0,-1,0)
v.SetProj(0, -1, 0)
v.SetTwist(0)
def back_view(self):
v = self._get_view()
v.SetProj(0,1,0)
v.SetProj(0, 1, 0)
v.SetTwist(0)
def left_view(self):
v = self._get_view()
v.SetProj(-1,0,0)
v.SetProj(-1, 0, 0)
v.SetTwist(0)
def right_view(self):
v = self._get_view()
v.SetProj(1,0,0)
v.SetProj(1, 0, 0)
v.SetTwist(0)
def shaded_view(self):
c = self._get_context()
c.SetDisplayMode(AIS_Shaded, True)
def wireframe_view(self):
c = self._get_context()
c.SetDisplayMode(AIS_WireFrame, True)
def show_grid(self,
step=1.,
size=10.+1e-6,
color1=(.7,.7,.7),
color2=(0,0,0)):
def show_grid(
self, step=1.0, size=10.0 + 1e-6, color1=(0.7, 0.7, 0.7), color2=(0, 0, 0)
):
viewer = self._get_viewer()
viewer.ActivateGrid(Aspect_GT_Rectangular,
Aspect_GDM_Lines)
viewer.ActivateGrid(Aspect_GT_Rectangular, Aspect_GDM_Lines)
viewer.SetRectangularGridGraphicValues(size, size, 0)
viewer.SetRectangularGridValues(0, 0, step, step, 0)
grid = viewer.Grid()
grid.SetColors(Quantity_Color(*color1,TOC_RGB),
Quantity_Color(*color2,TOC_RGB))
grid.SetColors(
Quantity_Color(*color1, TOC_RGB), Quantity_Color(*color2, TOC_RGB)
)
def hide_grid(self):
viewer = self._get_viewer()
viewer.DeactivateGrid()
@pyqtSlot(bool,float)
@pyqtSlot(bool, float)
@pyqtSlot(bool)
def toggle_grid(self,
value : bool,
dim : float = 10.):
def toggle_grid(self, value: bool, dim: float = 10.0):
if value:
self.show_grid(step=dim/20,size=dim+1e-9)
self.show_grid(step=dim / 20, size=dim + 1e-9)
else:
self.hide_grid()
@pyqtSlot(gp_Ax3)
def set_grid_orientation(self,orientation : gp_Ax3):
def set_grid_orientation(self, orientation: gp_Ax3):
viewer = self._get_viewer()
viewer.SetPrivilegedPlane(orientation)
def show_axis(self,origin = (0,0,0), direction=(0,0,1)):
ax_placement = Geom_Axis1Placement(gp_Ax1(gp_Pnt(*origin),
gp_Dir(*direction)))
def show_axis(self, origin=(0, 0, 0), direction=(0, 0, 1)):
ax_placement = Geom_Axis1Placement(gp_Ax1(gp_Pnt(*origin), gp_Dir(*direction)))
ax = AIS_Axis(ax_placement)
self._display_ais(ax)
def save_screenshot(self):
fname = get_save_filename(self.IMAGE_EXTENSIONS)
if fname != '':
self._get_view().Dump(fname)
def _display_ais(self,ais):
if fname != "":
self._get_view().Dump(fname)
def _display_ais(self, ais):
self._get_context().Display(ais)
def _get_view(self):
return self.canvas.view
def _get_viewer(self):
return self.canvas.viewer
def _get_context(self):
return self.canvas.context
@pyqtSlot(list)
def handle_selection(self,obj):
def handle_selection(self, obj):
self.sigObjectSelected.emit(obj)
@pyqtSlot(list)
def set_selected(self,ais):
def set_selected(self, ais):
ctx = self._get_context()
ctx.ClearSelected(False)
for obj in ais:
ctx.AddOrRemoveSelected(obj,False)
ctx.AddOrRemoveSelected(obj, False)
self.redraw()
if __name__ == "__main__":
# pass
import sys
from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox
@@ -379,10 +423,10 @@ if __name__ == "__main__":
dlg.setFixedHeight(400)
dlg.setFixedWidth(600)
layout(dlg,(viewer,),dlg)
layout(dlg, (viewer,), dlg)
dlg.show()
box = BRepPrimAPI_MakeBox(20,20,30)
box = BRepPrimAPI_MakeBox(20, 20, 30)
box_ais = AIS_ColoredShape(box.Shape())
viewer.display(box_ais)