Open RubendeBruin opened 2 months ago
Default field if no argument is specified (no :
)
The default would really depend on the context: selections in gui: type reporting: tag or name
but we can add shorthands:
k: --> type (not t as that is used for tag) n: --> name t: --> tag
For clarity the fuzzy auto-completed version of the user input should be shown in the Gui as feedback (and maybe as auto-complete as well ???)
For auto-completion we need a scene (for the node names and possible parents and such)
WIP in scratch 52
from dataclasses import fields
from DAVE import *
from DAVE.settings import DAVE_ADDITIONAL_RUNTIME_MODULES
from DAVE.tools import MostLikelyMatch
def make_node_selector_query(user_string, s) -> tuple[str,list[str]]:
# user_string = "type: Poi"
errors = []
# cut into blocks
blocks = user_string.replace(':', ':,').replace(' ',',').split(',')
# remove empty blocks
blocks = [b for b in blocks if b]
if not blocks:
return '', ['No input']
# parse values into a dict with keys, where keys are identified by the string ending with :
# first block has to be a key
if blocks[0][-1] != ':':
blocks.insert(0, 'type:')
raw_tokens = dict()
values = None
for block in blocks:
if block[-1] == ':':
if values:
raw_tokens[key[:-1]] = values
key = block
values = []
continue
values.append(block) # first block as alwyas a key, so we can append to values
if key not in raw_tokens.keys():
raw_tokens[key[:-1]] = values
print(raw_tokens)
# clean up the keys
# subsitutions
substitutions = {'type': 'kind',
'kind': 'type',
'k': 'kind',
't' : 'tag',
'tag' : 'tag',
'tags' : 'tag',
'name': 'name',
'names': 'name',
'n': 'name',
}
# get all field names from the NodeSelector data-class
for f in fields(NodeSelector):
name = f.name
substitutions[name] = name
# apply substitutions
fields_clean = dict()
for key, value in raw_tokens.items():
if key in substitutions.keys():
fields_clean[substitutions[key]] = value
else:
errors.append(f'Unknown key: {key}')
print(fields_clean)
node_types = []
for string, kind in DAVE_ADDITIONAL_RUNTIME_MODULES.items():
if issubclass(kind, Node):
node_types.append(string)
node_names = s.node_names
# found node-type:
print(node_types)
# Now fill in the values using the intended types
all_clean = dict()
for key, value in fields_clean.items():
values = []
if not value:
pass
elif key == 'kind' or key == 'kind_not':
for v in value:
if v in node_types:
values.append(v)
else:
values.append(MostLikelyMatch(v, node_types))
elif key == 'name':
if len(values) > 1:
errors.append(f'Only one name allowed, ignoring {values[1:]}')
values.append(value[0]) # can only have one name
elif key == 'tag':
values = value
elif key == 'managed':
if len(value) > 1:
errors.append(f'Only one managed allowed, ignoring {values[1:]}')
if value[0][0].lower() in ['t','y']:
values = True
else:
values = False
elif key in ['on','family_of','core_connected_to','fixed_to']:
if len(value) > 1:
errors.append(f'Only one node allowed for {key}, ignoring {values[1:]}')
v = value[0]
if v in node_names:
values.append(v)
else:
errors.append(f'No node with name {v} - using most likely match instead')
values.append(MostLikelyMatch(v, node_names))
all_clean[key] = values
print(all_clean)
print(errors)
# make a query
q = ""
for key, value in all_clean.items():
q += key + ': '
if isinstance(value, (str, bool)):
q += str(value) + ' '
for v in value:
q += v + ' '
return q, errors
s = Scene("res: cheetah with crane and 4p block.dave")
make_node_selector_query("Cheetah on:",s)
# using pyside6, make a window with a text box and a label
# when the text box is changed, the label should show the result of make_node_selector_query
# hint: use the signal textChanged
# hint: use the slot make_node_selector_query
# hint: use the signal textChanged.connect(make_node_selector_query)
# hint: use a QVBoxLayout with a QLineEdit and a QLabel
# hint: use the QVBoxLayout.addWidget method to add the QLineEdit and QLabel to the layout
# hint: use the QWidget.setLayout method to set the layout of the window
# hint: use the QWidget.show method to show the window
# hint: use the QApplication.exec method to start the event loop
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit, QLabel, QCompleter
from PySide6.QtCore import QStringListModel
app = QApplication([])
window = QWidget()
layout = QVBoxLayout()
text = QLineEdit()
label = QLabel()
label2 = QLabel()
# Create a QCompleter
completer = QCompleter()
# Set the model for the completer
model = QStringListModel()
completer.setModel(model)
# Set the completer for the QLineEdit
text.setCompleter(completer)
# Set the completion string
model.setStringList(["Your completion string"])
# completer.setFilterMode(Qt.MatchContains)
completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
text.setText('Cheetah')
def update():
t = text.text()
r, _ = make_node_selector_query(t, s)
model.setStringList([str(r)])
label.setText(str(r))
label2.setText(str(_))
nodes = s.nodes_where(r)
label3.setText()
text.textChanged.connect(update)
layout.addWidget(label2)
layout.addWidget(text)
layout.addWidget(label)
window.setLayout(layout)
window.show()
app.exec()
The NodeSelector dataclass
provides a powerfull way to filter or select nodes. But it needs to be exposed to the user as well such that it can be used in the GUI.
Use For the filter above the tree In report sections to select nodes To filter visuals in the viewport To filter nodes in the viewport selection
How Outlook has a similar feature to filter messages, there the From: syntax is used.
type: Point, Frame name: F
--> name = "F"F
--> name = "Fname: "F"
--> name = "F"interpretation:
parsing values