class ExpressionLineEdit(QLineEdit): def __init__(self, column, host=None, parent=None): # Use a different pixmap self._current_profile = current_profile() QLineEdit.__init__(self, parent) self.column = column self._entity = self.column.entity self.layer = self.create_layer() self.host = host # Configure load button self.btn_load = QToolButton(parent) self.btn_load.setCursor(Qt.PointingHandCursor) self.btn_load.setFocusPolicy(Qt.NoFocus) px = GuiUtils.get_icon_pixmap('expression.png') self.btn_load.setIcon(QIcon(px)) self.btn_load.setIconSize(px.size()) self.btn_load.setStyleSheet('background: transparent; padding: 0px; ' 'border: none;') frame_width = self.set_button_minimum_size(self.btn_load) # Ensure that text does not overlay button padding = self.btn_load.sizeHint().width() + frame_width + 1 self.setStyleSheet('padding-right: ' + str(padding * 2) + 'px;') # Set layout self.button_layout = QHBoxLayout(self) self.button_layout.addWidget(self.btn_load, 0, Qt.AlignRight) self.button_layout.setSpacing(0) self.button_layout.setMargin(5) # Readonly as text generated automatically self.setReadOnly(True) # Current model object self._current_item = None def create_layer(self): srid = None column = '' if self.entity.has_geometry_column(): geom_cols = [col.name for col in self.entity.columns.values() if col.TYPE_INFO == 'GEOMETRY'] column = geom_cols[0] geom_col_obj = self.entity.columns[column] if geom_col_obj.srid >= 100000: srid = geom_col_obj.srid layer = vector_layer(self.entity.name, geom_column=column, proj_wkt=srid) return layer def get_feature_value(self, model=None): self.layer.startEditing() feature = None request = QgsFeatureRequest() if model is None: model = self.host.model() request.setFilterFid(model.id) feature_itr = self.layer.getFeatures(request) for feat in feature_itr: feature = feat break exp = QgsExpression(self.column.expression) if exp.hasParserError(): raise Exception(exp.parserErrorString()) exp.prepare(self.layer.fields()) if feature is not None: value = exp.evaluate(feature) return value else: return None def set_button_minimum_size(self, button): """ Sets the minimum button size. :param button: The button to be set. :type button: QToolButton :return: Returns the frame width of the button :rtype: Integer """ frame_width = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) msz = self.minimumSizeHint() self.setMinimumSize( max( msz.width(), button.sizeHint().height() + frame_width * 2 + 2 ), max( msz.height(), button.sizeHint().height() + frame_width * 2 + 2 ) ) return frame_width @property def entity(self): """ :return: Returns the entity object corresponding to this widget. :rtype: Entity """ return self._entity def clear_line_edit(self): """ Clears the text in the line edit. """ self.clear() def on_expression_triggered(self, model=None): """ Slot raised to load browser for selecting foreign key entities. To be implemented by subclasses. """ self.format_display(self.get_feature_value(model)) return self.get_feature_value() def format_display(self, value): """ Extract object values to show in the line edit based on the specified display columns. """ if value is not None: self.setText(str(value))
class ForeignKeyLineEdit(QLineEdit): """ Line edit that enables the browsing of related entities defined through foreign key constraint. """ def __init__(self, column, parent=None, pixmap=None, host=None): """ Class constructor. :param column: Column object containing foreign key information. :type column: BaseColumn :param parent: Parent widget for the control. :type parent: QWidget :param pixmap: Pixmap to use for the line edit button. :type pixmap: QPixmap """ QLineEdit.__init__(self, parent) self.column = column self._entity = self.column.entity self.entity_dialog = host # Configure load button self.btn_load = QToolButton(parent) self.btn_load.setCursor(Qt.PointingHandCursor) self.btn_load.setFocusPolicy(Qt.NoFocus) px = GuiUtils.get_icon_pixmap('select_record.png') if not pixmap is None: px = pixmap self.btn_load.setIcon(QIcon(px)) self.btn_load.setIconSize(px.size()) self.btn_load.setStyleSheet('background: transparent; padding: 0px; ' 'border: none;') self.btn_load.clicked.connect(self.on_load_foreign_key_browser) clear_px = GuiUtils.get_icon_pixmap('clear.png') self.btn_clear = QToolButton(parent) self.btn_clear.setCursor(Qt.PointingHandCursor) self.btn_clear.setFocusPolicy(Qt.NoFocus) self.btn_clear.setIcon(QIcon(clear_px)) self.btn_clear.setIconSize(clear_px.size()) self.btn_clear.setStyleSheet('background: transparent; padding: 0px; ' 'border: none;') self.btn_clear.clicked.connect(self.clear_line_edit) frame_width = self.set_button_minimum_size(self.btn_load) self.set_button_minimum_size(self.btn_clear) # Ensure that text does not overlay button padding = self.btn_load.sizeHint().width() + frame_width + 1 self.setStyleSheet('padding-right: ' + str(padding * 2) + 'px;') # Set layout self.button_layout = QHBoxLayout(self) self.button_layout.addWidget(self.btn_clear, 0, Qt.AlignRight) self.button_layout.addWidget(self.btn_load, 0, Qt.AlignRight) self.button_layout.setSpacing(0) self.button_layout.setMargin(5) self.btn_clear.setVisible(False) # Readonly as text is loaded from the related entity self.setReadOnly(True) # Current model object self._current_item = None def set_button_minimum_size(self, button): """ Sets the minimum button size. :param button: The button to be set. :type button: QToolButton :return: Returns the frame width of the button :rtype: Integer """ frame_width = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) msz = self.minimumSizeHint() self.setMinimumSize( max( msz.width(), button.sizeHint().height() + frame_width * 2 + 2 ), max( msz.height(), button.sizeHint().height() + frame_width * 2 + 2 ) ) return frame_width @property def current_item(self): return self._current_item @current_item.setter def current_item(self, value): # Update display every time the current item is changed. self._current_item = value self.format_display() @property def entity(self): """ :return: Returns the entity object corresponding to this widget. :rtype: Entity """ return self._entity def clear_line_edit(self): """ Clears the text in the line edit. """ self.clear() self.hide_clear_button() def hide_clear_button(self): """ Hides the clear button. """ self.btn_clear.setVisible(False) self.button_layout.setStretch(0, 0) def show_clear_button(self): """ Shows the clear button if a text exists. """ if len(self.text()) > 0: self.btn_clear.setVisible(True) self.button_layout.setStretch(0, 5) def on_load_foreign_key_browser(self): """ Slot raised to load browser for selecting foreign key entities. To be implemented by subclasses. """ raise NotImplementedError def format_display(self): """ Extract object values to show in the line edit based on the specified display columns. """ raise NotImplementedError def parent_entity_model(self): """ :return: Returns the database model corresponding to the parent table of the relation defined by this column. Please note that the database model will not contain relationship configurations in its attributes. :rtype: object """ entity = self.column.entity_relation.parent return entity_model(entity, entity_only=True) def load_current_item_from_id(self, id): """ Loads the current item from the id corresponding to the primary key. :param id: Primary key of the referenced entity. :type id: int """ QApplication.processEvents() model = self.parent_entity_model() if model is None: return model_obj = model() res = model_obj.queryObject().filter(model.id == id).first() if not res is None: self.current_item = res