FEM: Post data visualization bug fixes and quality of life updates

This commit is contained in:
Stefan Tröger
2025-04-21 12:41:37 +02:00
parent a5ac5571b7
commit 0fb7c3cc5c
7 changed files with 134 additions and 42 deletions

View File

@@ -245,9 +245,6 @@ class _SummaryWidget(QtGui.QWidget):
# build the UI
self.stButton = self._button(st_object.Label)
self.stButton.setIcon(st_object.ViewObject.Icon)
self.extrButton = self._button(extr_label)
self.extrButton.setIcon(extractor.ViewObject.Icon)
@@ -260,6 +257,19 @@ class _SummaryWidget(QtGui.QWidget):
else:
self.viewButton.setIconSize(QtCore.QSize(0,0))
if st_object:
self.stButton = self._button(st_object.Label)
self.stButton.setIcon(st_object.ViewObject.Icon)
else:
# that happens if the source of the extractor was deleted and now
# that property is set to None
self.extrButton.hide()
self.viewButton.hide()
self.warning = QtGui.QLabel(self)
self.warning.full_text = f"{extractor.Label}: Data source not available"
self.rmButton = QtGui.QToolButton(self)
self.rmButton.setIcon(QtGui.QIcon.fromTheme("delete"))
self.rmButton.setAutoRaise(True)
@@ -270,13 +280,15 @@ class _SummaryWidget(QtGui.QWidget):
policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.setSizePolicy(policy)
self.setMinimumSize(self.stButton.sizeHint()+self.frame.sizeHint()*3)
self.setMinimumSize(self.extrButton.sizeHint()+self.frame.sizeHint()*3)
# connect actions. We add functions to widget, as well as the data we need,
# and use those as callback. This way every widget knows which objects to use
self.stButton.clicked.connect(self.showVisualization)
self.extrButton.clicked.connect(self.editApp)
self.viewButton.clicked.connect(self.editView)
if st_object:
self.stButton.clicked.connect(self.showVisualization)
self.extrButton.clicked.connect(self.editApp)
self.viewButton.clicked.connect(self.editView)
self.rmButton.clicked.connect(self.deleteTriggered)
# make sure initial drawing happened
@@ -300,37 +312,47 @@ class _SummaryWidget(QtGui.QWidget):
btn_total_size = ((self.size() - self.rmButton.size()).width() - 20) #20 is space to rmButton
btn_margin = (self.rmButton.size() - self.rmButton.iconSize()).width()
fm = self.fontMetrics()
min_text_width = fm.size(QtGui.Qt.TextSingleLine, "...").width()*2
pos = 0
btns = [self.stButton, self.extrButton, self.viewButton]
btn_rel_size = [0.4, 0.4, 0.2]
btn_elide_mode = [QtGui.Qt.ElideMiddle, QtGui.Qt.ElideMiddle, QtGui.Qt.ElideRight]
for i, btn in enumerate(btns):
if self._st_object:
btn_size = btn_total_size*btn_rel_size[i]
txt_size = btn_size - btn.iconSize().width() - btn_margin
min_text_width = fm.size(QtGui.Qt.TextSingleLine, "...").width()*2
# we elide only if there is enough space for a meaningful text
if txt_size >= min_text_width:
pos = 0
btns = [self.stButton, self.extrButton, self.viewButton]
btn_rel_size = [0.4, 0.4, 0.2]
btn_elide_mode = [QtGui.Qt.ElideMiddle, QtGui.Qt.ElideMiddle, QtGui.Qt.ElideRight]
for i, btn in enumerate(btns):
text = fm.elidedText(btn.full_text, btn_elide_mode[i], txt_size)
btn.setText(text)
btn.setStyleSheet("text-align:left;padding:6px");
else:
btn.setText("")
btn.setStyleSheet("text-align:center;");
btn_size = btn_total_size*btn_rel_size[i]
txt_size = btn_size - btn.iconSize().width() - btn_margin
rect = QtCore.QRect(pos,0, btn_size, btn.sizeHint().height())
btn.setGeometry(rect)
pos+=btn_size
# we elide only if there is enough space for a meaningful text
if txt_size >= min_text_width:
rmsize = self.stButton.height()
text = fm.elidedText(btn.full_text, btn_elide_mode[i], txt_size)
btn.setText(text)
btn.setStyleSheet("text-align:left;padding:6px");
else:
btn.setText("")
btn.setStyleSheet("text-align:center;");
rect = QtCore.QRect(pos,0, btn_size, btn.sizeHint().height())
btn.setGeometry(rect)
pos+=btn_size
else:
warning_txt = fm.elidedText(self.warning.full_text, QtGui.Qt.ElideRight, btn_total_size)
self.warning.setText(warning_txt)
rect = QtCore.QRect(0,0, btn_total_size, self.extrButton.sizeHint().height())
self.warning.setGeometry(rect)
rmsize = self.extrButton.sizeHint().height()
pos = self.size().width() - rmsize
self.rmButton.setGeometry(pos, 0, rmsize, rmsize)
frame_hint = self.frame.sizeHint()
rect = QtCore.QRect(0, self.stButton.height()+frame_hint.height(), self.size().width(), frame_hint.height())
rect = QtCore.QRect(0, self.extrButton.sizeHint().height()+frame_hint.height(), self.size().width(), frame_hint.height())
self.frame.setGeometry(rect)
def resizeEvent(self, event):
@@ -563,6 +585,7 @@ class ExtractLinkView(QtGui.QWidget):
FreeCADGui.doCommand(
f"visualization = {vis_data.module}.{vis_data.factory}(FreeCAD.ActiveDocument)"
)
analysis = self._find_parent_analysis(self._object)
if analysis:
FreeCADGui.doCommand(
@@ -576,10 +599,18 @@ class ExtractLinkView(QtGui.QWidget):
FreeCADGui.doCommand(
f"extraction.Source = FreeCAD.ActiveDocument.{self._object.Name}"
)
# default values: color
color_prop = FreeCADGui.ActiveDocument.ActiveObject.Proxy.get_default_color_property()
if color_prop:
FreeCADGui.doCommand(
f"extraction.ViewObject.{color_prop} = visualization.ViewObject.Proxy.get_next_default_color()"
)
FreeCADGui.doCommand(
f"visualization.addObject(extraction)"
)
self._post_dialog._recompute()
self.repopulate()
@@ -596,6 +627,14 @@ class ExtractLinkView(QtGui.QWidget):
FreeCADGui.doCommand(
f"extraction.Source = FreeCAD.ActiveDocument.{self._object.Name}"
)
# default values: color
color_prop = FreeCADGui.ActiveDocument.ActiveObject.Proxy.get_default_color_property()
if color_prop:
FreeCADGui.doCommand(
f"extraction.ViewObject.{color_prop} = (Gui.ActiveDocument.{vis_obj.Name}.Proxy.get_next_default_color())"
)
FreeCADGui.doCommand(
f"App.ActiveDocument.{vis_obj.Name}.addObject(extraction)"
)
@@ -616,6 +655,14 @@ class ExtractLinkView(QtGui.QWidget):
FreeCADGui.doCommand(
f"extraction.Source = FreeCAD.ActiveDocument.{post_obj.Name}"
)
# default values for color
color_prop = FreeCADGui.ActiveDocument.ActiveObject.Proxy.get_default_color_property()
if color_prop:
FreeCADGui.doCommand(
f"extraction.ViewObject.{color_prop} = Gui.ActiveDocument.{self._object.Name}.Proxy.get_next_default_color()"
)
FreeCADGui.doCommand(
f"App.ActiveDocument.{self._object.Name}.addObject(extraction)"
)

View File

@@ -60,14 +60,6 @@ class _BasePostTaskPanel(base_femtaskpanel._BaseTaskPanel):
if button == QtGui.QDialogButtonBox.Apply:
self.obj.Document.recompute()
def accept(self):
print("accept")
return super().accept()
def reject(self):
print("reject")
return super().reject()
# Helper functions
# ################

View File

@@ -118,6 +118,16 @@ class VPPostExtractor:
# To be implemented by subclasses:
# ################################
def get_default_color_property(self):
# Returns the property name to set the default color to.
# Return None if no such property
raise FreeCAD.Base.FreeCADError("Not implemented")
def get_default_field_properties(self):
# Returns the property name to which the default field name should be set
# ret: [FieldProperty, ComponentProperty]
raise FreeCAD.Base.FreeCADError("Not implemented")
def get_kw_args(self):
# Returns the matplotlib plot keyword arguments that represent the
# properties of the object.

View File

@@ -32,6 +32,7 @@ __url__ = "https://www.freecad.org"
from PySide import QtGui, QtCore
import Plot
import FreeCAD
import FreeCADGui
from . import view_base_femobject
@@ -68,6 +69,8 @@ class VPPostVisualization:
# Mark ourself as visible in the tree
return True
def getDisplayModes(self, obj):
return ["Dialog"]
def doubleClicked(self,vobj):
@@ -122,3 +125,10 @@ class VPPostVisualization:
def show_visualization(self):
# Shows the visualization without going into edit mode
raise FreeCAD.Base.FreeCADError("Not implemented")
def get_next_default_color(self):
# Returns the next default color a new object should use
# Returns color in FreeCAD proeprty notation (r,g,b,a)
# If the relevant extractors do not have color properties, this
# can stay unimplemented
raise FreeCAD.Base.FreeCADError("Not implemented")

View File

@@ -289,7 +289,7 @@ class VPPostHistogramFieldData(view_base_fempostextractors.VPPostExtractor):
name="LineColor",
group="HistogramLine",
doc="The color the data bin area is drawn with",
value=(0, 85, 255, 255),
value=(0, 0, 0, 1), # black
),
_GuiPropHelper(
type="App::PropertyFloatConstraint",
@@ -355,6 +355,9 @@ class VPPostHistogramFieldData(view_base_fempostextractors.VPPostExtractor):
return kwargs
def get_default_color_property(self):
return "BarColor"
class VPPostHistogramIndexOverFrames(VPPostHistogramFieldData):
"""
@@ -477,8 +480,10 @@ class VPPostHistogram(view_base_fempostvisualization.VPPostVisualization):
def show_visualization(self):
if not hasattr(self, "_plot") or not self._plot:
main = Plot.getMainWindow()
main = FreeCADGui.getMainWindow()
self._plot = Plot.Plot()
self._plot.setParent(main)
self._plot.setWindowFlags(QtGui.Qt.Dialog)
self._plot.resize(main.size().height()/2, main.size().height()/3) # keep it square
self.update_visualization()
@@ -565,3 +570,9 @@ class VPPostHistogram(view_base_fempostvisualization.VPPostVisualization):
self._plot.update()
def get_next_default_color(self):
# we use the next color in order. We do not check (yet) if this
# color is already taken
i = len(self.Object.Group)
cmap = mpl.pyplot.get_cmap("tab10")
return cmap(i)

View File

@@ -354,6 +354,9 @@ class VPPostLineplotFieldData(view_base_fempostextractors.VPPostExtractor):
kwargs["markersize"] = self.ViewObject.MarkerSize
return kwargs
def get_default_color_property(self):
return "Color"
class VPPostLineplotIndexOverFrames(VPPostLineplotFieldData):
"""
@@ -387,7 +390,7 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization):
name="Grid",
group="Lineplot",
doc="If be the bars shoud show the cumulative sum left to rigth",
value=False,
value=True,
),
_GuiPropHelper(
type="App::PropertyEnumeration",
@@ -455,8 +458,10 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization):
def show_visualization(self):
if not hasattr(self, "_plot") or not self._plot:
main = Plot.getMainWindow()
main = FreeCADGui.getMainWindow()
self._plot = Plot.Plot()
self._plot.setParent(main)
self._plot.setWindowFlags(QtGui.Qt.Dialog)
self._plot.resize(main.size().height()/2, main.size().height()/3) # keep the aspect ratio
self.update_visualization()
@@ -481,6 +486,7 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization):
# we do not iterate the table, but iterate the children. This makes it possible
# to attribute the correct styles
plotted = False
for child in self.Object.Group:
table = child.Table
@@ -492,6 +498,8 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization):
for i in range(0,table.GetNumberOfColumns(),2):
plotted = True
# add the kw args, with some slide change over color for multiple frames
tmp_args = {}
for key in kwargs:
@@ -534,10 +542,16 @@ class VPPostLineplot(view_base_fempostvisualization.VPPostVisualization):
if self.ViewObject.YLabel:
self._plot.axes.set_ylabel(self.ViewObject.YLabel)
if self.ViewObject.Legend and self.Object.Group:
if self.ViewObject.Legend and plotted:
self._plot.axes.legend(loc = self.ViewObject.LegendLocation)
self._plot.axes.grid(self.ViewObject.Grid)
self._plot.update()
def get_next_default_color(self):
# we use the next color in order. We do not check (yet) if this
# color is already taken
i = len(self.Object.Group)
cmap = mpl.pyplot.get_cmap("tab10")
return cmap(i)

View File

@@ -211,6 +211,9 @@ class VPPostTableFieldData(view_base_fempostextractors.VPPostExtractor):
name = self.ViewObject.Name
return (QtGui.QPixmap(), name)
def get_default_color_property(self):
return None
class VPPostTableIndexOverFrames(VPPostTableFieldData):
"""
@@ -254,8 +257,13 @@ class VPPostTable(view_base_fempostvisualization.VPPostVisualization):
def show_visualization(self):
if not hasattr(self, "_tableview") or not self._tableview:
main = FreeCADGui.getMainWindow()
self._tableModel = vtv.VtkTableModel()
self._tableview = vtv.VtkTableView(self._tableModel)
self._tableview.setParent(main)
self._tableview.setWindowFlags(QtGui.Qt.Dialog)
self._tableview.resize(main.size().height()/2, main.size().height()/3) # keep the aspect ratio
self.update_visualization()
self._tableview.show()