class PlotWidget(Widgets.WidgetBase): def __init__(self, plot, width=500, height=500): super(PlotWidget, self).__init__() self.widget = FigureCanvas(plot.get_figure()) self.widget._resizeEvent = self.widget.resizeEvent self.widget.resizeEvent = self.resize_event self.plot = plot self.logger = plot.logger def set_plot(self, plot): self.plot = plot self.logger = plot.logger self.logger.debug("set_plot called") def configure_window(self, wd, ht): fig = self.plot.get_figure() fig.set_size_inches(float(wd) / fig.dpi, float(ht) / fig.dpi) def resize_event(self, event): rect = self.widget.geometry() x1, y1, x2, y2 = rect.getCoords() width = x2 - x1 height = y2 - y1 if width > 0 and height > 0: self.configure_window(width, height) self.widget._resizeEvent(event)
class PlotWidget(Widgets.WidgetBase): def __init__(self, plot, width=500, height=500): super(PlotWidget, self).__init__() self.widget = FigureCanvas(plot.get_figure()) self.widget._resizeEvent = self.widget.resizeEvent self.widget.resizeEvent = self.resize_event self.plot = plot self.logger = plot.logger def set_plot(self, plot): self.plot = plot self.logger = plot.logger self.logger.debug("set_plot called") def configure_window(self, wd, ht): fig = self.plot.get_figure() fig.set_size_inches(float(wd) / fig.dpi, float(ht) / fig.dpi) def resize_event(self, event): rect = self.widget.geometry() x1, y1, x2, y2 = rect.getCoords() width = x2 - x1 height = y2 - y1 if width > 0 and height > 0: self.configure_window(width, height) self.widget._resizeEvent(event)
def __init__(self, fig): super().__init__() _widget = QtWidgets.QWidget() self.setCentralWidget(_widget) layout = QtWidgets.QVBoxLayout(_widget) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) canvas = FigureCanvas(fig) self.addToolBar(NavigationToolbar(canvas, self)) scroll = QtWidgets.QScrollArea() scroll.setWidget(canvas) self._fig_dpi = canvas.geometry().width() / \ fig.get_size_inches()[0] layout.addWidget(scroll)
class MatplotGraph(QMainWindow): def __init__(self, parent=None): super(MatplotGraph, self).__init__(parent) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle("Matplot Graph") screen_x_pos, screen_y_pos, screen_width, screen_height = QDesktopWidget( ).geometry().getRect() self.window_width = screen_width * 0.5 self.window_height = screen_height * 0.5 self.resize(self.window_width, self.window_height) self.move((screen_width - self.window_width) / 2, (screen_height - self.window_height) / 2) # Note that this winodw should be modaless #self.setWindowModality(Qt.ApplicationModaless) # Destructed when closed self.setAttribute(Qt.WA_DeleteOnClose) # Menu bar file_menu = self.menuBar().addMenu("File(&F)") resize_action = QAction("resize image", self) resize_action.triggered.connect(self.resizeImage) file_menu.addAction(resize_action) export_action = QAction("export image", self) export_action.triggered.connect(self.exportImage) file_menu.addAction(export_action) # Create the matplotlib widget self.plot_widget = FigureCanvas(Figure()) self.setCentralWidget(self.plot_widget) self.plot_widget.setParent(self) # make the figure square cen_x_pos, cen_y_pos, cen_x_len, cen_y_len = self.centralWidget( ).geometry().getRect() ratio_tmp = (self.window_width - self.window_height) / (cen_x_len - cen_y_len) self.window_width -= (cen_x_len - cen_y_len) / 2 * ratio_tmp self.window_height += (cen_x_len - cen_y_len) / 2 * ratio_tmp self.resize(self.window_width, self.window_height) self.move((screen_width - self.window_width) / 2, (screen_height - self.window_height) / 2) #FigureCanvas.setSizePolicy(self.plot_widget, QSizePolicy.Expanding, QSizePolicy.Expanding) #FigureCanvas.updateGeometry(self.plot_widget) # Status Bar self.statusBar() def printMsg(self, msg_str): self.statusBar().showMessage(msg_str) def getPlotWidget(self): return self.plot_widget def show(self): self.plot_widget.draw() super(MatplotGraph, self).show() def resizeImage(self): main_x_pos, main_y_pox, main_x_len, main_y_len = self.geometry( ).getRect() fig_x_pos, fig_y_pos, fig_x_len, fig_y_len = self.plot_widget.geometry( ).getRect() get_size_dialog = ImageSizeDialog(self, fig_x_len, fig_y_len) if (get_size_dialog.exec()): fig_x, fig_y = get_size_dialog.getXYText() try: fig_x = int(fig_x) fig_y = int(fig_y) except ValueError: self.printMsg("Should input integer.") else: if (fig_x != 0 and fig_y != 0): # set size of figure self.setGeometry(main_x_pos, main_y_pox, main_x_len + (fig_x - fig_x_len), main_y_len + (fig_y - fig_y_len)) self.plot_widget.setGeometry(fig_x_pos, fig_y_pos, fig_x, fig_y) def exportImage(self): file_path, file_type = QFileDialog.getSaveFileName( self, "Output Figure", "./", "PNG Image Files (*.png);;All Files (*)") if file_path != '': self.plot_widget.figure.savefig(file_path)
class MapWidget(QtWidgets.QWidget): """ Widget with the background map This widget works based on a WMTS, which contains background tiles within and around the axes limits. Each action can update the bounding box, and thus the tile set. """ element_dct = { 'harborarea': { 'kwargs': {'facecolor': 'C0', 'alpha': 0.4, 'edgecolor': 'C0'}, 'handle': 'patch', 'label': 'haventerrein' }, 'breakwaters': { 'kwargs': {'colors': 'C1', 'linewidths': 2, 'alpha': 1.0}, 'handle': 'element', 'label': 'havendam(men)' }, 'flooddefence': { 'kwargs': {'colors': 'C3', 'linewidths': 2, 'alpha': 1.0}, 'handle': 'element', 'label': 'waterkering' }, 'support_locations': { 'kwargs': {'color': '0.1', 'marker': 'x', 'ms': 6, 'mew': 2, 'alpha': 1.0}, 'handle': 'element', 'label': 'HRD-locaties' }, 'result_locations': { 'kwargs': {'color': '0.1', 'marker': 'o', 'ms': 2, 'alpha': 1.0}, 'handle': 'element', 'label': 'uitvoerlocaties' }, 'entrance': { 'kwargs': {'lw': 1.5, 'color': '0.1', 'dashes': (2,2), 'alpha': 1.0}, 'handle': 'element', 'label': 'haveningang' }, 'inner': { 'kwargs': {'color': 'grey', 'alpha': 0.3}, 'handle': 'patch', 'label': 'binnengaats gebied' }, 'support_location': { 'kwargs': {'color': '0.1', 'marker': 'o', 'ms': 10, 'alpha': 1.0, 'mfc': 'none'}, 'handle': 'element', 'label': 'steunpuntlocatie' } } def __init__(self, mainmodel, maintab): """ Constructor of the tab """ # Create child class QtWidgets.QWidget.__init__(self) self.landboundary = io.geometry.read_landboundary() # Dictionaries for legend and toggling items self.plot_elements = {} self.bbox = (0., 300000., 280000., 620000.) self.labels = {} self.legend = InteractiveLegend(self, self.plot_elements) self.mainmodel = mainmodel self.main = maintab self.schematisation = mainmodel.schematisation self.init_widget() self.load_project() self.update_tiles_thread = threads.UpdateTilesThread(self) def init_widget(self): # Aangepast Svasek 31/10/2018 - Ander gebruik van figure, waardoor er in Spyder geen extra figuur opent # Create figure self.figure = Figure(figsize=(self.geometry().width() / 100., self.geometry().height() / 100.)) # Add canvas self.canvas = FigureCanvasQTAgg(self.figure) self.canvas.mpl_connect('pick_event', self.legend._onpick) # this is the Navigation widget # it takes the Canvas widget and a parent self.toolbar = widgets.CustomNavigationToolbar(canvas=self.canvas, widget=self, update_func=self._update_background) # Add checkbox for showing labels hbox = QtWidgets.QHBoxLayout() self.label_checkbox = QtWidgets.QCheckBox('Laat labels zien') self.label_checkbox.setChecked(False) self.label_checkbox.stateChanged.connect(self.show_labels) hbox.addWidget(self.label_checkbox, 0, QtCore.Qt.AlignLeft) # Create WMTS selection label = QtWidgets.QLabel('Achtergrondkaartlaag:') hbox.addWidget(label, 0, QtCore.Qt.AlignRight) self.wmts_combobox = QtWidgets.QComboBox() self.comboboxitems = [ ('Geen achtergrond', '', ''), ('OpenTopo Achtergrondkaart', 'layer.png', 'opentopoachtergrondkaart'), ('Luchtfoto PDOK', 'layer.png', '2017_ortho25'), ('AHN2 5m DTM', 'layer.png', 'ahn2_05m_ruw'), ('BRT achtergrondkaart Grijs', 'layer.png', 'brtachtergrondkaartgrijs') ] # Get path to icon data for text, iconname, userdata in self.comboboxitems: # Get path to icon iconpath = os.path.join(self.mainmodel.datadir,'icons',iconname) # Add item to combobox with icon self.wmts_combobox.addItem(QtGui.QIcon(iconpath), text, userdata) self.wmts_combobox.setCurrentIndex(0) hbox.addWidget(self.wmts_combobox) self.wmts_combobox.currentIndexChanged.connect(self._set_WMTS_layer) # create an axis self.ax = self.figure.add_axes([0, 0, 1, 1]) self.bbox = self._get_filled_bbox(self.bbox) self.ax.set_xlim(self.bbox[0], self.bbox[2]) self.ax.set_ylim(self.bbox[1], self.bbox[3]) # Add WMTS self.WMTS = None self._set_WMTS_layer(0) self.landboundary, = self.ax.plot(*self.landboundary, color='0.8', zorder=-20, lw=0.75) if self.WMTSlayer == '': self.landboundary.set_visible(True) self.ax.axis('off') layout = QtWidgets.QVBoxLayout() layout.addWidget(self.toolbar) layout.addWidget(self.canvas) layout.addLayout(hbox) self.setLayout(layout) def load_project(self): """ Set project elements visible """ if not self.schematisation.result_locations.empty: self.set_visible('result_locations') if not self.schematisation.harborarea.empty: self.set_visible('harborarea') if not self.schematisation.breakwaters.empty: self.set_visible('breakwaters') if len(self.schematisation.breakwaters) == 2 or hasattr(self.schematisation, 'entrance_coordinate'): self.set_visible('inner') self.set_visible('entrance') if not self.schematisation.flooddefence.empty: self.set_visible('flooddefence') if not self.schematisation.support_locations.empty: self.set_visible('support_locations') if not pd.isnull(self.schematisation.support_location).all(): self.set_visible('support_location') def show_labels(self, state=QtCore.Qt.Checked): """ Update labels on canvas Parameters ---------- state : QtCore.Qt.[state] State of the checkbox """ if state == QtCore.Qt.Checked: # Both result and support locations for locations, column in zip(['result_locations', 'support_locations'], ['Naam', 'Name']): # If not in visible elements, continue if locations not in self.plot_elements.keys(): continue # Plot each label for row in getattr(self.schematisation, locations).itertuples(): labelkey = locations[0] + getattr(row, column) if labelkey not in self.labels.keys(): self.labels[labelkey] = self.ax.text(*row.geometry.coords[0], s=getattr(row, column), ha='left', va='bottom') else: # Remove all labels for key, label in self.labels.items(): label.remove() self.labels.clear() self.canvas.draw() def _set_WMTS_layer(self, idx): """ Change the WMTS layer """ try: activated_idx = idx if idx == -1: return None item = self.comboboxitems[idx] if not item: return None matched_idx = self.wmts_combobox.findData(item[2]) assert activated_idx == matched_idx # Get layer name from combobox self.WMTSlayer = item[2] # Remove the present WMTS if self.WMTS is not None: self.WMTS.clean_all() self.WMTS = None self.landboundary.set_visible(True) # Skip if no background if self.WMTSlayer != '': self.landboundary.set_visible(False) # Add Web Mapping Tile Service self.WMTS = WMTS( 'http://geodata.nationaalgeoregister.nl/tiles/service', 'EPSG:28992', self.WMTSlayer ) self._update_background(None) except Exception as e: print(e) def _get_filled_bbox(self, limits): # Get canvas size geo = self.canvas.geometry() canvas_width = (geo.width()) / 100. canvas_height = (geo.height()) / 100. canvas_ratio = max(1, canvas_width) / max(1, canvas_height) # Get axis size left, lower, right, upper = limits ax_width = right - left ax_height = upper - lower ax_ratio = (ax_width / ax_height) # Als de assen te hoog zijn, corrigeer de breedte if canvas_ratio > ax_ratio: ax_width = ax_width / (ax_ratio / canvas_ratio) center = 0.5 * (left + right) left = center - 0.5 * ax_width right = center + 0.5 * ax_width # Als de assen te breed zijn elif canvas_ratio < ax_ratio: ax_height = ax_height / (canvas_ratio / ax_ratio) center = 0.5 * (upper + lower) lower = center - 0.5 * ax_height upper = center + 0.5 * ax_height # Return bbox return (left, lower, right, upper) def _update_background(self, event, limits=None): """ Update the bounding box """ # Get the limits of the frame for the bouding box if not limits: limits = (self.ax.get_xlim()[0], self.ax.get_ylim()[0], self.ax.get_xlim()[1], self.ax.get_ylim()[1]) self.bbox = self._get_filled_bbox(limits) # Reset the axis limits self.ax.set_xlim(self.bbox[0], self.bbox[2]) self.ax.set_ylim(self.bbox[1], self.bbox[3]) # Scale markers in plotter self.main.plotter.set_location_values() self.canvas.draw_idle() # Skip if no layer if self.WMTSlayer == '': return None # Check if the thread is still running from an older background update if self.update_tiles_thread.isRunning(): self.update_tiles_thread.quit() self.update_tiles_thread.wait() logger.debug('Tile update thread interrupted.') # Start the thread self.update_tiles_thread.start() def remove_plot_element(self, element): """ Remove plotted element from view Parameters ---------- element : str element that is removed """ # Remove element if present if element in self.plot_elements.keys(): self.plot_elements[element].remove() del self.plot_elements[element] self.legend.remove(element) def set_visible(self, element, ax=None): """ Plot a given element Parameters ---------- element : str element to be plotted """ if ax is None: ax = self.ax # Remove the element if it is already plotted self.remove_plot_element(element) # Get plotting properties plot_props = self.element_dct[element] kwargs = plot_props['kwargs'] # If element is geodataframe geometry = getattr(self.schematisation, element) if isinstance(geometry, (gpd.GeoDataFrame, gpd.GeoSeries)): if geometry.empty: return None self.plot_elements[element] = self._plot_gdf(gdf=geometry, kwargs=kwargs, ax=ax) elif isinstance(geometry, LineString): self.plot_elements[element], = ax.plot(*np.vstack(geometry.coords[:]).T, **kwargs) elif isinstance(geometry, (Polygon, MultiPolygon)): self.plot_elements[element] = ax.add_collection(PatchCollection([PolygonPatch(geometry)], **kwargs)) else: raise TypeError('Geometry type not recognized.') # Add legend handles if plot_props['handle'] == 'patch': self.legend.add_item(element, handle=PolygonPatch(Polygon([(0,0), (0,1), (1,1), (1,0)]), **kwargs), label=plot_props['label']) else: self.legend.add_item(element, handle=self.plot_elements[element], label=plot_props['label']) # Update legend self.legend._update_legend() # Update labels if self.label_checkbox.isChecked() and element in ['support_locations', 'result_locations']: self.show_labels() self.canvas.draw_idle() def _plot_gdf(self, gdf, kwargs={}, ax=None): """ Plot geodataframe to axis This function will plot a default style for all geometry types. Paramaters ---------- gdf : geopandas.GeoDataFrame geodataframe with data to plot kwargs : dictionary dictionary with keyword arguments for plotting ax : matplotlib.pyplot.axes axes to plot to """ if ax is None: ax = self.ax # get geometries if isinstance(gdf, gpd.GeoSeries): geometries = [gdf['geometry']] else: geometries = gdf['geometry'].values.tolist() # for line and pts if isinstance(geometries[0], LineString): # Create collection segments = [np.vstack(geo.coords[:]) for geo in geometries] collection = ax.add_collection(LineCollection(segments, **kwargs)) if isinstance(geometries[0], Polygon): # Create collection polygons = [PolygonPatch(geo) for geo in geometries] collection = ax.add_collection(PatchCollection(polygons, **kwargs)) if isinstance(geometries[0], Point): kwargs['linestyle'] = '' crds = np.vstack([pt.coords[0] for pt in geometries]) collection, = ax.plot(*crds.T, **kwargs) return collection