def exportAsImage(self): filename = unicode(QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)'))) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = QRectF(0, 0, 1, 1) for item in self.scene.items(): totalRect = totalRect.united(item.sceneBoundingRect()) totalRect.adjust(-10, -10, 10, 10) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, totalRect, totalRect) painter.end() img.save(filename)
def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As PDF'), '', self.tr('PDF files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage("", self.tr("Successfully exported model as PDF to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True)
def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As PDF'), '', self.tr('PDF files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as PDF", level=Qgis.Success, duration=5) self.repaintModel(controls=True)
def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName( self, self.tr('Save Model As PDF'), '', self.tr('PDF files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as PDF", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True)
def exportAsImage(self): filename, filter = QFileDialog.getSaveFileName( self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = QRectF(0, 0, 1, 1) for item in list(self.scene.items()): totalRect = totalRect.united(item.sceneBoundingRect()) totalRect.adjust(-10, -10, 10, 10) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, totalRect, totalRect) painter.end() img.save(filename)
def exportAsImage(self): filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = QRectF(0, 0, 1, 1) for item in list(self.scene.items()): totalRect = totalRect.united(item.sceneBoundingRect()) totalRect.adjust(-10, -10, 10, 10) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, totalRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage("", "Model was correctly exported as image", level=QgsMessageBar.SUCCESS, duration=5)
def paint(self, painter, xxx, xxx2): self.setPos(self.toCanvasCoordinates(self.map_pos)) halfSize = self.size / 2.0 rect = QRectF(0 - halfSize, 0 - halfSize, self.size, self.size) painter.setRenderHint(QPainter.Antialiasing) if self.quality == 0: color = self.red elif self.quality == 1: color = self.green elif self.quality >= 2: color = self.blue else: color = self.red self.pointpen.setColor(Qt.gray) self.pointpen.setWidth(2) self.pointbrush.setColor(color) painter.setBrush(self.pointbrush) painter.setPen(self.pointpen) y = 0 - halfSize x = rect.width() / 2 - halfSize line = QLine(x, y, x, rect.height() - halfSize) y = rect.height() / 2 - halfSize x = 0 - halfSize line2 = QLine(x, y, rect.width() - halfSize, y) # Arrow p = QPolygonF() p.append(QPoint(0 - halfSize, 0)) p.append(QPoint(0, -self.size)) x = rect.width() - halfSize p.append(QPoint(x, 0)) p.append(QPoint(0, 0)) offsetp = QPolygonF() offsetp.append(QPoint(0 - halfSize, 0)) offsetp.append(QPoint(0, -self.size)) x = rect.width() - halfSize offsetp.append(QPoint(x, 0)) offsetp.append(QPoint(0, 0)) waypoint = self.gps.waypoint if waypoint: az = self.map_pos.azimuth(waypoint) painter.save() painter.rotate(az) self.pointbrush.setColor(Qt.red) painter.setBrush(self.pointbrush) path = QPainterPath() path.addPolygon(offsetp) painter.drawPath(path) painter.restore() painter.save() painter.rotate(self._heading) path = QPainterPath() path.addPolygon(p) painter.drawPath(path) painter.restore() painter.drawEllipse(rect) painter.drawLine(line) painter.drawLine(line2)
class PlotItem(LogItem): # emitted when the style is updated style_updated = pyqtSignal() def __init__(self, size=QSizeF(400, 200), render_type=POINT_RENDERER, x_orientation=ORIENTATION_LEFT_TO_RIGHT, y_orientation=ORIENTATION_UPWARD, allow_mouse_translation=False, allow_wheel_zoom=False, symbology=None, parent=None): """ Parameters ---------- size: QSize Size of the item render_type: Literal[POINT_RENDERER, LINE_RENDERER, POLYGON_RENDERER] Type of renderer x_orientation: Literal[ORIENTATION_LEFT_TO_RIGHT, ORIENTATION_RIGHT_TO_LEFT] y_orientation: Literal[ORIENTATION_UPWARD, ORIENTATION_DOWNWARD] allow_mouse_translation: bool Allow the user to translate the item with the mouse (??) allow_wheel_zoom: bool Allow the user to zoom with the mouse wheel symbology: QDomDocument QGIS symbology to use for the renderer parent: QObject """ LogItem.__init__(self, parent) self.__item_size = size self.__data_rect = None self.__data = None self.__delta = None self.__x_orientation = x_orientation self.__y_orientation = y_orientation # origin point of the graph translation, if any self.__translation_orig = None self.__render_type = render_type # type: Literal[POINT_RENDERER, LINE_RENDERER, POLYGON_RENDERER] self.__allow_mouse_translation = allow_mouse_translation self.__allow_wheel_zoom = allow_wheel_zoom self.__layer = None self.__default_renderers = [QgsFeatureRenderer.defaultRenderer(POINT_RENDERER), QgsFeatureRenderer.defaultRenderer(LINE_RENDERER), QgsFeatureRenderer.defaultRenderer(POLYGON_RENDERER)] symbol = self.__default_renderers[1].symbol() symbol.setWidth(1.0) symbol = self.__default_renderers[0].symbol() symbol.setSize(5.0) symbol = self.__default_renderers[2].symbol() symbol.symbolLayers()[0].setStrokeWidth(1.0) if not symbology: self.__renderer = self.__default_renderers[self.__render_type] else: self.__renderer = QgsFeatureRenderer.load(symbology.documentElement(), QgsReadWriteContext()) # index of the current point to label self.__old_point_to_label = None self.__point_to_label = None def boundingRect(self): return QRectF(0, 0, self.__item_size.width(), self.__item_size.height()) def height(self): return self.__item_size.height() def set_height(self, height): self.__item_size.setHeight(height) def width(self): return self.__item_size.width() def set_width(self, width): self.__item_size.setWidth(width) def set_data_window(self, window): """window: QRectF""" self.__data_rect = window def min_depth(self): if self.__data_rect is None: return None return self.__data_rect.x() def max_depth(self): if self.__data_rect is None: return None return (self.__data_rect.x() + self.__data_rect.width()) def set_min_depth(self, min_depth): if self.__data_rect is not None: self.__data_rect.setX(min_depth) def set_max_depth(self, max_depth): if self.__data_rect is not None: w = max_depth - self.__data_rect.x() self.__data_rect.setWidth(w) def layer(self): return self.__layer def set_layer(self, layer): self.__layer = layer def data_window(self): return self.__data_rect def set_data(self, x_values, y_values): self.__x_values = x_values self.__y_values = y_values if len(self.__x_values) != len(self.__y_values): raise ValueError("X and Y array has different length : " "{} != {}".format(len(self.__x_values), len(self.__y_values))) # Remove None values for i in reversed(range(len(self.__y_values))): if (self.__y_values[i] is None or self.__x_values[i] is None or math.isnan(self.__y_values[i]) or math.isnan(self.__x_values[i])): self.__y_values.pop(i) self.__x_values.pop(i) if not self.__x_values: return # Initialize data rect to display all data # with a 20% buffer around Y values min_x = min(self.__x_values) max_x = max(self.__x_values) min_y = min(self.__y_values) max_y = max(self.__y_values) if max_y == 0.0: max_y = 1.0 h = max_y - min_y min_y -= h * 0.1 max_y += h * 0.1 self.__data_rect = QRectF( min_x, min_y, max_x-min_x, max_y-min_y) def renderer(self): return self.__renderer def paint(self, painter, option, widget): self.draw_background(painter) if self.__data_rect is None: return # Look for the first and last X values that fit our rendering rect imin_x = bisect.bisect_left(self.__x_values, self.__data_rect.x()) imax_x = bisect.bisect_right(self.__x_values, self.__data_rect.right()) # For lines and polygons, retain also one value before the min and one after the max # so that lines do not appear truncated # Do this only if we have at least one point to render within out rect if imin_x > 0 and imin_x < len(self.__x_values) and self.__x_values[imin_x] >= self.__data_rect.x(): # FIXME add a test to avoid adding a point too "far away" ? imin_x -= 1 if imax_x < len(self.__x_values) - 1 and self.__x_values[imax_x] <= self.__data_rect.right(): # FIXME add a test to avoid adding a point too "far away" ? imax_x += 1 x_values_slice = np.array(self.__x_values[imin_x:imax_x]) y_values_slice = np.array(self.__y_values[imin_x:imax_x]) if len(x_values_slice) == 0: return # filter points that are not None (nan in numpy arrays) n_points = len(x_values_slice) if self.__x_orientation == ORIENTATION_LEFT_TO_RIGHT and self.__y_orientation == ORIENTATION_UPWARD: if self.__data_rect.width() > 0: rw = float(self.__item_size.width()) / self.__data_rect.width() else: rw = float(self.__item_size.width()) if self.__data_rect.height() > 0: rh = float(self.__item_size.height()) / self.__data_rect.height() else: rh = float(self.__item_size.height()) xx = (x_values_slice - self.__data_rect.x()) * rw yy = (y_values_slice - self.__data_rect.y()) * rh elif self.__x_orientation == ORIENTATION_DOWNWARD and self.__y_orientation == ORIENTATION_LEFT_TO_RIGHT: if self.__data_rect.width() > 0: rw = float(self.__item_size.height()) / self.__data_rect.width() else: rw = float(self.__item_size.height()) if self.__data_rect.height() > 0: rh = float(self.__item_size.width()) / self.__data_rect.height() else: rh = float(self.__item_size.width()) xx = (y_values_slice - self.__data_rect.y()) * rh yy = self.__item_size.height() - (x_values_slice - self.__data_rect.x()) * rw if self.__render_type == LINE_RENDERER: # WKB structure of a linestring # # 01 : endianness # 02 00 00 00 : WKB type (linestring) # nn nn nn nn : number of points (int32) # Then, for each point: # xx xx xx xx xx xx xx xx : X coordinate (float64) # yy yy yy yy yy yy yy yy : Y coordinate (float64) wkb = np.zeros(8*2*n_points+9, dtype='uint8') wkb[0] = 1 # wkb endianness wkb[1] = 2 # linestring size_view = np.ndarray(buffer=wkb, dtype='int32', offset=5, shape=(1,)) size_view[0] = n_points coords_view = np.ndarray(buffer=wkb, dtype='float64', offset=9, shape=(n_points,2)) coords_view[:,0] = xx[:] coords_view[:,1] = yy[:] elif self.__render_type == POINT_RENDERER: # WKB structure of a multipoint # # 01 : endianness # 04 00 00 00 : WKB type (multipoint) # nn nn nn nn : number of points (int32) # Then, for each point: # 01 : endianness # 01 00 00 00 : WKB type (point) # xx xx xx xx xx xx xx xx : X coordinate (float64) # yy yy yy yy yy yy yy yy : Y coordinate (float64) wkb = np.zeros((8*2+5)*n_points+9, dtype='uint8') wkb[0] = 1 # wkb endianness wkb[1] = 4 # multipoint size_view = np.ndarray(buffer=wkb, dtype='int32', offset=5, shape=(1,)) size_view[0] = n_points coords_view = np.ndarray(buffer=wkb, dtype='float64', offset=9+5, shape=(n_points,2), strides=(16+5,8)) coords_view[:,0] = xx[:] coords_view[:,1] = yy[:] # header of each point h_view = np.ndarray(buffer=wkb, dtype='uint8', offset=9, shape=(n_points,2), strides=(16+5,1)) h_view[:,0] = 1 # endianness h_view[:,1] = 1 # point elif self.__render_type == POLYGON_RENDERER: # WKB structure of a polygon # # 01 : endianness # 03 00 00 00 : WKB type (polygon) # 01 00 00 00 : Number of rings (always 1 here) # nn nn nn nn : number of points (int32) # Then, for each point: # xx xx xx xx xx xx xx xx : X coordinate (float64) # yy yy yy yy yy yy yy yy : Y coordinate (float64) # # We add two additional points to close the polygon wkb = np.zeros(8*2*(n_points+2)+9+4, dtype='uint8') wkb[0] = 1 # wkb endianness wkb[1] = 3 # polygon wkb[5] = 1 # number of rings size_view = np.ndarray(buffer=wkb, dtype='int32', offset=9, shape=(1,)) size_view[0] = n_points+2 coords_view = np.ndarray(buffer=wkb, dtype='float64', offset=9+4, shape=(n_points,2)) coords_view[:,0] = xx[:] coords_view[:,1] = yy[:] # two extra points extra_coords = np.ndarray(buffer=wkb, dtype='float64', offset=8*2*n_points+9+4, shape=(2,2)) if self.__x_orientation == ORIENTATION_LEFT_TO_RIGHT and self.__y_orientation == ORIENTATION_UPWARD: extra_coords[0,0] = coords_view[-1,0] extra_coords[0,1] = 0.0 extra_coords[1,0] = coords_view[0,0] extra_coords[1,1] = 0.0 elif self.__x_orientation == ORIENTATION_DOWNWARD and self.__y_orientation == ORIENTATION_LEFT_TO_RIGHT: extra_coords[0,0] = 0.0 extra_coords[0,1] = coords_view[-1,1] extra_coords[1,0] = 0.0 extra_coords[1,1] = coords_view[0,1] # build a geometry from the WKB # since numpy arrays have buffer protocol, sip is able to read it geom = QgsGeometry() geom.fromWkb(wkb.tobytes()) painter.setClipRect(0, 0, self.__item_size.width(), self.__item_size.height()) fields = QgsFields() #fields.append(QgsField("", QVariant.String)) feature = QgsFeature(fields, 1) feature.setGeometry(geom) context = qgis_render_context(painter, self.__item_size.width(), self.__item_size.height()) context.setExtent(QgsRectangle(0, 1, self.__item_size.width(), self.__item_size.height())) self.__renderer.startRender(context, fields) self.__renderer.renderFeature(feature, context) self.__renderer.stopRender(context) if self.__point_to_label is not None: i = self.__point_to_label x, y = self.__x_values[i], self.__y_values[i] if self.__x_orientation == ORIENTATION_LEFT_TO_RIGHT and self.__y_orientation == ORIENTATION_UPWARD: px = (x - self.__data_rect.x()) * rw py = self.__item_size.height() - (y - self.__data_rect.y()) * rh elif self.__x_orientation == ORIENTATION_DOWNWARD and self.__y_orientation == ORIENTATION_LEFT_TO_RIGHT: px = (y - self.__data_rect.y()) * rh py = (x - self.__data_rect.x()) * rw painter.drawLine(px-5, py, px+5, py) painter.drawLine(px, py-5, px, py+5) def mouseMoveEvent(self, event): if not self.__x_values: return if self.__x_orientation == ORIENTATION_LEFT_TO_RIGHT and self.__y_orientation == ORIENTATION_UPWARD: xx = (event.scenePos().x() - self.pos().x()) / self.width() * self.__data_rect.width() + self.__data_rect.x() elif self.__x_orientation == ORIENTATION_DOWNWARD and self.__y_orientation == ORIENTATION_LEFT_TO_RIGHT: xx = (event.scenePos().y() - self.pos().y()) / self.height() * self.__data_rect.width() + self.__data_rect.x() i = bisect.bisect_left(self.__x_values, xx) if i >= 0 and i < len(self.__x_values): # switch the attached point when we are between two points if i > 0 and (xx - self.__x_values[i-1]) < (self.__x_values[i] - xx): i -= 1 self.__point_to_label = i else: self.__point_to_label = None if self.__point_to_label != self.__old_point_to_label: self.update() if self.__point_to_label is not None: x, y = self.__x_values[i], self.__y_values[i] if self.__x_orientation == ORIENTATION_LEFT_TO_RIGHT and self.__y_orientation == ORIENTATION_UPWARD: dt = datetime.fromtimestamp(x, UTC()) txt = "Time: {} Value: {}".format(dt.strftime("%x %X"), y) elif self.__x_orientation == ORIENTATION_DOWNWARD and self.__y_orientation == ORIENTATION_LEFT_TO_RIGHT: txt = "Depth: {} Value: {}".format(x, y) self.tooltipRequested.emit(txt) self.__old_point_to_label = self.__point_to_label def edit_style(self): from qgis.gui import QgsSingleSymbolRendererWidget from qgis.core import QgsStyle style = QgsStyle() sw = QStackedWidget() sw.addWidget for i in range(3): if self.__renderer and i == self.__render_type: w = QgsSingleSymbolRendererWidget(self.__layer, style, self.__renderer) else: w = QgsSingleSymbolRendererWidget(self.__layer, style, self.__default_renderers[i]) sw.addWidget(w) combo = QComboBox() combo.addItem("Points") combo.addItem("Line") combo.addItem("Polygon") combo.currentIndexChanged[int].connect(sw.setCurrentIndex) combo.setCurrentIndex(self.__render_type) dlg = QDialog() vbox = QVBoxLayout() btn = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) btn.accepted.connect(dlg.accept) btn.rejected.connect(dlg.reject) vbox.addWidget(combo) vbox.addWidget(sw) vbox.addWidget(btn) dlg.setLayout(vbox) dlg.resize(800, 600) r = dlg.exec_() if r == QDialog.Accepted: self.__render_type = combo.currentIndex() self.__renderer = sw.currentWidget().renderer().clone() self.update() self.style_updated.emit() def qgis_style(self): """Returns the current style, as a QDomDocument""" from PyQt5.QtXml import QDomDocument from qgis.core import QgsReadWriteContext doc = QDomDocument() elt = self.__renderer.save(doc, QgsReadWriteContext()) doc.appendChild(elt) return (doc, self.__render_type)
def paintEvent(self, event): # based on # http://qt.gitorious.org/qt/qt/blobs/master/src/gui/widgets/qslider.cpp painter = QPainter(self) style = self.style() opt = QStyleOptionSlider() self.initStyleOption(opt) groove_rect = style.subControlRect(style.CC_Slider, opt, QStyle.SC_SliderGroove, self) handle_rect = style.subControlRect(style.CC_Slider, opt, QStyle.SC_SliderHandle, self) slider_space = style.pixelMetric(style.PM_SliderSpaceAvailable, opt) range_x1 = style.sliderPositionFromValue(self.minimum(), self.maximum(), self._low, slider_space) range_x2 = style.sliderPositionFromValue(self.minimum(), self.maximum(), self._high, slider_space) range_height = 4 groove_rect = QRectF( groove_rect.x(), handle_rect.center().y() - (range_height / 2), groove_rect.width(), range_height, ) range_rect = QRectF( groove_rect.x() + (handle_rect.width() / 2) + range_x1, handle_rect.center().y() - (range_height / 2), range_x2 - range_x1, range_height, ) if style.metaObject().className() != "QMacStyle": # Paint groove for Fusion and Windows styles cur_brush = painter.brush() cur_pen = painter.pen() painter.setBrush(QBrush(QColor(169, 169, 169))) painter.setPen(Qt.NoPen) # painter.drawRect(groove_rect) painter.drawRoundedRect(groove_rect, groove_rect.height() / 2, groove_rect.height() / 2) painter.setBrush(cur_brush) painter.setPen(cur_pen) cur_brush = painter.brush() cur_pen = painter.pen() # painter.setBrush(QBrush(QColor(18, 141, 148))) # painter.setPen(Qt.NoPen) painter.drawRect(range_rect) painter.setBrush(cur_brush) painter.setPen(cur_pen) for i, value in enumerate([self._low, self._high]): opt = QStyleOptionSlider() self.initStyleOption(opt) # Only draw the groove for the first slider so it doesn't get drawn # on top of the existing ones every time # if i == 0: # opt.subControls = QStyle.SC_SliderGroove | \ # QStyle.SC_SliderHandle # else: opt.subControls = QStyle.SC_SliderHandle if self.tickPosition() != self.NoTicks: opt.subControls |= QStyle.SC_SliderTickmarks if self.isSliderDown(): opt.state |= QStyle.State_Sunken else: opt.state |= QStyle.State_Active if self.pressed_control: opt.activeSubControls = self.pressed_control else: opt.activeSubControls = self.hover_control opt.sliderPosition = value opt.sliderValue = value style.drawComplexControl(QStyle.CC_Slider, opt, painter, self)