def setupUi(self): ''' set up the user interface ''' self.setMinimumWidth(500) self.setWindowTitle('Neues Projekt erstellen') project_manager = ProjectManager() self.project_names = [p.name for p in project_manager.projects] layout = QVBoxLayout(self) label = QLabel('Name des Projekts') self.name_edit = QLineEdit() self.name_edit.textChanged.connect(self.validate) layout.addWidget(label) layout.addWidget(self.name_edit) self.path = os.path.join(project_manager.settings.TEMPLATE_PATH, 'projektflaechen') hlayout = QHBoxLayout(self) label = QLabel('Import der (Teil-)Flächen des Plangebiets') self.layer_combo = QgsMapLayerComboBox() self.layer_combo.setFilters(QgsMapLayerProxyModel.VectorLayer) self.source = None self.layer_combo.layerChanged.connect(self.set_layer) self.layer_combo.layerChanged.connect(self.validate) browse_button = QPushButton('...') browse_button.clicked.connect(self.browse_path) browse_button.setMaximumWidth(30) hlayout.addWidget(self.layer_combo) hlayout.addWidget(browse_button) layout.addWidget(label) layout.addLayout(hlayout) self.status_label = QLabel() layout.addWidget(self.status_label) spacer = QSpacerItem( 20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) layout.addItem(spacer) buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.ok_button = buttons.button(QDialogButtonBox.Ok) self.ok_button.setEnabled(False) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) layout.addWidget(buttons) if len(self.layer_combo) > 0: self.set_layer(self.layer_combo.currentLayer()) self.layer_combo.setCurrentIndex(0)
class TheWidgetItem(QWidget, FORM_CLASS): """TheWidgetItem is a class which create item for listWidget.""" def __init__(self, parent=None): """Constructor.""" super(TheWidgetItem, self).__init__(parent) self.setupUi(self) self.labels = [ self.lblPlt1, self.lblPlt2, self.lblPlt3, self.lblPlt4, self.lblPlt5, self.lblPlt6 ] self.textQVBoxLayout = QVBoxLayout() self.allQHBoxLayout = QHBoxLayout() verticalSpacer = QSpacerItem(0, 70, QSizePolicy.Minimum, QSizePolicy.Expanding) self.textQVBoxLayout.addItem(verticalSpacer) self.allQHBoxLayout.addLayout(self.textQVBoxLayout) self.route_id = -1 self.setLayout(self.allQHBoxLayout) if sys.platform == "linux2": for label in self.labels: label.setFont(QtGui.QFont('SansSerif', 10)) if sys.platform == "win32": for label in self.labels: label.setFont(QtGui.QFont('Arial', 9)) def set_route_name(self, text, color): self.lblRouteName.setText(text) self.lblRouteName.setStyleSheet('color: rgb(' + str(color[0]) + ',' + str(color[1]) + ',' + str(color[2]) + ')') def set_route_id(self, route_id): self.route_id = route_id def set_distance_time(self, distance, time): self.lblDistanceTime.show() self.lblDistanceTime.setText("Distance: {}, time: {}".format( distance, time)) def hide_all_lbl_pollutants(self): for label in self.labels: label.setText("") label.hide() def set_pollutants(self, idx, plt, value): label = self.labels[idx] label.setText("{}: {}".format(plt, value)) label.show()
class ErrorWidgetItem(QWidget, FORM_CLASS): def __init__(self, parent=None): """Constructor.""" super(ErrorWidgetItem, self).__init__(parent) self.setupUi(self) self.textQVBoxLayout = QVBoxLayout() self.allQHBoxLayout = QHBoxLayout() verticalSpacer = QSpacerItem(0, 70, QSizePolicy.Minimum, QSizePolicy.Expanding) self.textQVBoxLayout.addItem(verticalSpacer) self.allQHBoxLayout.addLayout(self.textQVBoxLayout) self.route_id = -1 self.setLayout(self.allQHBoxLayout) def set_error_msg(self, text): self.lblErrorMsg.setText(text)
def __init__(self, iface, parent, params): QDialog.__init__(self, parent) self.iface = iface self.parent = parent self.params = params self.output_reader = None self.tool = None self.element_ids_nodes = None self.element_ids_links = None self.nodes_lay = None self.links_lay = None self.setWindowTitle(Parameters.plug_in_name) # Selection changed listeners self.params.junctions_vlay.selectionChanged.connect(self.feature_sel_changed) self.params.reservoirs_vlay.selectionChanged.connect(self.feature_sel_changed) self.params.tanks_vlay.selectionChanged.connect(self.feature_sel_changed) self.params.pipes_vlay.selectionChanged.connect(self.feature_sel_changed) self.params.pumps_vlay.selectionChanged.connect(self.feature_sel_changed) self.params.valves_vlay.selectionChanged.connect(self.feature_sel_changed) # self.setMinimumWidth(min_width) # self.setMinimumHeight(min_height) fra_main_lay = QVBoxLayout(self) self.fra_out_file = QFrame(self) fra_out_file_lay = QHBoxLayout(self.fra_out_file) self.lbl_out_file = QLabel('Simulation output file:') self.txt_out_file = QLineEdit('') self.txt_out_file.setReadOnly(True) self.btn_out_file = QToolButton() self.btn_out_file.setText('...') self.btn_out_file.clicked.connect(self.btn_out_file_clicked) fra_out_file_lay.addWidget(self.lbl_out_file) fra_out_file_lay.addWidget(self.txt_out_file) fra_out_file_lay.addWidget(self.btn_out_file) self.tab_widget = QTabWidget(self) # Graphs tab --------------------------------------------------------------------------------------------------- self.tab_graphs = QWidget() tab_graphs_lay = QHBoxLayout(self.tab_graphs) # Left frame self.fra_graphs_left = QFrame() self.fra_graphs_left.setMaximumWidth(100) fra_graphs_left_lay = QVBoxLayout(self.fra_graphs_left) self.btn_sel_element = QPushButton('Pick') self.btn_sel_element.clicked.connect(self.btn_sel_element_clicked) fra_graphs_left_lay.addWidget(self.btn_sel_element) # Nodes self.grb_nodes = QGroupBox(u'Nodes') lay_grb_nodes = QVBoxLayout(self.grb_nodes) self.chk_node_demand = QCheckBox('Demand') lay_grb_nodes.addWidget(self.chk_node_demand) self.chk_node_head = QCheckBox('Head') lay_grb_nodes.addWidget(self.chk_node_head) self.chk_node_pressure = QCheckBox('Pressure') lay_grb_nodes.addWidget(self.chk_node_pressure) self.chk_node_quality = QCheckBox('Quality') lay_grb_nodes.addWidget(self.chk_node_quality) fra_graphs_left_lay.addWidget(self.grb_nodes) # Links self.grb_links = QGroupBox(u'Links') lay_grb_links = QVBoxLayout(self.grb_links) self.chk_link_flow = QCheckBox('Flow') lay_grb_links.addWidget(self.chk_link_flow) self.chk_link_velocity = QCheckBox('Velocity') lay_grb_links.addWidget(self.chk_link_velocity) self.chk_link_headloss = QCheckBox('Headloss') lay_grb_links.addWidget(self.chk_link_headloss) self.chk_link_quality = QCheckBox('Quality') lay_grb_links.addWidget(self.chk_link_quality) fra_graphs_left_lay.addWidget(self.grb_links) self.btn_draw_graph = QPushButton('Draw') self.btn_draw_graph.clicked.connect(self.draw_graphs) fra_graphs_left_lay.addWidget(self.btn_draw_graph) self.spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) fra_graphs_left_lay.addItem(self.spacer) tab_graphs_lay.addWidget(self.fra_graphs_left) # Right frame self.fra_graphs_right = QFrame() fra_graphs_right_lay = QVBoxLayout(self.fra_graphs_right) fra_graphs_right_lay.setContentsMargins(0, 0, 0, 0) self.static_canvas = StaticMplCanvas(self.fra_graphs_right, width=5, height=4, dpi=100) fra_graphs_right_lay.addWidget(self.static_canvas) tab_graphs_lay.addWidget(self.fra_graphs_right) # lay.addWidget(self.button) self.tab_widget.addTab(self.tab_graphs, 'Graphs') # Maps tab ----------------------------------------------------------------------------------------------------- self.tab_maps = QWidget() tab_maps_lay = QHBoxLayout(self.tab_maps) # Left frame self.fra_maps_left = QFrame() self.fra_maps_left.setMaximumWidth(200) fra_maps_left_lay = QVBoxLayout(self.fra_maps_left) self.grb_maps = QGroupBox(u'Variable') grb_maps_lay = QVBoxLayout(self.grb_maps) self.rad_maps_node_demand = QRadioButton(u'Node demand') grb_maps_lay.addWidget(self.rad_maps_node_demand) self.rad_maps_node_head = QRadioButton(u'Node head') grb_maps_lay.addWidget(self.rad_maps_node_head) self.rad_maps_node_pressure = QRadioButton(u'Node pressure') grb_maps_lay.addWidget(self.rad_maps_node_pressure) self.rad_maps_node_quality = QRadioButton(u'Node quality') grb_maps_lay.addWidget(self.rad_maps_node_quality) self.rad_maps_link_flow = QRadioButton(u'Link flow') grb_maps_lay.addWidget(self.rad_maps_link_flow) self.rad_maps_link_velocity = QRadioButton(u'Link velocity') grb_maps_lay.addWidget(self.rad_maps_link_velocity) self.rad_maps_link_headloss = QRadioButton(u'Link headloss') grb_maps_lay.addWidget(self.rad_maps_link_headloss) self.rad_maps_link_quality = QRadioButton(u'Link quality') grb_maps_lay.addWidget(self.rad_maps_link_quality) fra_maps_left_lay.addWidget(self.grb_maps) fra_maps_left_lay.addItem(self.spacer) tab_maps_lay.addWidget(self.fra_maps_left) # Right maps frame self.fra_maps_right = QFrame() fra_maps_right_lay = QVBoxLayout(self.fra_maps_right) self.fra_maps_right_time = QFrame() fra_maps_right_time_lay = QFormLayout(self.fra_maps_right_time) self.lbl_map_times = QLabel(u'Period [h]:') self.cbo_map_times = QComboBox() fra_maps_right_time_lay.addRow(self.lbl_map_times, self.cbo_map_times) fra_maps_right_lay.addWidget(self.fra_maps_right_time) self.btn_draw_map = QPushButton(u'Draw map') self.btn_draw_map.clicked.connect(self.draw_maps) fra_maps_right_lay.addWidget(self.btn_draw_map) fra_maps_right_lay.addItem(self.spacer) tab_maps_lay.addWidget(self.fra_maps_right) self.tab_widget.addTab(self.tab_maps, 'Maps') # # Add to main fra_main_lay.addWidget(self.fra_out_file) fra_main_lay.addWidget(self.tab_widget) self.setup() self.initialize() # self.read_outputs() # Set size self.setMinimumWidth(self.tab_graphs.width()) self.setMinimumHeight(self.tab_graphs.height())
def on_resolve_href(dialog, layer, feature, field): """ @param dialog the dialog where the feature form is opened @param layer the layer on which the href link stands @param feature the current feature @param field the field name storing the href URL @param linked_layer_id the QGIS layer id of the already resolved layer, for update """ from .import_gmlas_panel import ImportGmlasPanel path = feature[field] if not path: return # if parent is a Dialog, we are in a feature form # else in a attribute table is_feature_form = isinstance(dialog.parent, QDialog) # The href is resolved thanks to the OGR GMLAS driver. # We need to determine what is the "root" layer of the imported # href, so that we can connect the xlink:href link to the # newly loaded set of layers. # There seems to be no way to determine what is the "root" layer # of a GMLAS database. # So, we rely on XML parsing to determine the root element # and on layer xpath found in metadata # Download the file so that it is used for XML parsing # and for GMLAS loading from ..core.qgis_urlopener import remote_open_from_qgis from ..core.gml_utils import extract_features from ..core.xml_utils import xml_root_tag, no_ns, no_prefix import tempfile with remote_open_from_qgis(path) as fi: with tempfile.NamedTemporaryFile(delete=False) as fo: fo.write(fi.read()) tmp_file = fo.name with open(tmp_file, 'r') as file_io: root_tag = xml_root_tag(file_io) # reuse the GMLAS import panel widget dlg = QDialog() import_widget = ImportGmlasPanel(dlg, gml_path=tmp_file) path_edit = QLineEdit(path, dlg) path_edit.setEnabled(False) btn = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, dlg) layout = QVBoxLayout() layout.addWidget(path_edit) layout.addWidget(import_widget) layout.addItem( QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)) layout.addWidget(btn) dlg.setLayout(layout) btn.accepted.connect(dlg.accept) btn.rejected.connect(dlg.reject) dlg.resize(400, 300) dlg.setWindowTitle("Options for xlink:href loading") if not dlg.exec_(): return # close the current form w = dialog while not isinstance(w, QDialog): w = w.parent() w.close() import_widget.do_load() # Add a link between the current layer # and the root layer of the newly loaded (complex) features # 1. determine the root layer and pkid of all its features root_layer = None for l in QgsProject.instance().mapLayers().values(): if no_ns(l.customProperty("xpath", "")) == no_prefix(root_tag): root_layer = l break if root_layer is None: raise RuntimeError("Cannot determine the root layer") pkid = layer.customProperty("pkid") pkid_value = feature[pkid] root_layer.startEditing() # 2. add a href_origin_pkid field in the root layer if "parent_href_pkid" not in [f.name() for f in root_layer.fields()]: new_field = QgsField(layer.fields().field(pkid)) new_field.setName("parent_href_pkid") root_layer.addAttribute(new_field) # 3. set its value to the id of current feature ids_to_change = [] for f in root_layer.getFeatures(): if f["parent_href_pkid"] is None: ids_to_change.append(f.id()) idx = root_layer.fields().indexFromName("parent_href_pkid") for fid in ids_to_change: # sets the pkid_value root_layer.changeAttributeValue(fid, idx, pkid_value) root_layer.commitChanges() # 4. declare a new QgsRelation rel_name = "1_n_" + layer.name() + "_" + field rel = QgsProject.instance().relationManager().relations().get(rel_name) if rel is None: rel = QgsRelation() rel.setId(rel_name) rel.setName(field) rel.setReferencedLayer(layer.id()) rel.setReferencingLayer(root_layer.id()) rel.addFieldPair("parent_href_pkid", pkid) QgsProject.instance().relationManager().addRelation(rel) # 5. declare the new relation in the form widgets # new 1:N in the current layer fc = layer.editFormConfig() rel_tab = fc.tabs()[1] rel_tab.addChildElement( QgsAttributeEditorRelation(rel.name(), rel, rel_tab)) # new field in the root layer fc = root_layer.editFormConfig() main_tab = fc.tabs()[0] main_tab.addChildElement( QgsAttributeEditorField("parent_href_pkid", idx, main_tab)) # declare as reference relation widget s = QgsEditorWidgetSetup( "RelationReference", { 'AllowNULL': False, 'ReadOnly': True, 'Relation': rel.id(), 'OrderByValue': False, 'MapIdentification': False, 'AllowAddFeatures': False, 'ShowForm': True }) root_layer.setEditorWidgetSetup(idx, s) # write metadata in layers href_resolved = layer.customProperty("href_resolved", []) if path not in href_resolved: layer.setCustomProperty("href_resolved", href_resolved + [path]) href_linked_layers = layer.customProperty("href_linked_layers", {}) href_linked_layers[field] = root_layer.id() layer.setCustomProperty("href_linked_layers", href_linked_layers) # 6. reload the current form from ..main import get_iface if is_feature_form: get_iface().openFeatureForm(layer, feature) else: get_iface().showAttributeTable(layer)
def show(self, *args, title: str = 'Parameter einstellen', scrollable: bool = False): ''' render parameters and elements in parent Parameters ---------- args : optional arguments for appending parameter layout to parent (like x, y if parent is grid layout) title : str, optional title of the parameter dialog, defaults to 'Parameter einstellen' scrollable : bool, optional a scrollbar will be added to both preview and dialog if True, recommended if there are a lot of parameters, defaults to not scrollable ''' if self.parent is None: raise Exception("can't render Params object with no parent set") # Debug: function to automatically write a help file with all params # with empty texts, should be removed in production if (settings.DEBUG and getattr(self, 'help_file', None) and not os.path.exists(self.help_file)): if not os.path.exists(self.HELP_PATH): os.mkdir(self.HELP_PATH) with open(self.help_file, 'w') as json_file: json.dump(self.help_dict, json_file, indent=4) self.dialog = ParamsDialog(parent=None, help_text=self.help_dict['beschreibung'], title=title) self.parent.addLayout(self.layout, *args) if scrollable: frame = QFrame() scroll_area = QScrollArea() layout = QVBoxLayout() layout.setSpacing(5) frame.setLayout(layout) scroll_area.setWidget(frame) scroll_area.setWidgetResizable(True) scroll_area.setFixedHeight(400) self.layout.addWidget(scroll_area) else: layout = self.layout for element in self._elements: if isinstance(element, QLayoutItem): layout.addItem(element) # overview elif not getattr(element, 'hide_in_overview', None): element.draw(layout) self.dialog.draw(element) if not self.editable: return row = QHBoxLayout() button = QPushButton(self.button_label) icon = QIcon(os.path.join(settings.IMAGE_PATH, 'iconset_mob', '20190619_iconset_mob_edit_1.png')) button.setIcon(icon) tool_tip = self.help_dict.get('tooltip', None) button.setToolTip(tool_tip) row.addItem( QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)) row.addWidget(button) self.layout.addItem( QSpacerItem(10, 10, QSizePolicy.Fixed, QSizePolicy.Minimum)) self.layout.addLayout(row) button.clicked.connect(self.show_dialog)
class Params(QObject): ''' collection of parameters, single parameters can be added by setting them as attributes. supports dict-like access of the parameters by name Attributes ---------- changed : pyqtSignal fired on change of any parameter in collection ''' changed = pyqtSignal() HELP_PATH = os.path.join(settings.HELP_PATH, 'params') def __init__(self, parent: QObject = None, button_label: str = 'Editieren', editable: bool = True, help_text: str = '', help_file: str = None): ''' Parameters ---------- parent : QObject, optional ui element to draw the parameters in, can't be drawn if parent is None, defaults to no parent button_label : parent, optional label of the edit button in the parameter preview, defaults to 'Editieren' editable : bool, optional parameters are editable. if not, no edit button will be shown in the preview, defaults to editable parameters help_file : str, optional json-style text file containing help texts for each parameter, will be automatically created if not existing, defaults to no help file help_text : str, optional the general help displayed in the parameter dialog, overrides the description in the help file if given, defaults no help text ''' super().__init__() self._params = OrderedDict() self._elements = [] self.button_label = button_label #self._dependencies = [] self.parent = parent self.dialog = None self.editable = editable self.layout = QVBoxLayout() self.layout.setSpacing(5) self.help_dict = { 'tooltip': 'Parameter editieren' } if help_file: self.help_file = help_file if os.path.exists(help_file) else \ os.path.join(self.HELP_PATH, help_file) if os.path.exists(self.help_file): with open(self.help_file) as json_file: self.help_dict = yaml.safe_load(json_file) # passed help text overrides the one from file if help_text or 'beschreibung' not in self.help_dict: self.help_dict['beschreibung'] = help_text def add(self, element: Union[Param, Seperator, Title, QLayoutItem], name: str = ''): ''' add an element (parameter or style element) elements will be rendered in order of addition Parameters ---------- element : object parameter or style element to add name : str, optional name of parameter to add, parameter can be adressed by that name. ignored when element is not a parameter, defaults to no name ''' self._elements.append(element) if name and isinstance(element, Param): self._params[name] = element if element.input: if element.help_text or name not in self.help_dict: self.help_dict[name] = element.help_text else: element.help_text = self.help_dict[name] @property def params(self) -> List[Param]: ''' Returns ------- list a list of all parameters ''' return self._params.values() def show(self, *args, title: str = 'Parameter einstellen', scrollable: bool = False): ''' render parameters and elements in parent Parameters ---------- args : optional arguments for appending parameter layout to parent (like x, y if parent is grid layout) title : str, optional title of the parameter dialog, defaults to 'Parameter einstellen' scrollable : bool, optional a scrollbar will be added to both preview and dialog if True, recommended if there are a lot of parameters, defaults to not scrollable ''' if self.parent is None: raise Exception("can't render Params object with no parent set") # Debug: function to automatically write a help file with all params # with empty texts, should be removed in production if (settings.DEBUG and getattr(self, 'help_file', None) and not os.path.exists(self.help_file)): if not os.path.exists(self.HELP_PATH): os.mkdir(self.HELP_PATH) with open(self.help_file, 'w') as json_file: json.dump(self.help_dict, json_file, indent=4) self.dialog = ParamsDialog(parent=None, help_text=self.help_dict['beschreibung'], title=title) self.parent.addLayout(self.layout, *args) if scrollable: frame = QFrame() scroll_area = QScrollArea() layout = QVBoxLayout() layout.setSpacing(5) frame.setLayout(layout) scroll_area.setWidget(frame) scroll_area.setWidgetResizable(True) scroll_area.setFixedHeight(400) self.layout.addWidget(scroll_area) else: layout = self.layout for element in self._elements: if isinstance(element, QLayoutItem): layout.addItem(element) # overview elif not getattr(element, 'hide_in_overview', None): element.draw(layout) self.dialog.draw(element) if not self.editable: return row = QHBoxLayout() button = QPushButton(self.button_label) icon = QIcon(os.path.join(settings.IMAGE_PATH, 'iconset_mob', '20190619_iconset_mob_edit_1.png')) button.setIcon(icon) tool_tip = self.help_dict.get('tooltip', None) button.setToolTip(tool_tip) row.addItem( QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)) row.addWidget(button) self.layout.addItem( QSpacerItem(10, 10, QSizePolicy.Fixed, QSizePolicy.Minimum)) self.layout.addLayout(row) button.clicked.connect(self.show_dialog) def get(self, name: str) -> Param: ''' get a parameter by name Returns ------- Param the parameter matching the name or None if not found ''' return self._params.get(name, None) def close(self): ''' close rendered parameters ''' if self.dialog: clear_layout(self.dialog.layout) del(self.dialog) clear_layout(self.layout) def show_dialog(self): ''' show the dialog to edit parameters ''' confirmed = self.dialog.exec_() if confirmed: has_changed = False for param in self.params: if not param.input or param.value == param.input.value: continue param.value = param.input.value has_changed = True if has_changed: self.changed.emit() else: # reset inputs for param in self.params: if param.input: param.input.value = param.value def __getattr__(self, name): param = self._params.get(name, None) if param: return param return self.__dict__.get(name, None) def __setattr__(self, name, value): if isinstance(value, Param): self.add(value, name) else: self.__dict__[name] = value def __getitem__(self, key): return self._params.get(key, None) def __setitem__(self, key, value): self.add(value, key)
class DisplayAequilibraEFormatsDialog(QtWidgets.QDialog, FORM_CLASS): def __init__(self, iface): QtWidgets.QDialog.__init__(self) self.iface = iface self.setupUi(self) self.error = None self.error = None self.data_path, self.data_type = GetOutputFileName( self, 'AequilibraE custom formats', ["Aequilibrae dataset(*.aed)", "Aequilibrae matrix(*.aem)"], '.aed', standard_path()) if self.data_type is None: self.error = 'Path provided is not a valid dataset' self.exit_with_error() self.data_type = self.data_type.upper() if self.data_type == 'AED': self.data_to_show = AequilibraEData() elif self.data_type == 'AEM': self.data_to_show = AequilibraeMatrix() try: self.data_to_show.load(self.data_path) except: self.error = 'Could not load dataset' self.exit_with_error() # Elements that will be used during the displaying self._layout = QVBoxLayout() self.table = QTableView() self._layout.addWidget(self.table) # Settings for displaying self.show_layout = QHBoxLayout() # Thousand separator self.thousand_separator = QCheckBox() self.thousand_separator.setChecked(True) self.thousand_separator.setText('Thousands separator') self.thousand_separator.toggled.connect(self.format_showing) self.show_layout.addWidget(self.thousand_separator) self.spacer = QSpacerItem(5, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.show_layout.addItem(self.spacer) # Decimals txt = QLabel() txt.setText('Decimal places') self.show_layout.addWidget(txt) self.decimals = QSpinBox() self.decimals.valueChanged.connect(self.format_showing) self.decimals.setMinimum(0) self.decimals.setValue(4) self.decimals.setMaximum(10) self.show_layout.addWidget(self.decimals) self._layout.addItem(self.show_layout) # differentiates between matrix and dataset if self.data_type == 'AEM': self.data_to_show.computational_view([self.data_to_show.names[0]]) # Matrices need cores and indices to be set as well self.mat_layout = QHBoxLayout() self.mat_list = QComboBox() for n in self.data_to_show.names: self.mat_list.addItem(n) self.mat_list.currentIndexChanged.connect(self.change_matrix_cores) self.mat_layout.addWidget(self.mat_list) self.idx_list = QComboBox() for i in self.data_to_show.index_names: self.idx_list.addItem(i) self.idx_list.currentIndexChanged.connect(self.change_matrix_cores) self.mat_layout.addWidget(self.idx_list) self._layout.addItem(self.mat_layout) self.change_matrix_cores() self.but_export = QPushButton() self.but_export.setText('Export') self.but_export.clicked.connect(self.export) self.but_close = QPushButton() self.but_close.clicked.connect(self.exit_procedure) self.but_close.setText('Close') self.but_layout = QHBoxLayout() self.but_layout.addWidget(self.but_export) self.but_layout.addWidget(self.but_close) self._layout.addItem(self.but_layout) # We chose to use QTableView. However, if we want to allow the user to edit the dataset # The we need to allow them to switch to the slower QTableWidget # Code below # self.table = QTableWidget(self.data_to_show.entries, self.data_to_show.num_fields) # self.table.setHorizontalHeaderLabels(self.data_to_show.fields) # self.table.setObjectName('data_viewer') # # self.table.setVerticalHeaderLabels([str(x) for x in self.data_to_show.index[:]]) # self.table.clearContents() # # for i in range(self.data_to_show.entries): # for j, f in enumerate(self.data_to_show.fields): # item1 = QTableWidgetItem(str(self.data_to_show.data[f][i])) # item1.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) # self.table.setItem(i, j, item1) self.resize(700, 500) self.setLayout(self._layout) self.format_showing() def format_showing(self): decimals = self.decimals.value() separator = self.thousand_separator.isChecked() if isinstance(self.data_to_show, AequilibraeMatrix): m = NumpyModel(self.data_to_show, separator, decimals) else: m = DatabaseModel(self.data_to_show, separator, decimals) self.table.clearSpans() self.table.setModel(m) def change_matrix_cores(self): self.data_to_show.computational_view([self.mat_list.currentText()]) self.data_to_show.set_index(self.data_to_show.index_names[0]) self.format_showing() def export(self): new_name, file_type = GetOutputFileName( self, self.data_type, ["Comma-separated file(*.csv)"], ".csv", self.data_path) if new_name is not None: self.data_to_show.export(new_name) def exit_with_error(self): qgis.utils.iface.messageBar().pushMessage("Error:", self.error, level=1) self.exit_procedure() def exit_procedure(self): self.close()
class ProgressDialog(QDialog): """ Progress dialog shows progress bar for algorithm. """ def __init__(self, iface): QDialog.__init__(self, iface.mainWindow()) self.workerThread = None self.state = False self.resultStatus = None self.doReRun = False self.wasCanceled = False self.wasSuccessful = False self.savedProj = None self.result = None self.messageTxt = { 'msg_optimierung': self.tr('Berechnung der optimalen Stuetzenpositionen...'), 'msg_seillinie': self.tr('Berechnung der optimale Seillinie...') } # Build GUI Elements self.setWindowTitle(self.tr("SEILAPLAN wird ausgefuehrt")) self.resize(500, 100) self.container = QVBoxLayout() self.progressBar = QProgressBar(self) self.progressBar.setMinimumWidth(500) self.statusLabel = QLabel(self) self.hbox = QHBoxLayout() self.cancelButton = QDialogButtonBox() self.closeButton = QDialogButtonBox() self.resultLabel = QLabel(self) self.resultLabel.setMaximumWidth(500) self.resultLabel.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)) self.resultLabel.setWordWrap(True) spacer1 = QSpacerItem(20, 20, QSizePolicy.Fixed, QSizePolicy.Fixed) self.rerunButton = QPushButton(self.tr("zurueck zum Startfenster")) self.rerunButton.setVisible(False) spacer2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.cancelButton.setStandardButtons(QDialogButtonBox.Cancel) self.cancelButton.button(QDialogButtonBox.Cancel).setText(self.tr("Abbrechen")) self.cancelButton.clicked.connect(self.onAbort) self.closeButton.setStandardButtons(QDialogButtonBox.Close) self.closeButton.button(QDialogButtonBox.Close).setText(self.tr("Schliessen")) self.closeButton.clicked.connect(self.onClose) self.hbox.addWidget(self.rerunButton) self.hbox.addItem(spacer2) self.hbox.addWidget(self.cancelButton) self.hbox.setAlignment(self.cancelButton, Qt.AlignHCenter) self.hbox.addWidget(self.closeButton) self.hbox.setAlignment(self.closeButton, Qt.AlignHCenter) self.closeButton.hide() self.container.addWidget(self.progressBar) self.container.addWidget(self.statusLabel) self.container.addWidget(self.resultLabel) self.container.addItem(spacer1) self.container.addLayout(self.hbox) self.container.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(self.container) # noinspection PyMethodMayBeStatic def tr(self, message, **kwargs): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString Parameters ---------- **kwargs """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate(type(self).__name__, message) def setThread(self, workerThread): self.workerThread = workerThread self.connectThreadSignals() def connectThreadSignals(self): # Connect signals of thread self.workerThread.sig_jobEnded.connect(self.jobEnded) self.workerThread.sig_jobError.connect(self.onError) self.workerThread.sig_value.connect(self.valueFromThread) self.workerThread.sig_range.connect(self.rangeFromThread) self.workerThread.sig_text.connect(self.textFromThread) self.workerThread.sig_result.connect(self.resultFromThread) self.rerunButton.clicked.connect(self.onRerun) def run(self): # Show modal dialog window (QGIS is still responsive) self.show() # start event loop self.exec() def jobEnded(self, success): self.setWindowTitle("SEILAPLAN") if success: self.progressBar.setValue(self.progressBar.maximum()) self.wasSuccessful = True # Close progress dialog so that adjustment window can be opened self.close() else: # If there was an abort by the user self.statusLabel.setText(self.tr("Berechnungen abgebrochen.")) self.progressBar.setValue(self.progressBar.minimum()) self.finallyDo() def valueFromThread(self, value): self.progressBar.setValue(int(value)) def rangeFromThread(self, range_vals): self.progressBar.setRange(int(round(range_vals[0])), int(round(range_vals[1]))) def maxFromThread(self, max): self.progressBar.setValue(self.progressBar.maximum()) def textFromThread(self, message): self.statusLabel.setText(self.messageTxt[message]) def resultFromThread(self, resultStatus): self.resultStatus = resultStatus # resultStatus: # 1 = Optimization successful # 2 = Cable takes off from support # 3 = Optimization partially successful def onAbort(self): self.setWindowTitle('SEILAPLAN') self.statusLabel.setText(self.tr( 'Laufender Prozess wird abgebrochen...')) self.workerThread.cancel() # Terminates process cleanly self.wasCanceled = True def onError(self, exception_string): self.setWindowTitle(self.tr('SEILAPLAN: Berechnung fehlgeschlagen')) self.statusLabel.setText(self.tr('Ein Fehler ist aufgetreten:')) self.resultLabel.setText(self.tr(exception_string)) self.resultLabel.setHidden(False) self.progressBar.setValue(self.progressBar.minimum()) self.setLayout(self.container) self.finallyDo() def onRerun(self): self.doReRun = True self.onClose() def finallyDo(self): self.rerunButton.setVisible(True) self.cancelButton.hide() self.closeButton.show() def onClose(self): self.close()
class ProfileDialog(QDialog): def __init__(self, parent, interface, drawTool, projectHandler): """ :type drawTool: gui.mapMarker.MapMarkerTool :type projectHandler: projectHandler.ProjectConfHandler """ QDialog.__init__(self, parent) self.iface = interface self.projectHandler = projectHandler self.drawTool = drawTool self.setWindowTitle(self.tr('Gelaendelinie')) self.setWindowModality(Qt.WindowModal) self.profile = None # Array with properties fixed poles self.poleData = [] # Array with sections without poles self.noPoleSection = [] # Profile data self.xdata = None self.zdata = None self.profileMin = 0 self.profileMax = None # Plot self.sc = ProfilePlot(self) # Pan/Zoom Tools for diagram tbar = MyNavigationToolbar(self.sc, self) tbar.pan() self.sc.setToolbar(tbar) # Layout main_widget = QWidget(self) self.container = QVBoxLayout(main_widget) self.outerLayout = QVBoxLayout() # GUI fields stueTitle = QLabel('<b>' + self.tr('Stuetzenoptimierung einschraenken') + '</b>') hbox = QHBoxLayout() line1 = QFrame() line1.setFrameShape(QFrame.HLine) line1.setFrameShadow(QFrame.Sunken) # Create labels and buttons self.fixStueAdd = QPushButton(self.tr('Fixe Stuetze definieren')) self.noStueAdd = QPushButton(self.tr('Abschnitt ohne Stuetzen definieren')) self.noStueDel = QPushButton() icon = QIcon() icon.addPixmap( QPixmap(':/plugins/SeilaplanPlugin/gui/icons/icon_bin.png'), QIcon.Normal, QIcon.Off) self.noStueDel.setIcon(icon) self.noStueDel.setIconSize(QSize(16, 16)) self.fixStueAdd.setToolTip(self.tr('Tooltip Fixe Stuetzen')) self.noStueAdd.setToolTip(self.tr('Tooltip Abschnitte ohne Stuetzen')) self.noStueDel.setToolTip(self.tr('Tooltip Abschnitte loeschen')) spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) hbox.addWidget(self.fixStueAdd) hbox.addItem(spacerItem1) hbox.addWidget(self.noStueAdd) hbox.addWidget(self.noStueDel) hbox.setAlignment(self.noStueAdd, Qt.AlignRight) btnBoxSpacer = QSpacerItem(40, 40, QSizePolicy.Fixed, QSizePolicy.Fixed) self.buttonBox = QDialogButtonBox(main_widget) self.buttonBox.setStandardButtons(QDialogButtonBox.Ok) # Build up Gui self.container.addWidget(self.sc) self.container.addWidget(tbar, alignment=Qt.AlignHCenter | Qt.AlignTop) self.container.addWidget(line1) self.container.addWidget(stueTitle) self.container.addLayout(hbox) self.container.addLayout(self.outerLayout) self.container.addItem(btnBoxSpacer) self.container.addWidget(self.buttonBox) # Connect signals self.fixStueAdd.clicked.connect(self.sc.acitvateCrosshairPole) self.noStueAdd.clicked.connect(self.sc.activateCrosshairSection) self.noStueDel.clicked.connect(self.deleteSections) self.buttonBox.accepted.connect(self.Apply) self.setLayout(self.container) # Gui's functionality for fixed pole gui fields self.buildPoleHeader() self.poleLayout = CustomPoleWidget(self, self.outerLayout, self.poleData) self.poleLayout.sig_updatePole.connect(self.updatePole) self.poleLayout.sig_deletePole.connect(self.deletePole) # noinspection PyMethodMayBeStatic def tr(self, message, **kwargs): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString Parameters ---------- **kwargs """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate(type(self).__name__, message) def setProfile(self, profile): self.profile = profile self.xdata = self.profile.di self.zdata = self.profile.zi self.profileMax = floor(self.xdata[-1]) # Draw profile in diagram self.sc.plotData(self.profile) def reset(self): """Resets the window and remove all pole layouts. Markers do not have to be deleted, they are reset when new profile line is drawn. Plot points are reset when plot is cleared at next setProfile().""" # Delete pole rows in gui self.poleLayout.removeAll() self.poleData = [] self.noPoleSection = [] self.profileMax = None self.sc.reset() def setPoleData(self, poles, sections): """Fills gui, plot and map with data of fixed poles and pole sections.""" self.drawTool.removeIntermediateMarkers() # Make sure the pole layout has the correct reference to the pole data self.poleLayout.poleArr = self.poleData # Set the max ranges self.poleLayout.setInitialGui([self.profileMin, self.profileMax]) for pole in poles: self.addPole(pole['d'], pole['z'], pole['h'], name=pole['name']) for section in sections: # Draw line onto map self.activateMapLine(section[0]) self.finishLine(section[1]) # Plot section in plot for x in section: z = self.getZValue(x) self.sc.drawSection(x, z) self.sc.draw() def buildPoleHeader(self): headerRow = QHBoxLayout() spacerItemA = QSpacerItem(60, 20, QSizePolicy.Fixed, QSizePolicy.Minimum) spacerItemE = QSpacerItem(60, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) headername = QLabel(self.tr('Stuetzenbezeichnung')) headername.setMinimumSize(QSize(180, 30)) headerDist = QLabel(self.tr('Hori.distanz')) headerDist.setMinimumSize(QSize(95, 30)) headerHeight = QLabel(self.tr('Hoehe')) headerHeight.setMinimumSize(QSize(85, 30)) headerRow.addItem(spacerItemA) headerRow.addWidget(headername) headerRow.addWidget(headerDist) headerRow.addWidget(headerHeight) headerRow.addItem(spacerItemE) self.outerLayout.addLayout(headerRow) def addPole(self, d, z, h=None, name='', angle=False): """Called when user clicks onto plot window to create fixed pole. Function creates a new row in gui with properties of pole, creates a point in the plot and a marker on the map.""" idx = 0 for i, pole in enumerate(reversed(self.poleData)): if pole['d'] <= d: idx = len(self.poleData) - i break if not z: z = self.getZValue(d) if not name: name = self.tr('fixe Stuetze') # Draw point on plot drawnPoint = self.sc.createPoint(d, z) # Draw marker onto map self.createMapMarker(d, idx+1) # Save new fixed pole in list self.poleData.insert(idx, { 'name': name, 'nr': idx+1, 'poleType': 'fixed', 'd': d, 'z': z, 'h': h, 'angle': angle, 'plotPoint': drawnPoint }) self.poleLayout.addRow(idx, addBtn=False) def updatePole(self, idx, property_name, val): """Called when user manually changes distance or height values in LineEdits. Function updates point in plot and marker on map.""" if self.poleData[idx][property_name] == val: return if property_name == 'd': # Calculate z value self.poleData[idx]['z'] = self.getZValue(val) # Update in plot self.sc.deletePoint(self.poleData[idx]['plotPoint']) self.poleData[idx]['plotPoint'] = \ self.sc.createPoint(val, self.poleData[idx]['z']) # Update on map marker = self.projectHandler.transform2MapCoords(float(val)) self.drawTool.updateMarker(marker, idx+1) if property_name in ['d', 'h']: self.poleData[idx][property_name] = round(val, 0) else: self.poleData[idx][property_name] = val self.poleLayout.changeRow(idx, property_name, val) def deletePole(self, idx): """Called when user clicks on delete button on pole row. Function removes point in plot, marker on map and row in gui.""" self.sc.deletePoint(self.poleData[idx]['plotPoint']) self.drawTool.removeMarker(idx+1) self.poleLayout.deleteRow(idx) self.poleData.pop(idx) def getZValue(self, dist): return self.zdata[np.argmax(self.xdata >= dist)] def createMapMarker(self, horiDist, idx): point = self.projectHandler.transform2MapCoords(float(horiDist)) self.drawTool.drawMarker(point, idx) def deactivateMapCursor(self): self.drawTool.deactivateCursor() def activateMapCursor(self, initPoint, color): self.updateMapCursor(initPoint, color) def updateMapCursor(self, horiDist, color): point = self.projectHandler.transform2MapCoords(horiDist) self.drawTool.updateCursor(point, color) def activateMapLine(self, horiDist): self.noPoleSection.append([horiDist, None]) initPoint = self.projectHandler.transform2MapCoords(horiDist) self.drawTool.activateSectionLine(initPoint) def lineMoved(self, horiDist): point = self.projectHandler.transform2MapCoords(horiDist) self.drawTool.updateSectionLine(point) def finishLine(self, horiDist): self.noPoleSection[-1][1] = horiDist endPoint = self.projectHandler.transform2MapCoords(horiDist) self.drawTool.updateSectionLine(endPoint) self.drawTool.deactivateCursor() def deleteSections(self): if self.noPoleSection: # Redraw profile self.sc.plotData(self.profile) # Redraw markers of pole for pole in self.poleData: pole['plotPoint'] = self.sc.createPoint(pole['d'], pole['z']) # Delete all sections in map self.drawTool.deleteSectionLines() self.noPoleSection = [] def stopActiveEdits(self): self.deactivateMapCursor() self.drawTool.clearUnfinishedLines() # Remove last section if its not finished if self.noPoleSection and not self.noPoleSection[-1][1]: self.noPoleSection.pop(-1) def Apply(self): self.close() def closeEvent(self, event): self.stopActiveEdits() self.projectHandler.setFixedPoles(self.poleData) self.projectHandler.setNoPoleSection(self.noPoleSection) # Reset gui since this can only be done when the window is still open self.reset()
class GraphDockWidget(QDockWidget): """Main Dock Widget for showing 3Di results in Graphs""" closingWidget = pyqtSignal(int) def __init__( self, iface, parent_widget=None, parent_class=None, nr=0, ts_datasources=None, root_tool=None, ): """Constructor""" super().__init__(parent_widget) self.iface = iface self.parent_class = parent_class self.nr = nr self.ts_datasources = ts_datasources self.root_tool = root_tool self.setup_ui(self) parameter_config = self._get_active_parameter_config() # add graph widgets self.q_graph_widget = GraphWidget( self, self.ts_datasources, parameter_config["q"], "Q graph", QgsWkbTypes.LineString, ) self.h_graph_widget = GraphWidget( self, self.ts_datasources, parameter_config["h"], "H graph", QgsWkbTypes.Point, ) self.graphTabWidget.addTab(self.q_graph_widget, self.q_graph_widget.name) self.graphTabWidget.addTab(self.h_graph_widget, self.h_graph_widget.name) # add listeners self.addSelectedObjectButton.clicked.connect(self.add_objects) # init current layer state and add listener self.selected_layer_changed(self.iface.mapCanvas().currentLayer) self.iface.currentLayerChanged.connect(self.selected_layer_changed) self.root_tool.timeslider_widget.datasource_changed.connect( self.on_active_ts_datasource_change) def on_close(self): """ unloading widget and remove all required stuff :return: """ self.addSelectedObjectButton.clicked.disconnect(self.add_objects) self.iface.currentLayerChanged.disconnect(self.selected_layer_changed) self.root_tool.timeslider_widget.datasource_changed.disconnect( self.on_active_ts_datasource_change) # self.q_graph_widget.close() # self.h_graph_widget.close() def closeEvent(self, event): """ overwrite of QDockWidget class to emit signal :param event: QEvent """ self.on_close() self.closingWidget.emit(self.nr) event.accept() def _get_active_parameter_config(self): active_ts_datasource = self.root_tool.timeslider_widget.active_ts_datasource if active_ts_datasource is not None: # TODO: just taking the first datasource, not sure if correct: threedi_result = active_ts_datasource.threedi_result() available_subgrid_vars = threedi_result.available_subgrid_map_vars available_agg_vars = threedi_result.available_aggregation_vars if not available_agg_vars: messagebar_message("Warning", "No aggregation netCDF was found.", level=1, duration=5) parameter_config = generate_parameter_config( available_subgrid_vars, available_agg_vars) else: parameter_config = {"q": {}, "h": {}} return parameter_config def on_active_ts_datasource_change(self): parameter_config = self._get_active_parameter_config() self.q_graph_widget.set_parameter_list(parameter_config["q"]) self.h_graph_widget.set_parameter_list(parameter_config["h"]) def selected_layer_changed(self, active_layer): tdi_layer = False # get active layer from canvas, otherwise .dataProvider doesn't work canvas = self.iface.mapCanvas() current_layer = canvas.currentLayer() if current_layer: provider = current_layer.dataProvider() valid_object_type = normalized_object_type(current_layer.name()) if provider.name() in VALID_PROVIDERS and valid_object_type: tdi_layer = True elif current_layer.name() in ("flowlines", "nodes"): tdi_layer = True # activate button if 3Di layers found self.addSelectedObjectButton.setEnabled(tdi_layer) def add_objects(self): canvas = self.iface.mapCanvas() current_layer = canvas.currentLayer() if not current_layer: # todo: feedback select layer first return provider = current_layer.dataProvider() if provider.name() not in VALID_PROVIDERS: return if current_layer.name() not in list(LAYER_QH_TYPE_MAPPING.keys()): if current_layer.name() not in ("flowlines", "nodes"): # todo: feedback layer not supported return selected_features = current_layer.selectedFeatures() if current_layer.name() == "flowlines": self.q_graph_widget.add_objects(current_layer, selected_features) self.graphTabWidget.setCurrentIndex( self.graphTabWidget.indexOf(self.q_graph_widget)) return elif current_layer.name() == "nodes": self.h_graph_widget.add_objects(current_layer, selected_features) self.graphTabWidget.setCurrentIndex( self.graphTabWidget.indexOf(self.h_graph_widget)) return if LAYER_QH_TYPE_MAPPING[current_layer.name()] == "q": self.q_graph_widget.add_objects(current_layer, selected_features) self.graphTabWidget.setCurrentIndex( self.graphTabWidget.indexOf(self.q_graph_widget)) else: self.h_graph_widget.add_objects(current_layer, selected_features) self.graphTabWidget.setCurrentIndex( self.graphTabWidget.indexOf(self.h_graph_widget)) def on_btnstate(self, state): """Toggle ``absolute`` state of the GraphPlots""" checked = state == Qt.Checked self.q_graph_widget.graph_plot.absolute = ( self.h_graph_widget.graph_plot.absolute) = checked def setup_ui(self, dock_widget): """ initiate main Qt building blocks of interface :param dock_widget: QDockWidget instance """ dock_widget.setObjectName("dock_widget") dock_widget.setAttribute(Qt.WA_DeleteOnClose) self.dockWidgetContent = QWidget(self) self.dockWidgetContent.setObjectName("dockWidgetContent") self.mainVLayout = QVBoxLayout(self.dockWidgetContent) self.dockWidgetContent.setLayout(self.mainVLayout) # add button to add objects to graphs self.buttonBarHLayout = QHBoxLayout(self) self.addSelectedObjectButton = QPushButton(self.dockWidgetContent) self.addSelectedObjectButton.setObjectName("addSelectedObjectButton") self.checkbox = QCheckBox("Absolute", parent=self.dockWidgetContent) self.checkbox.setChecked(False) self.checkbox.stateChanged.connect(self.on_btnstate) self.buttonBarHLayout.addWidget(self.addSelectedObjectButton) self.buttonBarHLayout.addWidget(self.checkbox) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.buttonBarHLayout.addItem(spacerItem) self.mainVLayout.addItem(self.buttonBarHLayout) # add tabWidget for graphWidgets self.graphTabWidget = QTabWidget(self.dockWidgetContent) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(6) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.graphTabWidget.sizePolicy().hasHeightForWidth()) self.graphTabWidget.setSizePolicy(sizePolicy) self.graphTabWidget.setObjectName("graphTabWidget") self.mainVLayout.addWidget(self.graphTabWidget) # add dockwidget dock_widget.setWidget(self.dockWidgetContent) self.retranslate_ui(dock_widget) QMetaObject.connectSlotsByName(dock_widget) def retranslate_ui(self, DockWidget): DockWidget.setWindowTitle("3Di result plots %i" % self.nr) self.addSelectedObjectButton.setText("Add")