Source code for optimeed.visualize.gui.gui_data_selector

from PyQt5 import QtCore, QtWidgets
from optimeed.core.tools import rgetattr, indentParagraph
import sys
import traceback
import numpy as np
from abc import ABCMeta, abstractmethod


if QtWidgets.QApplication.instance() is None:
[docs] app = QtWidgets.QApplication(sys.argv) # must initialize only once
[docs]class Action_on_selector_update(metaclass=ABCMeta): @abstractmethod
[docs] def selector_updated(self, selection_name, the_collection, indices_data): """ Action to perform once the data have been selected :param selection_name: name of the selection (deprecated ?) :param the_collection: the collection :param indices_data: indices of the data :return: """ pass
[docs]class Attribute_selector: # Contains min max values for either list or scalar: # If list, displays from self.children_list [Attribute_selector] # Otherwise displays from self.min/max_value_box def __init__(self, attribute_name, value): self.attribute_name = attribute_name self.value = value self.min_value_box = None self.max_value_box = None self.children_list = []
[docs] def add_child(self, child): self.children_list.append(child)
[docs] def get_children(self): return self.children_list
[docs] def get_name(self): return '{:.50}'.format(self.attribute_name)
[docs] def get_min_max_attributes(self): min_max_attributes = [] def _get_min_max_attributes(the_attribute_selector, min_max_attr): if not len(the_attribute_selector.children_list): attribute_min_valid = True try: min_value = float(the_attribute_selector.min_value_box.text()) except ValueError: min_value = float("-inf") attribute_min_valid = False attribute_max_valid = True try: max_value = float(the_attribute_selector.max_value_box.text()) except ValueError: max_value = float("inf") attribute_max_valid = False if attribute_max_valid or attribute_min_valid: min_max_attr.append((min_value, max_value, the_attribute_selector.attribute_name)) else: for child in the_attribute_selector.get_children(): _get_min_max_attributes(child, min_max_attr) _get_min_max_attributes(self, min_max_attributes) return min_max_attributes
[docs] def __str__(self): theStr = '' for child in self.children_list: theStr += indentParagraph(str(child), 1) if not len(self.children_list): theStr += self.get_name() + ': \t' + str(self.value) return theStr
[docs]class Container_attribute_selector: def __init__(self, containerName): self.children = [] self.containerName = containerName self.attribute_selectors = []
[docs] def add_child(self, child): self.children.append(child)
[docs] def add_attribute_selector(self, attribute_selector): self.attribute_selectors.append(attribute_selector)
[docs] def set_attribute_selectors(self, attribute_selectors): self.attribute_selectors = attribute_selectors
[docs] def get_name(self): return self.containerName
[docs] def get_children(self): return self.children
[docs] def get_attribute_selectors(self): return self.attribute_selectors
[docs] def __str__(self): theStr = '' for item in self.attribute_selectors: theStr += str(item) + '\n' index = 1 for item in self.children: theStr += str(index) + '\n' theStr += indentParagraph(str(item)) index += 1 return theStr
[docs]class GuiDataSelector(QtWidgets.QMainWindow): def __init__(self, list_ListDataStruct_in, actionOnUpdate: Action_on_selector_update): """ Base on collection, display a list of variables that can be updated. When the button "update" is pressed, select the datas in Collection and call the method methodToCallWhenUpdated - args: collection_in (type Collection) containing the data to select Action_on_selector_update """ """Store variables""" self.theListDataStructs = list_ListDataStruct_in
[docs] self.theActionOnUpdate = actionOnUpdate
"""Generate GUI""" super(GuiDataSelector, self).__init__() self.mainbox = QtWidgets.QWidget() scrollarea = QtWidgets.QScrollArea() scrollarea.setWidget(self.mainbox) scrollarea.setWidgetResizable(True) scrollarea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setCentralWidget(scrollarea) layout = QtWidgets.QVBoxLayout(scrollarea) self.mainbox.setLayout(layout) # Create a layout for a global name self.selectionName = QtWidgets.QLineEdit("Default Name") self.mainbox.layout().addWidget(self.selectionName) # For each attribute, create new layout self.main_container = Container_attribute_selector('Device') get_attr_object(self.main_container, self.theListDataStructs[0].get_data()[0]) def collapse(theGroupBox, theFrame): if theGroupBox.isChecked(): theFrame.show() else: theFrame.hide() def create_frames(theContainer, parent_layout, curr_recursion): MAX_RECURSION = 2 theGroupBox = QtWidgets.QGroupBox(theContainer.get_name()) theGroupBox.setCheckable(True) theGroupBox.setChecked(True) parent_layout.addWidget(theGroupBox) # Little trick here: to be able to hide only the content, put a frame in a layout in the groupbox, and hide the frame new_layout_temp = QtWidgets.QVBoxLayout(theGroupBox) new_frame = QtWidgets.QFrame() theGroupBox.clicked.connect(lambda: collapse(theGroupBox, new_frame)) if curr_recursion >= MAX_RECURSION: new_frame.hide() theGroupBox.setChecked(False) new_layout_temp.addWidget(new_frame) new_layout = QtWidgets.QVBoxLayout(new_frame) for attribute_selector in theContainer.get_attribute_selectors(): # create horizontal_layout = QtWidgets.QHBoxLayout() new_layout.addLayout(horizontal_layout) # First: label label = QtWidgets.QLabel() label.setText(attribute_selector.get_name()) horizontal_layout.addWidget(label, alignment=QtCore.Qt.AlignTop) vertical_layout = QtWidgets.QVBoxLayout() horizontal_layout.addLayout(vertical_layout) min_value_layout = QtWidgets.QHBoxLayout() vertical_layout.addLayout(min_value_layout) min_value_layout.addStretch() max_value_layout = QtWidgets.QHBoxLayout() vertical_layout.addLayout(max_value_layout) max_value_layout.addStretch() separator = QtWidgets.QFrame() separator.setFrameShape(QtWidgets.QFrame.HLine) new_layout.addWidget(separator) # Create buttons create_buttons(attribute_selector, min_value_layout, max_value_layout) for child in theContainer.get_children(): create_frames(child, new_layout, curr_recursion=curr_recursion + 1) def create_buttons(the_attribute_selector, min_value_layout, max_value_layout): if not len(the_attribute_selector.get_children()): the_attribute_selector.min_value_box = QtWidgets.QLineEdit(None) the_attribute_selector.min_value_box.setFixedWidth(50) min_value_layout.addWidget(the_attribute_selector.min_value_box, alignment=QtCore.Qt.AlignLeft) the_attribute_selector.max_value_box = QtWidgets.QLineEdit(None) the_attribute_selector.max_value_box.setFixedWidth(50) max_value_layout.addWidget(the_attribute_selector.max_value_box, alignment=QtCore.Qt.AlignLeft) else: for child in the_attribute_selector.get_children(): create_buttons(child, min_value_layout, max_value_layout) create_frames(self.main_container, layout, 1) # Create a button in the window dock = QtWidgets.QDockWidget(self) button = QtWidgets.QPushButton('Update', self) dock.setWidget(button) self.addDockWidget(QtCore.Qt.TopDockWidgetArea, dock) button.clicked.connect(self.apply_filters) self.resize(self.mainbox.sizeHint())
[docs] def apply_filters(self, _): try: for collection in self.theListDataStructs: theData = collection.get_data() selected_data_indices = [] for data_index in range(len(theData)): the_item = theData[data_index] if is_object_selected(self.main_container, the_item): selected_data_indices.append(data_index) self.theActionOnUpdate.selector_updated(self.selectionName.text(), collection, selected_data_indices) except KeyboardInterrupt: raise except Exception: print("Following error occurred in selector :" + traceback.format_exc())
# textboxValue = self.textbox.text()
[docs] def run(self): self.show()
# app.exec_()
[docs]def is_object_selected(container_in, object_in): for attribute_selector in container_in.get_attribute_selectors(): min_max_attribute = attribute_selector.get_min_max_attributes() for min_attr, max_attr, name_attr in min_max_attribute: # Reverse boundaries if max < min if min_attr > max_attr: buff = min_attr min_attr = max_attr max_attr = buff # Check if item satisfies the condition try: if not (min_attr <= rgetattr(object_in, name_attr) <= max_attr): return False except IndexError: pass return all([is_object_selected(container, rgetattr(object_in, container.get_name())) for container in container_in.get_children()])
[docs]def check_and_add_if_float(the_container, attribute_value, attribute_name, parent=None): if isinstance(attribute_value, float) or isinstance(attribute_value, int): new_attribute = Attribute_selector(attribute_name, attribute_value) if parent is not None: parent.add_child(new_attribute) # We put the value to the parent (because it is a list) else: the_container.add_attribute_selector(new_attribute) # We put the value to the container return True return False
[docs]def manage_list(the_container, in_object, _listOfValues, _listName): new_attribute = Attribute_selector(_listName, _listOfValues) if all(isinstance(n, int) or isinstance(n, float) for n in _listOfValues): the_container.add_attribute_selector(new_attribute) for index in range(len(_listOfValues)): if index < 10: attribute_name_nested = _listName + '[' + str(index) + ']' attribute_value_nested = rgetattr(in_object, attribute_name_nested) check_and_add_if_float(the_container, attribute_value_nested, attribute_name_nested, parent=new_attribute) elif all(getattr(n, '__dict__', None) is not None for n in _listOfValues): # If has "__dict__" attribute, more likely a class ;) index = 0 for item in _listOfValues: new_container = Container_attribute_selector(_listName + '[' + str(index) + ']') the_container.add_child(new_container) get_attr_object(new_container, item) index += 1 else: pass
[docs]def get_attr_object(the_container, in_object): for attribute_name in vars(in_object): attribute_value = rgetattr(in_object, attribute_name) if isinstance(attribute_value, list) or isinstance(attribute_value, np.ndarray): manage_list(the_container, in_object, attribute_value, attribute_name) elif getattr(attribute_value, '__dict__', None) is not None: new_container = Container_attribute_selector(attribute_name) the_container.add_child(new_container) get_attr_object(new_container, attribute_value) else: check_and_add_if_float(the_container, attribute_value, attribute_name)