class TimeSeriesView(QWidget): DEFAULT_ROW_HEIGHT = 150 # Emitted when some styles have been updated styles_updated = pyqtSignal() def __init__(self, title=None, image_dir=None, parent=None): QWidget.__init__(self, parent) self.__toolbar = QToolBar() self.__scene = MyScene(0, 0, 600, 400) self.__view = TimeSeriesGraphicsView(self.__scene) self.__view.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.__scene.sceneRectChanged.connect(self.on_rect_changed) if image_dir is None: image_dir = os.path.join(os.path.dirname(__file__), "img") self.__action_move_row_up = QAction( QIcon(os.path.join(image_dir, "up.svg")), "Move the row up", self.__toolbar) self.__action_move_row_up.triggered.connect(self.on_move_row_up) self.__action_move_row_down = QAction( QIcon(os.path.join(image_dir, "down.svg")), "Move the row down", self.__toolbar) self.__action_move_row_down.triggered.connect(self.on_move_row_down) self.__action_edit_style = QAction( QIcon(os.path.join(image_dir, "symbology.svg")), "Edit row style", self.__toolbar) self.__action_edit_style.triggered.connect(self.on_edit_style) self.__action_add_row = QAction( QIcon(os.path.join(image_dir, "add.svg")), "Add a data row", self.__toolbar) self.__action_add_row.triggered.connect(self.on_add_row) self.__action_remove_row = QAction( QIcon(os.path.join(image_dir, "remove.svg")), "Remove the row", self.__toolbar) self.__action_remove_row.triggered.connect(self.on_remove_row) self.__toolbar.addAction(self.__action_move_row_up) self.__toolbar.addAction(self.__action_move_row_down) self.__toolbar.addAction(self.__action_edit_style) self.__toolbar.addAction(self.__action_add_row) self.__toolbar.addAction(self.__action_remove_row) self.__title_label = QLabel() if title is not None: self.set_title(title) self.__status_bar = QStatusBar() vbox = QVBoxLayout() vbox.addWidget(self.__title_label) vbox.addWidget(self.__toolbar) vbox.addWidget(self.__view) vbox.addWidget(self.__status_bar) self.setLayout(vbox) self.__station_id = None # (log_item, legend_item) for each row self.__rows = [] # { layer : (log_item, legend_item) } self.__data2logitems = {} self.__row_heights = [] self._min_x = None self._max_x = None self.__allow_mouse_translation = True self.__translation_orig = None self.__style_dir = os.path.join(os.path.dirname(__file__), 'styles') self.select_row(-1) self._update_row_depths() def on_rect_changed(self, rect): for item, _ in self.__rows: item.set_width(rect.width()) def set_title(self, title): self.__title_label.setText(title) def _place_items(self): y = 0 for i, r in enumerate(self.__rows): item, legend = r height = self.__row_heights[i] legend.setPos(0, y) item.setPos(legend.boundingRect().width(), y) y += height rect = self.__scene.sceneRect() rect.setHeight(y) self.__scene.setSceneRect(rect) def _add_row(self, log_item, legend_item): self.__scene.addItem(log_item) self.__scene.addItem(legend_item) log_item.set_min_depth(self._min_x) log_item.set_max_depth(self._max_x) self.__rows.insert(0, (log_item, legend_item)) self.__row_heights.insert(0, log_item.boundingRect().height()) self._place_items() def set_x_range(self, min_x, max_x): self._min_x, self._max_x = min_x, max_x self._update_row_depths() def _update_row_depths(self): if self._min_x is None: return for item, _ in self.__rows: item.set_min_depth(self._min_x) item.set_max_depth(self._max_x) item.update() def remove_data_row(self, data): """Remove data row from widget :param data: data to be removed """ # Row doesn't exist if data not in self.__data2logitems: raise ValueError("Impossible to remove data row : given data" " object doesn't exist") log_item, legend_item = self.__data2logitems[data] for i, (pitem, litem) in enumerate(self.__rows): if pitem == log_item and litem == legend_item: self.__rows.pop(i) self.__row_widths.pop(i) del self.__data2logitems[data] self.__scene.removeItem(log_item) self.__scene.removeItem(legend_item) return # Rows not found assert False def clear_data_rows(self): # remove item from scenes for (item, legend) in self.__rows: self.__scene.removeItem(legend) self.__scene.removeItem(item) # remove from internal lists self.__rows = [] self.__rows_widths = [] self.__data2logitems = {} self.select_row(-1) self._place_items() self._update_button_visibility() self.add_time_scale() def on_plot_tooltip(self, station_name, txt): if station_name is not None: self.__status_bar.showMessage( u"Station: {} ".format(station_name) + txt) else: self.__status_bar.showMessage(txt) def add_data_row(self, data, title, uom, station_name=None, config=None): """ Parameters ---------- data: ?? title: str uom: str Unit of measure station_name: str Station name config: PlotConfig """ symbology, symbology_type = config.get_symbology() plot_item = PlotItem(size=QSizeF(self.__scene.width(), self.DEFAULT_ROW_HEIGHT), render_type=POINT_RENDERER if not symbology_type else symbology_type, symbology=symbology, x_orientation=ORIENTATION_LEFT_TO_RIGHT, y_orientation=ORIENTATION_UPWARD) plot_item.style_updated.connect(self.styles_updated) plot_item.set_layer(data.get_layer()) plot_item.tooltipRequested.connect( lambda txt: self.on_plot_tooltip(station_name, txt)) legend_item = LegendItem(self.DEFAULT_ROW_HEIGHT, title, unit_of_measure=uom, is_vertical=True) data.data_modified.connect( lambda data=data: self._update_data_row(data, config)) # center on new data self._min_x, self._max_x = data.get_x_min(), data.get_x_max() # if we have only one value, center it on a 2 minutes range if self._min_x and self._min_x == self._max_x: self._min_x -= 60 self._max_x += 60 self.__data2logitems[data] = (plot_item, legend_item) self._add_row(plot_item, legend_item) self._update_data_row(data, config) self._update_row_depths() def add_time_scale(self, title="Time"): scale_item = TimeScaleItem(self.__scene.width(), self.DEFAULT_ROW_HEIGHT * 3 / 4, self._min_x, self._max_x) legend_item = LegendItem(self.DEFAULT_ROW_HEIGHT * 3 / 4, title, is_vertical=True) self._add_row(scale_item, legend_item) def _update_data_row(self, data, config): plot_item, legend_item = self.__data2logitems[data] y_values = data.get_y_values() x_values = data.get_x_values() if y_values is None or x_values is None: plot_item.set_data_window(None) return plot_item.set_data(data.get_x_values(), data.get_y_values()) win = plot_item.data_window() min_x, min_y, max_x, max_y = win.left(), win.top(), win.right( ), win.bottom() if config and config.get("min") is not None: min_y = float(config['min']) if config and config.get("max") is not None: max_y = float(config['max']) # legend legend_item.set_scale(min_y, max_y) plot_item.set_data_window( QRectF(min_x, min_y, max_x - min_x, max_y - min_y)) self.__scene.update() def select_row_at(self, pos): y = pos.y() r = 0 selected = -1 for i, height in enumerate(self.__row_heights): if y >= r and y < r + height: selected = i break r += height self.select_row(selected) def select_row(self, idx): self.__selected_row = idx for i, p in enumerate(self.__rows): item, legend = p item.set_selected(idx == i) legend.set_selected(idx == i) item.update() legend.update() self._update_button_visibility() def _update_button_visibility(self): idx = self.__selected_row self.__action_move_row_up.setEnabled(idx != -1 and idx > 0) self.__action_move_row_down.setEnabled(idx != -1 and idx < len(self.__rows) - 1) self.__action_edit_style.setEnabled(idx != -1) self.__action_remove_row.setEnabled(idx != -1) def on_move_row_up(self): if self.__selected_row < 1: return sel = self.__selected_row self.__rows[sel - 1], self.__rows[sel] = self.__rows[sel], self.__rows[sel - 1] self.__row_heights[sel - 1], self.__row_heights[ sel] = self.__row_heights[sel], self.__row_heights[sel - 1] self.__selected_row -= 1 self._place_items() self._update_button_visibility() def on_move_row_down(self): if self.__selected_row == -1 or self.__selected_row >= len( self.__rows) - 1: return sel = self.__selected_row self.__rows[sel + 1], self.__rows[sel] = self.__rows[sel], self.__rows[sel + 1] self.__row_heights[sel + 1], self.__row_heights[ sel] = self.__row_heights[sel], self.__row_heights[sel + 1] self.__selected_row += 1 self._place_items() self._update_button_visibility() def on_remove_row(self): if self.__selected_row == -1: return sel = self.__selected_row # remove item from scenes item, legend = self.__rows[sel] self.__scene.removeItem(legend) self.__scene.removeItem(item) # remove from internal list del self.__rows[sel] del self.__row_heights[sel] self.__selected_row = -1 self._place_items() self._update_button_visibility() def on_edit_style(self): if self.__selected_row == -1: return item = self.__rows[self.__selected_row][0] item.edit_style() def on_add_row(self): # to be overridden by subclasses pass
def __init__(self, title=None, image_dir=None, parent=None): QWidget.__init__(self, parent) self.__toolbar = QToolBar() self.__scene = MyScene(0, 0, 600, 400) self.__view = TimeSeriesGraphicsView(self.__scene) self.__view.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.__scene.sceneRectChanged.connect(self.on_rect_changed) if image_dir is None: image_dir = os.path.join(os.path.dirname(__file__), "img") self.__action_move_row_up = QAction( QIcon(os.path.join(image_dir, "up.svg")), "Move the row up", self.__toolbar) self.__action_move_row_up.triggered.connect(self.on_move_row_up) self.__action_move_row_down = QAction( QIcon(os.path.join(image_dir, "down.svg")), "Move the row down", self.__toolbar) self.__action_move_row_down.triggered.connect(self.on_move_row_down) self.__action_edit_style = QAction( QIcon(os.path.join(image_dir, "symbology.svg")), "Edit row style", self.__toolbar) self.__action_edit_style.triggered.connect(self.on_edit_style) self.__action_add_row = QAction( QIcon(os.path.join(image_dir, "add.svg")), "Add a data row", self.__toolbar) self.__action_add_row.triggered.connect(self.on_add_row) self.__action_remove_row = QAction( QIcon(os.path.join(image_dir, "remove.svg")), "Remove the row", self.__toolbar) self.__action_remove_row.triggered.connect(self.on_remove_row) self.__toolbar.addAction(self.__action_move_row_up) self.__toolbar.addAction(self.__action_move_row_down) self.__toolbar.addAction(self.__action_edit_style) self.__toolbar.addAction(self.__action_add_row) self.__toolbar.addAction(self.__action_remove_row) self.__title_label = QLabel() if title is not None: self.set_title(title) self.__status_bar = QStatusBar() vbox = QVBoxLayout() vbox.addWidget(self.__title_label) vbox.addWidget(self.__toolbar) vbox.addWidget(self.__view) vbox.addWidget(self.__status_bar) self.setLayout(vbox) self.__station_id = None # (log_item, legend_item) for each row self.__rows = [] # { layer : (log_item, legend_item) } self.__data2logitems = {} self.__row_heights = [] self._min_x = None self._max_x = None self.__allow_mouse_translation = True self.__translation_orig = None self.__style_dir = os.path.join(os.path.dirname(__file__), 'styles') self.select_row(-1) self._update_row_depths()
def setupUi(self): self.setWindowTitle(self.tr("DB Manager")) self.setWindowIcon(QIcon(":/db_manager/icon")) self.resize(QSize(700, 500).expandedTo(self.minimumSizeHint())) # create central tab widget and add the first 3 tabs: info, table and preview self.tabs = QTabWidget() self.info = InfoViewer(self) self.tabs.addTab(self.info, self.tr("Info")) self.table = TableViewer(self) self.tabs.addTab(self.table, self.tr("Table")) self.preview = LayerPreview(self) self.tabs.addTab(self.preview, self.tr("Preview")) self.setCentralWidget(self.tabs) # display close button for all tabs but the first 3 ones, i.e. # HACK: just hide the close button where not needed (GS) self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(self.close_tab) tabbar = self.tabs.tabBar() for i in range(3): btn = tabbar.tabButton(i, QTabBar.RightSide) if tabbar.tabButton(i, QTabBar.RightSide) else tabbar.tabButton(i, QTabBar.LeftSide) btn.resize(0, 0) btn.hide() # Creates layout for message bar self.layout = QGridLayout(self.info) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # init messageBar instance self.infoBar = QgsMessageBar(self.info) sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # create database tree self.dock = QDockWidget("Tree", self) self.dock.setObjectName("DB_Manager_DBView") self.dock.setFeatures(QDockWidget.DockWidgetMovable) self.tree = DBTree(self) self.dock.setWidget(self.tree) self.addDockWidget(Qt.LeftDockWidgetArea, self.dock) # create status bar self.statusBar = QStatusBar(self) self.setStatusBar(self.statusBar) # create menus self.menuBar = QMenuBar(self) self.menuDb = QMenu(self.tr("&Database"), self) self.menuBar.addMenu(self.menuDb) self.menuSchema = QMenu(self.tr("&Schema"), self) actionMenuSchema = self.menuBar.addMenu(self.menuSchema) self.menuTable = QMenu(self.tr("&Table"), self) actionMenuTable = self.menuBar.addMenu(self.menuTable) self.menuHelp = None # QMenu(self.tr("&Help"), self) # actionMenuHelp = self.menuBar.addMenu(self.menuHelp) self.setMenuBar(self.menuBar) # create toolbar self.toolBar = QToolBar("Default", self) self.toolBar.setObjectName("DB_Manager_ToolBar") self.addToolBar(self.toolBar) # create menus' actions # menu DATABASE sep = self.menuDb.addSeparator() sep.setObjectName("DB_Manager_DbMenu_placeholder") sep.setVisible(False) self.actionRefresh = self.menuDb.addAction(QIcon(":/db_manager/actions/refresh"), self.tr("&Refresh"), self.refreshActionSlot, QKeySequence("F5")) self.actionSqlWindow = self.menuDb.addAction(QIcon(":/db_manager/actions/sql_window"), self.tr("&SQL window"), self.runSqlWindow, QKeySequence("F2")) self.menuDb.addSeparator() self.actionClose = self.menuDb.addAction(QIcon(), self.tr("&Exit"), self.close, QKeySequence("CTRL+Q")) # menu SCHEMA sep = self.menuSchema.addSeparator() sep.setObjectName("DB_Manager_SchemaMenu_placeholder") sep.setVisible(False) actionMenuSchema.setVisible(False) # menu TABLE sep = self.menuTable.addSeparator() sep.setObjectName("DB_Manager_TableMenu_placeholder") sep.setVisible(False) self.actionImport = self.menuTable.addAction(QIcon(":/db_manager/actions/import"), self.tr("&Import layer/file"), self.importActionSlot) self.actionExport = self.menuTable.addAction(QIcon(":/db_manager/actions/export"), self.tr("&Export to file"), self.exportActionSlot) self.menuTable.addSeparator() #self.actionShowSystemTables = self.menuTable.addAction(self.tr("Show system tables/views"), self.showSystemTables) #self.actionShowSystemTables.setCheckable(True) #self.actionShowSystemTables.setChecked(True) actionMenuTable.setVisible(False) # add actions to the toolbar self.toolBar.addAction(self.actionRefresh) self.toolBar.addAction(self.actionSqlWindow) self.toolBar.addAction(self.actionImport) self.toolBar.addAction(self.actionExport)
def __init__(self, title=None,image_dir=None, parent=None): QWidget.__init__(self, parent) self.toolbar = QToolBar() self.__log_scene = MyScene(0, 0, 600, 600) self.__log_view = LogGraphicsView(self.__log_scene) self.__log_view.setAlignment(Qt.AlignLeft|Qt.AlignTop) self.__log_scene.sceneRectChanged.connect(self.on_rect_changed) if image_dir is None: image_dir = os.path.join(os.path.dirname(__file__), "img") self.__action_move_column_left = QAction(QIcon(os.path.join(image_dir, "left.svg")), "Move the column to the left", self.toolbar) self.__action_move_column_left.triggered.connect(self.on_move_column_left) self.__action_move_column_right = QAction(QIcon(os.path.join(image_dir, "right.svg")), "Move the column to the right", self.toolbar) self.__action_move_column_right.triggered.connect(self.on_move_column_right) self.__action_edit_style = QAction(QIcon(os.path.join(image_dir, "symbology.svg")), "Edit column style", self.toolbar) self.__action_edit_style.triggered.connect(self.on_edit_style) self.__action_add_column = QAction(QIcon(os.path.join(image_dir, "add.svg")), "Add a data column from configured ones", self.toolbar) self.__action_add_column.triggered.connect(self.on_add_column) self.__action_remove_column = QAction(QIcon(os.path.join(image_dir, "remove.svg")), "Remove the column", self.toolbar) self.__action_remove_column.triggered.connect(self.on_remove_column) #self.__action_move_content_right = QAction("Move content right", self.toolbar) #self.__action_move_content_left = QAction("Move content left", self.toolbar) #self.__action_move_content_left.triggered.connect(self.on_move_content_left) #self.__action_move_content_right.triggered.connect(self.on_move_content_right) self.toolbar.addAction(self.__action_move_column_left) self.toolbar.addAction(self.__action_move_column_right) self.toolbar.addAction(self.__action_edit_style) self.toolbar.addAction(self.__action_add_column) self.toolbar.addAction(self.__action_remove_column) #self.__toolbar.addAction(self.__action_move_content_left) #self.__toolbar.addAction(self.__action_move_content_right) self.__title_label = QLabel() if title is not None: self.set_title(title) self.__status_bar = QStatusBar() vbox = QVBoxLayout() vbox.addWidget(self.__title_label) vbox.addWidget(self.toolbar) vbox.addWidget(self.__log_view) vbox.addWidget(self.__status_bar) self.setLayout(vbox) self.__station_id = None # (log_item, legend_item) for each column self.__columns = [] # { layer : (log_item, legend_item) } self.__data2logitems = {} self.__column_widths = [] self._min_z = 0 self._max_z = 40 self.__allow_mouse_translation = True self.__translation_orig = None self.__style_dir = os.path.join(os.path.dirname(__file__), 'styles') self.select_column(-1) # by default we have a Z scale self.add_z_scale()
class WellLogView(QWidget): DEFAULT_COLUMN_WIDTH = 150 # Emitted when some styles have been updated styles_updated = pyqtSignal() def __init__(self, title=None,image_dir=None, parent=None): QWidget.__init__(self, parent) self.toolbar = QToolBar() self.__log_scene = MyScene(0, 0, 600, 600) self.__log_view = LogGraphicsView(self.__log_scene) self.__log_view.setAlignment(Qt.AlignLeft|Qt.AlignTop) self.__log_scene.sceneRectChanged.connect(self.on_rect_changed) if image_dir is None: image_dir = os.path.join(os.path.dirname(__file__), "img") self.__action_move_column_left = QAction(QIcon(os.path.join(image_dir, "left.svg")), "Move the column to the left", self.toolbar) self.__action_move_column_left.triggered.connect(self.on_move_column_left) self.__action_move_column_right = QAction(QIcon(os.path.join(image_dir, "right.svg")), "Move the column to the right", self.toolbar) self.__action_move_column_right.triggered.connect(self.on_move_column_right) self.__action_edit_style = QAction(QIcon(os.path.join(image_dir, "symbology.svg")), "Edit column style", self.toolbar) self.__action_edit_style.triggered.connect(self.on_edit_style) self.__action_add_column = QAction(QIcon(os.path.join(image_dir, "add.svg")), "Add a data column from configured ones", self.toolbar) self.__action_add_column.triggered.connect(self.on_add_column) self.__action_remove_column = QAction(QIcon(os.path.join(image_dir, "remove.svg")), "Remove the column", self.toolbar) self.__action_remove_column.triggered.connect(self.on_remove_column) #self.__action_move_content_right = QAction("Move content right", self.toolbar) #self.__action_move_content_left = QAction("Move content left", self.toolbar) #self.__action_move_content_left.triggered.connect(self.on_move_content_left) #self.__action_move_content_right.triggered.connect(self.on_move_content_right) self.toolbar.addAction(self.__action_move_column_left) self.toolbar.addAction(self.__action_move_column_right) self.toolbar.addAction(self.__action_edit_style) self.toolbar.addAction(self.__action_add_column) self.toolbar.addAction(self.__action_remove_column) #self.__toolbar.addAction(self.__action_move_content_left) #self.__toolbar.addAction(self.__action_move_content_right) self.__title_label = QLabel() if title is not None: self.set_title(title) self.__status_bar = QStatusBar() vbox = QVBoxLayout() vbox.addWidget(self.__title_label) vbox.addWidget(self.toolbar) vbox.addWidget(self.__log_view) vbox.addWidget(self.__status_bar) self.setLayout(vbox) self.__station_id = None # (log_item, legend_item) for each column self.__columns = [] # { layer : (log_item, legend_item) } self.__data2logitems = {} self.__column_widths = [] self._min_z = 0 self._max_z = 40 self.__allow_mouse_translation = True self.__translation_orig = None self.__style_dir = os.path.join(os.path.dirname(__file__), 'styles') self.select_column(-1) # by default we have a Z scale self.add_z_scale() def on_rect_changed(self, rect): for item, _ in self.__columns: item.set_height(rect.height()) def set_title(self, title): self.__title_label.setText(title) def _place_items(self): x = 0 for i, c in enumerate(self.__columns): item, legend = c width = self.__column_widths[i] legend.setPos(x, 0) item.setPos(x, legend.boundingRect().height()) x += width rect = self.__log_scene.sceneRect() rect.setWidth(x) self.__log_scene.setSceneRect(rect) def _add_column(self, log_item, legend_item): self.__log_scene.addItem(log_item) self.__log_scene.addItem(legend_item) log_item.set_min_depth(self._min_z) log_item.set_max_depth(self._max_z) self.__columns.append((log_item, legend_item)) self.__column_widths.append(log_item.boundingRect().width()) self._place_items() def _fit_to_max_depth(self): self._min_z = min([i.min_depth() for i, _ in self.__columns if i.min_depth() is not None]) self._max_z = max([i.max_depth() for i, _ in self.__columns if i.max_depth() is not None]) # if we have only one value, center it on a 2 meters range if self._min_z == self._max_z: self._min_z -= 1.0 self._max_z += 1.0 def _update_column_depths(self): for item, _ in self.__columns: item.set_min_depth(self._min_z) item.set_max_depth(self._max_z) item.update() def add_z_scale(self, title="Depth"): scale_item = ZScaleItem(self.DEFAULT_COLUMN_WIDTH / 2, self.__log_scene.height(), self._min_z, self._max_z) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH / 2, title, unit_of_measure="m") self._add_column(scale_item, legend_item) def remove_data_column(self, data): """Remove data column from widget :param data: data to be removed """ # Column doesn't exist if data not in self.__data2logitems: raise ValueError("Impossible to remove data column : given data" " object doesn't exist") log_item, legend_item = self.__data2logitems[data] for i, (pitem, litem) in enumerate(self.__columns): if pitem == log_item and litem == legend_item: self.__columns.pop(i) self.__column_widths.pop(i) del self.__data2logitems[data] self.__log_scene.removeItem(log_item) self.__log_scene.removeItem(legend_item) return # Columns not found assert False def clear_data_columns(self): # remove item from scenes for (item, legend) in self.__columns: self.__log_scene.removeItem(legend) self.__log_scene.removeItem(item) # remove from internal lists self.__columns = [] self.__column_widths = [] self.__data2logitems = {} self.__selected_column = -1 self._place_items() self._update_button_visibility() # still add z scale self.add_z_scale() def on_plot_tooltip(self, txt, station_name = None): if station_name is not None: self.__status_bar.showMessage(u"Station: {} ".format(station_name) + txt) else: self.__status_bar.showMessage(txt) def add_data_column(self, data, title, uom, station_name = None, config = None): """ Parameters ---------- data: ?? title: str uom: str Unit of measure station_name: str Station name config: PlotConfig """ symbology, symbology_type = config.get_symbology() plot_item = PlotItem(size=QSizeF(self.DEFAULT_COLUMN_WIDTH, self.__log_scene.height()), render_type = POLYGON_RENDERER if symbology_type is None else symbology_type, symbology = symbology, x_orientation = ORIENTATION_DOWNWARD, y_orientation = ORIENTATION_LEFT_TO_RIGHT) plot_item.style_updated.connect(self.styles_updated) plot_item.set_layer(data.get_layer()) plot_item.tooltipRequested.connect(lambda txt: self.on_plot_tooltip(txt, station_name)) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title, unit_of_measure=uom) data.data_modified.connect(lambda data=data : self._update_data_column(data, config)) self.__data2logitems[data] = (plot_item, legend_item) self._add_column(plot_item, legend_item) self._update_data_column(data, config) self._update_column_depths() def _update_data_column(self, data, config): plot_item, legend_item = self.__data2logitems[data] y_values = data.get_y_values() x_values = data.get_x_values() if y_values is None or x_values is None: plot_item.set_data_window(None) return plot_item.set_data(data.get_x_values(), data.get_y_values()) win = plot_item.data_window() min_x, min_y, max_x, max_y = win.left(), win.top(), win.right(), win.bottom() if config and config.get('min') is not None: min_y = float(config['min']) if config and config.get('max') is not None: max_y = float(config['max']) # legend legend_item.set_scale(min_y, max_y) plot_item.set_data_window(QRectF(min_x, min_y, max_x-min_x, max_y-min_y)) self.__log_scene.update() def add_stratigraphy(self, layer, filter_expression, column_mapping, title, style_file=None, config=None): """Add stratigraphy data Parameters ---------- layer: QgsVectorLayer The layer where stratigraphic data are stored filter_expression: str A QGIS expression to filter the vector layer column_mapping: dict Dictionary of column names title: str Title of the graph style_file: str Name of the style file to use config: PlotConfig """ symbology = config.get_symbology()[0] if config else None item = StratigraphyItem(self.DEFAULT_COLUMN_WIDTH, self.__log_scene.height(), style_file=style_file if not symbology else None, symbology=symbology, column_mapping=column_mapping ) item.style_updated.connect(self.styles_updated) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title) item.set_layer(layer) item.tooltipRequested.connect(self.on_plot_tooltip) req = QgsFeatureRequest() req.setFilterExpression(filter_expression) item.set_data(list(layer.getFeatures(req))) self._add_column(item, legend_item) def add_imagery(self, image_filename, title, depth_from, depth_to): item = ImageryDataItem(self.DEFAULT_COLUMN_WIDTH, self.__log_scene.height(), image_filename, depth_from, depth_to) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title) self._add_column(item, legend_item) def select_column_at(self, pos): x = pos.x() c = 0 selected = -1 for i, width in enumerate(self.__column_widths): if x >= c and x < c + width: selected = i break c += width self.select_column(selected) def select_column(self, idx): self.__selected_column = idx for i, p in enumerate(self.__columns): item, legend = p item.set_selected(idx == i) legend.set_selected(idx == i) item.update() legend.update() self._update_button_visibility() def selected_column(self): return self.__selected_column def _update_button_visibility(self): idx = self.__selected_column self.__action_move_column_left.setEnabled(idx != -1 and idx > 0) self.__action_move_column_right.setEnabled(idx != -1 and idx < len(self.__columns) - 1) item = self.__columns[idx][0] if idx > 0 else None self.__action_edit_style.setEnabled(bool(item and not isinstance(item, ImageryDataItem))) self.__action_remove_column.setEnabled(idx != -1) def on_move_column_left(self): if self.__selected_column < 1: return sel = self.__selected_column self.__columns[sel-1], self.__columns[sel] = self.__columns[sel], self.__columns[sel-1] self.__column_widths[sel-1], self.__column_widths[sel] = self.__column_widths[sel], self.__column_widths[sel-1] self.__selected_column -= 1 self._place_items() self._update_button_visibility() def on_move_column_right(self): if self.__selected_column == -1 or self.__selected_column >= len(self.__columns) - 1: return sel = self.__selected_column self.__columns[sel+1], self.__columns[sel] = self.__columns[sel], self.__columns[sel+1] self.__column_widths[sel+1], self.__column_widths[sel] = self.__column_widths[sel], self.__column_widths[sel+1] self.__selected_column += 1 self._place_items() self._update_button_visibility() def on_remove_column(self): if self.__selected_column == -1: return sel = self.__selected_column # remove item from scenes item, legend = self.__columns[sel] self.__log_scene.removeItem(legend) self.__log_scene.removeItem(item) # remove from internal list del self.__columns[sel] del self.__column_widths[sel] self.__selected_column = -1 self._place_items() self._update_button_visibility() def on_edit_style(self): if self.__selected_column == -1: return item = self.__columns[self.__selected_column][0] item.edit_style() def on_add_column(self): # to be overridden by subclasses pass def styles(self): """Return the current style of each item""" return dict([(item.layer().id(), item.qgis_style()) for item, legend in self.__columns if hasattr(item, "qgis_style")])
class WellLogView(QWidget): DEFAULT_COLUMN_WIDTH = 150 def __init__(self, title=None, image_dir=None, parent=None): QWidget.__init__(self, parent) toolbar = QToolBar() self.__log_scene = MyScene(0, 0, 600, 600) self.__log_view = LogGraphicsView(self.__log_scene) self.__log_view.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.__log_scene.sceneRectChanged.connect(self.on_rect_changed) if image_dir is None: image_dir = os.path.join(os.path.dirname(__file__), "img") self.__action_move_column_left = QAction( QIcon(os.path.join(image_dir, "left.svg")), "Move the column to the left", toolbar) self.__action_move_column_left.triggered.connect( self.on_move_column_left) self.__action_move_column_right = QAction( QIcon(os.path.join(image_dir, "right.svg")), "Move the column to the right", toolbar) self.__action_move_column_right.triggered.connect( self.on_move_column_right) self.__action_edit_style = QAction( QIcon(os.path.join(image_dir, "symbology.svg")), "Edit column style", toolbar) self.__action_edit_style.triggered.connect(self.on_edit_style) self.__action_add_column = QAction( QIcon(os.path.join(image_dir, "add.svg")), "Add a data column", toolbar) self.__action_add_column.triggered.connect(self.on_add_column) self.__action_remove_column = QAction( QIcon(os.path.join(image_dir, "remove.svg")), "Remove the column", toolbar) self.__action_remove_column.triggered.connect(self.on_remove_column) #self.__action_move_content_right = QAction("Move content right", toolbar) #self.__action_move_content_left = QAction("Move content left", toolbar) #self.__action_move_content_left.triggered.connect(self.on_move_content_left) #self.__action_move_content_right.triggered.connect(self.on_move_content_right) toolbar.addAction(self.__action_move_column_left) toolbar.addAction(self.__action_move_column_right) toolbar.addAction(self.__action_edit_style) toolbar.addAction(self.__action_add_column) toolbar.addAction(self.__action_remove_column) #self.__toolbar.addAction(self.__action_move_content_left) #self.__toolbar.addAction(self.__action_move_content_right) self.__title_label = QLabel() if title is not None: self.set_title(title) self.__status_bar = QStatusBar() vbox = QVBoxLayout() vbox.addWidget(self.__title_label) vbox.addWidget(toolbar) vbox.addWidget(self.__log_view) vbox.addWidget(self.__status_bar) self.setLayout(vbox) self.__station_id = None # (log_item, legend_item) for each column self.__columns = [] # { layer : (log_item, legend_item) } self.__data2logitems = {} self.__column_widths = [] self._min_z = 0 self._max_z = 40 self.__allow_mouse_translation = True self.__translation_orig = None self.__style_dir = os.path.join(os.path.dirname(__file__), 'styles') self.select_column(-1) # by default we have a Z scale self.add_z_scale() def on_rect_changed(self, rect): for item, _ in self.__columns: item.set_height(rect.height()) def set_title(self, title): self.__title_label.setText(title) def _place_items(self): x = 0 for i, c in enumerate(self.__columns): item, legend = c width = self.__column_widths[i] legend.setPos(x, 0) item.setPos(x, legend.boundingRect().height()) x += width self.__log_view.setMinimumSize(x, self.__log_view.minimumSize().height()) def _add_column(self, log_item, legend_item): self.__log_scene.addItem(log_item) self.__log_scene.addItem(legend_item) log_item.set_min_depth(self._min_z) log_item.set_max_depth(self._max_z) self.__columns.append((log_item, legend_item)) self.__column_widths.append(log_item.boundingRect().width()) self._place_items() def _fit_to_max_depth(self): self._min_z = min([ i.min_depth() for i, _ in self.__columns if i.min_depth() is not None ]) self._max_z = max([ i.max_depth() for i, _ in self.__columns if i.max_depth() is not None ]) # if we have only one value, center it on a 2 meters range if self._min_z == self._max_z: self._min_z -= 1.0 self._max_z += 1.0 def _update_column_depths(self): for item, _ in self.__columns: item.set_min_depth(self._min_z) item.set_max_depth(self._max_z) item.update() def add_z_scale(self, title="Depth"): scale_item = ZScaleItem(self.DEFAULT_COLUMN_WIDTH / 2, self.__log_scene.height(), self._min_z, self._max_z) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH / 2, title, unit_of_measure="m") self._add_column(scale_item, legend_item) def remove_data_column(self, data): """Remove data column from widget :param data: data to be removed """ # Column doesn't exist if data not in self.__data2logitems: raise ValueError("Impossible to remove data column : given data" " object doesn't exist") log_item, legend_item = self.__data2logitems[data] for i, (pitem, litem) in enumerate(self.__columns): if pitem == log_item and litem == legend_item: self.__columns.pop(i) self.__column_widths.pop(i) del self.__data2logitems[data] self.__log_scene.removeItem(log_item) self.__log_scene.removeItem(legend_item) return # Columns not found assert False def clear_data_columns(self): # remove item from scenes for (item, legend) in self.__columns: self.__log_scene.removeItem(legend) self.__log_scene.removeItem(item) # remove from internal lists self.__columns = [] self.__column_widths = [] self.__data2logitems = {} self.__selected_column = -1 self._place_items() self._update_button_visibility() def on_plot_tooltip(self, txt, station_name=None): if station_name is not None: self.__status_bar.showMessage( u"Station: {} ".format(station_name) + txt) else: self.__status_bar.showMessage(txt) def add_data_column(self, data, title, uom, station_name=None): plot_item = PlotItem(size=QSizeF(self.DEFAULT_COLUMN_WIDTH, self.__log_scene.height()), render_type=POLYGON_RENDERER, x_orientation=ORIENTATION_DOWNWARD, y_orientation=ORIENTATION_LEFT_TO_RIGHT) plot_item.set_layer(data.get_layer()) plot_item.tooltipRequested.connect( lambda txt: self.on_plot_tooltip(txt, station_name)) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title, unit_of_measure=uom) data.data_modified.connect( lambda data=data: self._update_data_column(data)) self.__data2logitems[data] = (plot_item, legend_item) self._add_column(plot_item, legend_item) self._update_data_column(data) self._update_column_depths() def _update_data_column(self, data): plot_item, legend_item = self.__data2logitems[data] y_values = data.get_y_values() x_values = data.get_x_values() if y_values is None or x_values is None: plot_item.set_data_window(None) return plot_item.set_data(data.get_x_values(), data.get_y_values()) # r = QRectF(0, min_y, (max_x-min_x)/delta, max_y) # plot_item.set_data_window(r) # legend min_str = "{:.1f}".format(min(data.get_y_values())) max_str = "{:.1f}".format(max(data.get_y_values())) legend_item.set_scale(min_str, max_str) self.__log_scene.update() def add_stratigraphy(self, layer, column_mapping, title): item = StratigraphyItem(self.DEFAULT_COLUMN_WIDTH, self.__log_scene.height(), style_file=os.path.join( self.__style_dir, "stratigraphy_style.xml")) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title) item.set_layer(layer) item.tooltipRequested.connect(self.on_plot_tooltip) item.set_data( [[f[c] if c is not None else None for c in column_mapping] for f in layer.getFeatures()]) self._add_column(item, legend_item) def add_imagery(self, image_filename, title, depth_from, depth_to): item = ImageryDataItem(self.DEFAULT_COLUMN_WIDTH, self.__log_scene.height(), image_filename, depth_from, depth_to) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title) self._add_column(item, legend_item) def select_column_at(self, pos): x = pos.x() c = 0 selected = -1 for i, width in enumerate(self.__column_widths): if x >= c and x < c + width: selected = i break c += width self.select_column(selected) def select_column(self, idx): self.__selected_column = idx for i, p in enumerate(self.__columns): item, legend = p item.set_selected(idx == i) legend.set_selected(idx == i) item.update() legend.update() self._update_button_visibility() def selected_column(self): return self.__selected_column def _update_button_visibility(self): idx = self.__selected_column self.__action_move_column_left.setEnabled(idx != -1 and idx > 0) self.__action_move_column_right.setEnabled( idx != -1 and idx < len(self.__columns) - 1) self.__action_edit_style.setEnabled(idx != -1) self.__action_remove_column.setEnabled(idx != -1) def on_move_column_left(self): if self.__selected_column < 1: return sel = self.__selected_column self.__columns[ sel - 1], self.__columns[sel] = self.__columns[sel], self.__columns[sel - 1] self.__column_widths[sel - 1], self.__column_widths[ sel] = self.__column_widths[sel], self.__column_widths[sel - 1] self.__selected_column -= 1 self._place_items() self._update_button_visibility() def on_move_column_right(self): if self.__selected_column == -1 or self.__selected_column >= len( self.__columns) - 1: return sel = self.__selected_column self.__columns[ sel + 1], self.__columns[sel] = self.__columns[sel], self.__columns[sel + 1] self.__column_widths[sel + 1], self.__column_widths[ sel] = self.__column_widths[sel], self.__column_widths[sel + 1] self.__selected_column += 1 self._place_items() self._update_button_visibility() def on_remove_column(self): if self.__selected_column == -1: return sel = self.__selected_column # remove item from scenes item, legend = self.__columns[sel] self.__log_scene.removeItem(legend) self.__log_scene.removeItem(item) # remove from internal list del self.__columns[sel] del self.__column_widths[sel] self.__selected_column = -1 self._place_items() self._update_button_visibility() def on_edit_style(self): if self.__selected_column == -1: return item = self.__columns[self.__selected_column][0] item.edit_style() def on_add_column(self): # to be overridden by subclasses pass
def addLayer(self, layer, headers, types, features): tab = QWidget() tab.layer = layer p1_vertical = QVBoxLayout(tab) p1_vertical.setContentsMargins(0,0,0,0) table = QTableWidget() table.itemSelectionChanged.connect(self.highlight_features) table.title = layer.name() table.crs = layer.crs() table.setColumnCount(len(headers)) if len(features) > 0: table.setRowCount(len(features)) nbrow = len(features) self.loadingWindow.show() self.loadingWindow.setLabelText(table.title) self.loadingWindow.activateWindow() self.loadingWindow.showNormal() # Table population m = 0 for feature in features: n = 0 for cell in feature.attributes(): item = QTableWidgetItem() item.setData(Qt.DisplayRole, cell) item.setFlags(item.flags() ^ Qt.ItemIsEditable) item.feature = feature table.setItem(m, n, item) n += 1 m += 1 self.loadingWindow.setValue(int((float(m)/nbrow)*100)) QApplication.processEvents() else: table.setRowCount(0) table.setHorizontalHeaderLabels(headers) table.horizontalHeader().setSectionsMovable(True) table.types = types table.filter_op = [] table.filters = [] for i in range(0, len(headers)): table.filters.append('') table.filter_op.append(0) header = table.horizontalHeader() header.setContextMenuPolicy(Qt.CustomContextMenu) header.customContextMenuRequested.connect(partial(self.filterMenu, table)) table.setSortingEnabled(True) p1_vertical.addWidget(table) # Status bar to display informations (ie: area) tab.sb = QStatusBar() p1_vertical.addWidget(tab.sb) title = table.title # We reduce the title's length to 20 characters if len(title)>20: title = title[:20]+'...' # We add the number of elements to the tab's title. title += ' ('+str(len(features))+')' self.tabWidget.addTab(tab, title) # Add the tab to the conatiner self.tabWidget.setTabToolTip(self.tabWidget.indexOf(tab), table.title) # Display a tooltip with the layer's full name