class QueryController(QtCore.QObject): """ The QueryController is a QObject that contains the search widget which is a QDockWidget and the tree widget which is a QTreeWidget Attributes ---------- _keys : list List of search keys that will be displayed in the _query_input widget _key_descriptions : list List of descriptions for the keys that will appear as a tool tip on mouse hover _query_input : QtGui.QWidget The widget that displays a series of text input boxes with a 'search' button _results_tree : xray_vision.qt_widgets.displaydict.RecursiveTreeWidget The widget that displays the results as a tree with an 'add' button _search_dict : dict Dictionary that was unpacked into the search function. This attribute gets stored every time the 'search' button gets clicked _search_results : list List of dictionaries that the search function returns Methods ------- update_search_results(results_list) Populate the RecursiveTreeWidget with the results_list enable_add_btn(bool) Enable/disable the add button enable_search_btn(bool) Enable/disable the search button add() Function that executes when the 'add' button is clicked search() Function that executes when the 'search' button is clicked read_search_boxes() Read the text from the search boxes to form a search dictionary, stored as _search_dict update_query_keys(keys, key_descriptions=None) Remake the query widget with new query keys and key_descriptions """ # external handles for the add button and search button add_btn_sig = QtCore.Signal(dict, dict, dict, list) search_btn_sig = QtCore.Signal(dict) ################################################################### # Construction time behavior # ################################################################### def __init__(self, keys, add_btn_text="Add", *args, **kwargs): """ Parameters ---------- keys : dict keys = { "key1" : { "description" : "this is what key1 is for", "type" : "this is the type of key1", } } add_btn_text : str Label for the add button """ # call up the inheritance chain super(QueryController, self).__init__(*args, **kwargs) self._keys = keys # set up the query widget self._query_input = self.construct_query() # set up the results widget self._results_tree = self.construct_results(add_btn_text) self._search_dict = _defaults["empty_search"] self.update_search_results(self._search_dict) def construct_query(self): """ Construct the query widget Returns ------- QtGui.QGroupBox group box that contains the query widget """ # declare the group box query = QtGui.QGroupBox(title="Query") # declare the search button self._search_btn = QtGui.QPushButton(text="&Search") # connect the search buttons clicked signal to the method which parses # the text boxes to create a search dictionary that gets emitted by the # externally facing search_btn_sig QtCore.Signal self._search_btn.clicked.connect(self.search) # declare the query widget query_widg = self.construct_query_input() # declare the layout as a vertical box layout layout = QtGui.QVBoxLayout() # add the widgets to the layout layout.addWidget(query_widg) layout.addWidget(self._search_btn) # set the layout of the group box query.setLayout(layout) # return the widget return query def construct_results(self, add_btn_text): """ Construct the results widget Returns ------- QtGui.QGroupBox group box that contains the results widget along with the 'add' button """ # declare a group box _results = QtGui.QGroupBox(title="Results") # declare the layout as a vertical box layout = QtGui.QVBoxLayout() # declare the tree widget self._tree = RecursiveTreeWidget() # declare the "add to canvas" button self._add_btn = QtGui.QPushButton(text=add_btn_text) # connect the add button clicked signal to the externally facing # "add_btn_signal" QtCore.SIGNAL self._add_btn.clicked.connect(self.add) # add the tree widget to the layout layout.addWidget(self._tree) # add the button to the layout layout.addWidget(self._add_btn) # set the layout of the group box _results.setLayout(layout) # return the results group box return _results def construct_query_input(self, keys=None): """ Construct the input boxes for the query. Parameters ------- keys : dict keys = { "key1" : { "description" : "this is what key1 is for", "type" : "this is the type of key1", } } Returns ------- QWidget This is the widget that contains the search keys as labels and their input boxes typed on "type" """ # default behavior of keys input parameter if keys is None: keys = self._keys self._keys = keys # declare a vertical layout vert_layout = QtGui.QVBoxLayout() try: # if the input boxes dictionary exists, empty it self._input_boxes.clear() except AttributeError: # create a new dictionary self._input_boxes = {} _lookup_dict = {str: LineEdit, int: LineEdit, float: LineEdit, datetime.datetime: DateTimeBox, bool: CheckBox, list: ComboBox} # loop over the keys to create an input box for each key for key in keys: # declare a new horizontal layout horz_layout = QtGui.QHBoxLayout() # declare the label lbl = QtGui.QLabel(key) try: # get the description from the nested dict description = keys[key]["description"] except KeyError: # use the key as the description description = key try: # get the key_type from the nested dict key_type = keys[key]["type"] except KeyError: # default to string typed key_type = str input_box_type = _lookup_dict[key_type] # declare the input box input_box = input_box_type(label_text=key, hover_text=description, has_check_box=_defaults["has_check_box"]) # add the input box to the input_boxes dict self._input_boxes[key] = input_box # add the widgets to the layout horz_layout.addWidget(input_box) # set a dummy widget widg = QtGui.QWidget() widg.setLayout(horz_layout) # add the horizontal layout to the vertical layout vert_layout.addWidget(widg) query_input = QtGui.QWidget() query_input.setLayout(vert_layout) # return the vertical layout return query_input ############################################################################ # Runtime behavior # ############################################################################ def register_unique_id_gen_func(self, unique_id_func): """ Parameters ---------- unique_id_func : function Function that generates a unique ID for a results dictionary. For now, this function should probably just pick out the header_id """ self._unique_id_func = unique_id_func def enable_search_btn(self, is_enabled): """ Function to enable/disable the search button Parameters ---------- is_enabled : bool enables/disables the search button """ self._search_btn.setEnabled(is_enabled) def enable_add_btn(self, is_enabled): """ Function to enable/disable the search button Parameters ---------- is_enabled : bool enables/disables the search button """ self._add_btn.setEnabled(is_enabled) @QtCore.Slot() def add(self): """ Figure out which result is clicked and emit the add_btn_sig with the following arguments: dict1 : dict Dictionary of search keys used to generate the results shown in the tree widget dict2 : dict unique id dictionary that is guaranteed to return dict3 when unpacked into the registered search function dict3 : dict One results dictionary list : list path to the currently selected node in the tree widget """ # TODO Change this to debugger level logging logger.debug("add_clicked") path_to_node, result_idx = self._tree.find_root() print(self._search_results.__class__) res_keys = list(self._search_results) res_keys.sort() cur_result_dict = self._search_results[res_keys[result_idx]] print(list(cur_result_dict)) # todo ask the tree nicely for its currently selected dictionary # unique_id = tree.get_current() self.add_btn_sig.emit(self._search_dict, self.create_unique_id(cur_result_dict), cur_result_dict, path_to_node) def create_unique_id(self, result_dict): """ Call the unique id function that was registered Parameters ---------- result_dict : dict Dictionary that will be used to generate a unique id dictionary. Returns ------- unique_id : dict The unique id dictionary is guaranteed to produce "result_dict" when unpacked into the search function """ return self._unique_id_func(result_dict) @QtCore.Slot() def search(self): """ Parse the search boxes and emit it as a signal """ self.read_search_boxes() # once the dictionary is constructed, emit it as a signal self.search_btn_sig.emit(self._search_dict) @QtCore.Slot() def read_search_boxes(self): """ Parse the search boxes to set up the query dictionary and store it as an instance variable "_search_dict" """ # declare the search dict # TODO Change this to debugger level logging @tacaswell logger.debug("read_search_boxes") self._search_dict = {} print(self._input_boxes) try: # loop over the list of input boxes to extract the search string # todo need better list comprehension self._search_dict = {key: self._input_boxes[key].getValue() for key in self._input_boxes if self._input_boxes[key].getValue() is not None} except AttributeError as e: tb = traceback.format_exc() logger.error(tb) # the only time this will be caught is in the initial setup and it # is therefore ok to ignore this error pass @QtCore.Slot(list) def update_search_results(self, results): """ Pass the search results to the recursive tree widget which displays them Parameters ---------- results : array, list, object """ # stash the search results for later use self._search_results = results self._tree.fill_widget(results) self.enable_add_btn(is_enabled=True)
class QueryController(QtCore.QObject): """ The QueryController is a QObject that contains the search widget which is a QDockWidget and the tree widget which is a QTreeWidget Attributes ---------- _keys : list List of search keys that will be displayed in the _query_input widget _key_descriptions : list List of descriptions for the keys that will appear as a tool tip on mouse hover _query_input : QtGui.QWidget The widget that displays a series of text input boxes with a 'search' button _results_tree : xray_vision.qt_widgets.displaydict.RecursiveTreeWidget The widget that displays the results as a tree with an 'add' button _search_dict : dict Dictionary that was unpacked into the search function. This attribute gets stored every time the 'search' button gets clicked _search_results : list List of dictionaries that the search function returns Methods ------- update_search_results(results_list) Populate the RecursiveTreeWidget with the results_list enable_add_btn(bool) Enable/disable the add button enable_search_btn(bool) Enable/disable the search button add() Function that executes when the 'add' button is clicked search() Function that executes when the 'search' button is clicked read_search_boxes() Read the text from the search boxes to form a search dictionary, stored as _search_dict update_query_keys(keys, key_descriptions=None) Remake the query widget with new query keys and key_descriptions """ # external handles for the add button and search button add_btn_sig = QtCore.Signal(dict, dict, dict, list) search_btn_sig = QtCore.Signal(dict) ################################################################### # Construction time behavior # ################################################################### def __init__(self, keys, add_btn_text="Add", *args, **kwargs): """ Parameters ---------- keys : dict keys = { "key1" : { "description" : "this is what key1 is for", "type" : "this is the type of key1", } } add_btn_text : str Label for the add button """ # call up the inheritance chain super(QueryController, self).__init__(*args, **kwargs) self._keys = keys # set up the query widget self._query_input = self.construct_query() # set up the results widget self._results_tree = self.construct_results(add_btn_text) self._search_dict = _defaults["empty_search"] self.update_search_results(self._search_dict) def construct_query(self): """ Construct the query widget Returns ------- QtGui.QGroupBox group box that contains the query widget """ # declare the group box query = QtGui.QGroupBox(title="Query") # declare the search button self._search_btn = QtGui.QPushButton(text="&Search") # connect the search buttons clicked signal to the method which parses # the text boxes to create a search dictionary that gets emitted by the # externally facing search_btn_sig QtCore.Signal self._search_btn.clicked.connect(self.search) # declare the query widget query_widg = self.construct_query_input() # declare the layout as a vertical box layout layout = QtGui.QVBoxLayout() # add the widgets to the layout layout.addWidget(query_widg) layout.addWidget(self._search_btn) # set the layout of the group box query.setLayout(layout) # return the widget return query def construct_results(self, add_btn_text): """ Construct the results widget Returns ------- QtGui.QGroupBox group box that contains the results widget along with the 'add' button """ # declare a group box _results = QtGui.QGroupBox(title="Results") # declare the layout as a vertical box layout = QtGui.QVBoxLayout() # declare the tree widget self._tree = RecursiveTreeWidget() # declare the "add to canvas" button self._add_btn = QtGui.QPushButton(text=add_btn_text) # connect the add button clicked signal to the externally facing # "add_btn_signal" QtCore.SIGNAL self._add_btn.clicked.connect(self.add) # add the tree widget to the layout layout.addWidget(self._tree) # add the button to the layout layout.addWidget(self._add_btn) # set the layout of the group box _results.setLayout(layout) # return the results group box return _results def construct_query_input(self, keys=None): """ Construct the input boxes for the query. Parameters ------- keys : dict keys = { "key1" : { "description" : "this is what key1 is for", "type" : "this is the type of key1", } } Returns ------- QWidget This is the widget that contains the search keys as labels and their input boxes typed on "type" """ # default behavior of keys input parameter if keys is None: keys = self._keys self._keys = keys # declare a vertical layout vert_layout = QtGui.QVBoxLayout() try: # if the input boxes dictionary exists, empty it self._input_boxes.clear() except AttributeError: # create a new dictionary self._input_boxes = {} _lookup_dict = { str: LineEdit, int: LineEdit, float: LineEdit, datetime.datetime: DateTimeBox, bool: CheckBox, list: ComboBox } # loop over the keys to create an input box for each key for key in keys: # declare a new horizontal layout horz_layout = QtGui.QHBoxLayout() # declare the label lbl = QtGui.QLabel(key) try: # get the description from the nested dict description = keys[key]["description"] except KeyError: # use the key as the description description = key try: # get the key_type from the nested dict key_type = keys[key]["type"] except KeyError: # default to string typed key_type = str input_box_type = _lookup_dict[key_type] # declare the input box input_box = input_box_type( label_text=key, hover_text=description, has_check_box=_defaults["has_check_box"]) # add the input box to the input_boxes dict self._input_boxes[key] = input_box # add the widgets to the layout horz_layout.addWidget(input_box) # set a dummy widget widg = QtGui.QWidget() widg.setLayout(horz_layout) # add the horizontal layout to the vertical layout vert_layout.addWidget(widg) query_input = QtGui.QWidget() query_input.setLayout(vert_layout) # return the vertical layout return query_input ############################################################################ # Runtime behavior # ############################################################################ def register_unique_id_gen_func(self, unique_id_func): """ Parameters ---------- unique_id_func : function Function that generates a unique ID for a results dictionary. For now, this function should probably just pick out the header_id """ self._unique_id_func = unique_id_func def enable_search_btn(self, is_enabled): """ Function to enable/disable the search button Parameters ---------- is_enabled : bool enables/disables the search button """ self._search_btn.setEnabled(is_enabled) def enable_add_btn(self, is_enabled): """ Function to enable/disable the search button Parameters ---------- is_enabled : bool enables/disables the search button """ self._add_btn.setEnabled(is_enabled) @QtCore.Slot() def add(self): """ Figure out which result is clicked and emit the add_btn_sig with the following arguments: dict1 : dict Dictionary of search keys used to generate the results shown in the tree widget dict2 : dict unique id dictionary that is guaranteed to return dict3 when unpacked into the registered search function dict3 : dict One results dictionary list : list path to the currently selected node in the tree widget """ # TODO Change this to debugger level logging logger.debug("add_clicked") path_to_node, result_idx = self._tree.find_root() print(self._search_results.__class__) res_keys = list(self._search_results) res_keys.sort() cur_result_dict = self._search_results[res_keys[result_idx]] print(list(cur_result_dict)) # todo ask the tree nicely for its currently selected dictionary # unique_id = tree.get_current() self.add_btn_sig.emit(self._search_dict, self.create_unique_id(cur_result_dict), cur_result_dict, path_to_node) def create_unique_id(self, result_dict): """ Call the unique id function that was registered Parameters ---------- result_dict : dict Dictionary that will be used to generate a unique id dictionary. Returns ------- unique_id : dict The unique id dictionary is guaranteed to produce "result_dict" when unpacked into the search function """ return self._unique_id_func(result_dict) @QtCore.Slot() def search(self): """ Parse the search boxes and emit it as a signal """ self.read_search_boxes() # once the dictionary is constructed, emit it as a signal self.search_btn_sig.emit(self._search_dict) @QtCore.Slot() def read_search_boxes(self): """ Parse the search boxes to set up the query dictionary and store it as an instance variable "_search_dict" """ # declare the search dict # TODO Change this to debugger level logging @tacaswell logger.debug("read_search_boxes") self._search_dict = {} print(self._input_boxes) try: # loop over the list of input boxes to extract the search string # todo need better list comprehension self._search_dict = { key: self._input_boxes[key].getValue() for key in self._input_boxes if self._input_boxes[key].getValue() is not None } except AttributeError as e: tb = traceback.format_exc() logger.error(tb) # the only time this will be caught is in the initial setup and it # is therefore ok to ignore this error pass @QtCore.Slot(list) def update_search_results(self, results): """ Pass the search results to the recursive tree widget which displays them Parameters ---------- results : array, list, object """ # stash the search results for later use self._search_results = results self._tree.fill_widget(results) self.enable_add_btn(is_enabled=True)