def down_list(tree: QTreeWidget): selected = tree.currentItem() if selected: index = tree.indexOfTopLevelItem(selected) name = selected.text(0) desc = selected.text(1) data = selected.data(2, 2) new = QTreeWidgetItem([name, desc]) new.setData(2, 2, data) tree.takeTopLevelItem(index) tree.insertTopLevelItem(index + 1, new) tree.setCurrentItem(new)
class ObjectsBrowserWindow(QWidget): def __init__(self, parent, title, person, unsorted_objects, autoshow=True, progress_indicator=None): super().__init__(None) act = QAction("close", self) act.triggered.connect(self._do_close) act.setShortcut(QKeySequence("escape")) self.addAction(act) layout = QGridLayout() bar = QMenuBar(self) self._object_actions = bar.addMenu(_("Object actions")) property_actions = bar.addMenu(_("Property actions")) self._create_item(property_actions, _("Copy property value"), "ctrl+c", self.on_copypropvalue_selected) self._create_item(property_actions, _("Copy property name"), "alt+c", self.on_copypropname_selected) self._create_item(property_actions, _("Copy property name and value"), "ctrl+alt+c", self.on_copypropline_selected) objects_label = QLabel(_("Objects"), self) layout.addWidget(objects_label, 0, 0) self._objects_list = QListWidget(self) self._objects_list.setAccessibleName(objects_label.text()) self._objects_list.setContextMenuPolicy(Qt.CustomContextMenu) self._objects_list.currentRowChanged.connect(self.on_objects_listbox) self._objects_list.customContextMenuRequested.connect( self._on_objects_list_menu) objects_label.setBuddy(self._objects_list) layout.addWidget(self._objects_list, 1, 0) props_label = QLabel(_("Object properties"), self) layout.addWidget(props_label, 0, 1) self._props = QTreeWidget(self) self._props.setAccessibleName(props_label.text()) props_label.setBuddy(self._props) layout.addWidget(self._props, 1, 1) goto_button = QPushButton(_("Go to"), self) goto_button.setDefault(True) goto_button.clicked.connect(self.on_goto_clicked) self._objects_list.itemActivated.connect(goto_button.click) layout.addWidget(goto_button, 2, 0) close_button = QPushButton(_("Close"), self) close_button.clicked.connect(self.close) layout.addWidget(close_button, 2, 1) self.setLayout(layout) self.setWindowTitle(title + _(" ({num_objects} objects shown)").format( num_objects=len(unsorted_objects))) self._person = person self._autoshow = autoshow self._progress_indicator = progress_indicator self._all_actions = [] for member in object_actions.__dict__.values(): if inspect.isclass(member) and issubclass(member, ObjectAction): self._all_actions.append(member) self._objects_list.setCurrentRow(0) self._sorter = ObjectsSorter(unsorted_objects, person) self._sorter.objects_sorted.connect(self._objects_sorted) self._sorter.start() def _objects_sorted(self, data): objects, item_data = data self._objects = objects for (desc, dist, rel_bearing) in item_data: self._objects_list.addItem( _("{object}: distance {distance} meters, {rel_bearing}"). format(object=desc, distance=dist, rel_bearing=rel_bearing)) if self._progress_indicator: self._progress_indicator.hide() self._progress_indicator.deleteLater() if self._autoshow: self._objects_list.setCurrentRow(0) self.show() def _create_item(self, menu, label, shortcut, callback): action = menu.addAction(label) action.triggered.connect(callback) action.setShortcut(QKeySequence(shortcut)) def on_goto_clicked(self, evt): self._person.move_to(self.selected_object[2], force=True) def _do_close(self): self.close() self.destroy() windows = QApplication.topLevelWidgets() other_browsers = [ w for w in windows if w is not self and isinstance(w, self.__class__) and w.isVisible() ] if other_browsers: other_browsers[-1].activateWindow() else: menu_service().ensure_key_capturer_focus() def on_objects_listbox(self, current_index): selected = self._objects[current_index][1] self._props.clear() common_item = QTreeWidgetItem([_("Common properties")]) specific_item = QTreeWidgetItem([_("Specific properties")]) other_item = QTreeWidgetItem([ _("Other properties - they can not be searched and are not processed in any way" ) ]) common_fields = list( EntityMetadata.for_discriminator("OSMEntity").fields.keys()) selected_metadata = EntityMetadata.for_discriminator( selected.discriminator) known_fields = selected_metadata.all_fields formatted_values = {} for field_name in selected.defined_field_names(): raw_value = selected.value_of_field(field_name) if field_name not in known_fields: # By the mere fact that the other fields have no defined order, we can add them there without losing anything. other_item.addChild( QTreeWidgetItem([ "%s: %s" % (underscored_to_words(field_name), raw_value) ])) else: value_str = "%s: %s" % ( underscored_to_words(field_name), format_field_value(raw_value, known_fields[field_name].type_name)) formatted_values[field_name] = value_str for common in common_fields: del known_fields[common] common_item.addChild(QTreeWidgetItem([formatted_values[common]])) for specific in known_fields.keys( ): # Because we deleted the common ones in the loop before this, only the specific remain. if specific in formatted_values: specific_item.addChild( QTreeWidgetItem([formatted_values[specific]])) # We add the entity ID mainly for debugging purposes, and that's the reason why it is added the last and so special in the first place. common_item.addChild( QTreeWidgetItem([_("Object id: {}").format(selected.id)])) self._props.addTopLevelItem(common_item) if specific_item.childCount() > 0: self._props.addTopLevelItem(specific_item) self._props.expandItem(specific_item) #self._props.setCurrentItem(specific_item) # Breaks focus behavior slightly, but annoingly enough. if other_item.childCount() > 0: self._props.addTopLevelItem(other_item) self._object_actions.clear() for action in self._all_actions: if action.executable(selected): mi = self._object_actions.addAction(action.label) mi.triggered.connect( action_execution_handler_factory(action, selected, self)) @property def selected_object(self): return self._objects[self._objects_list.currentRow()] def on_copypropvalue_selected(self, evt): prop = self._props.currentItem().text(0) val = prop.split(": ", 1)[1] QApplication.clipboard().setText(val) def on_copypropname_selected(self, evt): prop = self._props.currentItem().text(0) name = prop.split(": ", 1)[0] QApplication.clipboard().setText(name) def on_copypropline_selected(self, evt): prop = self._props.currentItem().text(0) QApplication.clipboard().setText(prop) def _on_objects_list_menu(self, point): self._object_actions.exec_(point)
class SpecifySearchConditionsDialog(BaseDialog): def __init__(self, parent, entity): super().__init__(parent, _("Search criteria"), _("&Start search"), _("&Cancel")) self._entity = entity self._value_widget = None self._value_label = None self._search_expression_parts = [] self._added_condition = False self._populate_fields_tree(self._entity) def create_ui(self): fields_label = QLabel(_("Class fields"), self) self.layout.addWidget(fields_label, 0, 0) self._fields_tree = QTreeWidget(self) fields_label.setBuddy(self._fields_tree) self._fields_tree.currentItemChanged.connect( self.on_fields_tree_sel_changed) self.layout.addWidget(self._fields_tree, 1, 0) operator_label = QLabel(_("Operator"), self) self.layout.addWidget(operator_label, 0, 1) self._operator = QListWidget(self) operator_label.setBuddy(self._operator) self.layout.addWidget(self._operator, 1, 1) self._operator.currentRowChanged.connect(self.on_operator_choice) self._operator.setCurrentRow(0) add_button = QPushButton(_("&Add condition"), self) add_button.clicked.connect(self.on_add_clicked) self.layout.addWidget(add_button, 2, 0, 1, 3) criteria_label = QLabel(_("Current search criteria"), self) self.layout.addWidget(criteria_label, 3, 0, 1, 3) self._criteria_list = QListWidget(self) criteria_label.setBuddy(self._criteria_list) self.layout.addWidget(self._criteria_list, 4, 0, 1, 3) remove_button = QPushButton(_("&Remove condition"), self) remove_button.clicked.connect(self.on_remove_clicked) self.layout.addWidget(remove_button, 5, 0, 1, 3) distance_label = QLabel(_("Search objects to distance"), self) self.layout.addWidget(distance_label, 6, 0) self._distance_field = QSpinBox(self) self._distance_field.setMaximum(100000) self._distance_field.setSuffix(" " + _("meters")) self._distance_field.setSpecialValueText(_("No limit")) distance_label.setBuddy(self._distance_field) self.layout.addWidget(self._distance_field, 6, 1) def _populate_fields_tree(self, entity, parent=None): if parent is None: parent = self._fields_tree.invisibleRootItem() metadata = EntityMetadata.for_discriminator(entity) for field_name, field in sorted( metadata.all_fields.items(), key=lambda i: underscored_to_words(i[0])): child_metadata = None try: child_metadata = EntityMetadata.for_discriminator( field.type_name) except KeyError: pass if child_metadata: name = get_class_display_name(field.type_name) subparent = QTreeWidgetItem([name]) subparent.setData(0, Qt.UserRole, field_name) parent.addChild(subparent) self._populate_fields_tree(field.type_name, subparent) else: item = QTreeWidgetItem([underscored_to_words(field_name)]) item.setData(0, Qt.UserRole, (field_name, field)) parent.addChild(item) def on_fields_tree_sel_changed(self, item): data = item.data(0, Qt.UserRole) if data is not None and not isinstance(data, str): self._field_name = data[0] self._field = data[1] self._operators = operators_for_column_class(self._field.type_name) self._operator.clear() self._operator.addItems([o.label for o in self._operators]) self._added_condition = False def on_operator_choice(self, index): self._added_condition = False if self._value_widget: self.layout.removeWidget(self._value_widget) self._value_widget.deleteLater() if self._value_label: self.layout.removeWidget(self._value_label) self._value_label.deleteLater() self._value_label = None operator = self._operators[self._operator.currentRow()] value_label = self._create_value_label( operator.get_value_label(self._field)) self._value_widget = operator.get_value_widget(self, self._field) if not self._value_widget: return QWidget.setTabOrder(self._operator, self._value_widget) self._value_label = value_label if self._value_label: self._value_label.setBuddy(self._value_widget) self.layout.addWidget(self._value_label, 0, 2) self.layout.addWidget(self._value_widget, 1, 2) def _create_value_label(self, label): if not label: return label = QLabel(label, self) return label def on_add_clicked(self, evt): if not hasattr(self, "_field_name"): return self._added_condition = True json_path = [] parent_item = self._fields_tree.currentItem().parent() parent_data = parent_item.data(0, Qt.UserRole) if parent_item else None if isinstance(parent_data, str): json_path.append(parent_data) json_path.append(self._field_name) json_path = ".".join(json_path) operator_obj = self._operators[self._operator.currentRow()] expression = operator_obj.get_comparison_expression( self._field, FieldNamed(json_path), self._value_widget) self._search_expression_parts.append(expression) self._criteria_list.addItem( f"{underscored_to_words(self._field_name)} {operator_obj.label} {operator_obj.get_value_as_string(self._field, self._value_widget)}" ) @property def distance(self): return self._distance_field.value() def create_conditions(self): conditions = [] if self._search_expression_parts: for part in self._search_expression_parts: conditions.append(part) return conditions def on_remove_clicked(self, evt): selection = self._criteria_list.currentRow() if selection < 0: return del self._search_expression_parts[selection] self._criteria_list.removeItemWidget(self._criteria_list.currentItem()) def ok_clicked(self): if not self._added_condition: if QMessageBox.question( self, _("Question"), _("It appears that you forgot to add the current condition to the conditions list. Do you want to add it before starting the search?" )): self.on_add_clicked(None) super().ok_clicked()
def delete_list(tree: QTreeWidget): selected = tree.currentItem() if selected: index = tree.indexOfTopLevelItem(selected) tree.takeTopLevelItem(index)