def qimage_from_array(arr: np.ndarray) -> QImage: """ Create and return an QImage from a (N, M, C) uint8 array where C is 3 for RGB and 4 for RGBA channels. Parameters ---------- arr: (N, M C) uint8 array Returns ------- image: QImage An QImage with size (M, N) in ARGB32 format format depending on `C`. """ h, w, c = arr.shape if c == 4: format = QImage.Format_ARGB32 elif c == 3: format = QImage.Format_RGB32 else: raise ValueError(f"Wrong number of channels (need 3 or 4, got {c}") channels = arr.transpose((2, 0, 1)) img = QImage(w, h, QImage.Format_ARGB32) img.fill(Qt.white) if img.size().isEmpty(): return img buffer = img.bits().asarray(w * h * 4) view = np.frombuffer(buffer, np.uint32).reshape((h, w)) if format == QImage.Format_ARGB32: view[:, :] = qrgba(*channels) elif format == QImage.Format_RGB32: view[:, :] = qrgb(*channels) return img
def test(self): img = QImage(100, 100, QImage.Format_ARGB32) img.fill(Qt.green) p = QPainter(img) pix = QPixmap(10, 10) pix.fill(Qt.red) frgmts = [ QPainter.PixmapFragment.create( QPointF(25, 25), QRectF(0, 0, 10, 10), 5., 5., ), QPainter.PixmapFragment.create( QPointF(75, 75), QRectF(0, 0, 10, 10), 5., 5., ) ] p.drawPixmapFragments(frgmts, pix) p.end() self.assertEqual(QColor(img.pixel(10, 10)), QColor(Qt.red)) self.assertEqual(QColor(img.pixel(80, 80)), QColor(Qt.red)) self.assertEqual(QColor(img.pixel(90, 10)), QColor(Qt.green)) self.assertEqual(QColor(img.pixel(10, 90)), QColor(Qt.green))
def render_as_png(self, filename): img = QImage(50, 50, QImage.Format_ARGB32) img.fill(Qt.transparent) painter = QPainter(img) painter.setRenderHint(QPainter.Antialiasing, 1) self.scene.render(painter, QRectF(0, 0, 50, 50), QRectF(-25, -25, 50, 50)) painter.end() img.save(filename)
def qmacstyle_accent_color(style: QStyle): option = QStyleOptionButton() option.state |= (QStyle.State_Active | QStyle.State_Enabled | QStyle.State_Raised) option.features |= QStyleOptionButton.DefaultButton option.text = "" size = style.sizeFromContents(QStyle.CT_PushButton, option, QSize(20, 10), None) option.rect = QRect(0, 0, size.width(), size.height()) img = QImage(size.width(), size.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.transparent) painter = QPainter(img) try: style.drawControl(QStyle.CE_PushButton, option, painter, None) finally: painter.end() color = img.pixelColor(size.width() // 2, size.height() // 2) return color
def redraw_markers_overlay_image(self, *args, new_image=False): if not args and not self._drawing_args or self.data is None: return if args: self._drawing_args = args north, east, south, west, width, height, zoom, origin, map_pane_pos = self._drawing_args lat, lon = self._latlon_data.T visible = ((lat <= north) & (lat >= south) & (lon <= east) & (lon >= west)).nonzero()[0] in_subset = (np.in1d(self.data.ids, self._subset_ids) if self._subset_ids.size else np.tile(True, len(lon))) is_js_path = self.is_js_path = len(visible) < self.N_POINTS_PER_ITER self._update_legend(is_js_path) np.random.shuffle(visible) # Sort points in subset to be painted last visible = visible[np.lexsort((in_subset[visible],))] if is_js_path: self.evalJS('clear_markers_overlay_image()') self._update_js_markers(visible, in_subset[visible]) self._owwidget.disable_some_controls(False) return self.evalJS('clear_markers_js();') self._owwidget.disable_some_controls(True) selected = (self._selected_indices if self._selected_indices is not None else np.zeros(len(lat), dtype=bool)) cur = 0 im = QImage(self._overlay_image_path) if im.isNull() or self._prev_origin != origin or new_image: im = QImage(width, height, QImage.Format_ARGB32) im.fill(Qt.transparent) else: dx, dy = self._prev_map_pane_pos - map_pane_pos im = im.copy(dx, dy, width, height) self._prev_map_pane_pos = np.array(map_pane_pos) self._prev_origin = origin painter = QPainter(im) painter.setRenderHint(QPainter.Antialiasing, True) self.evalJS('clear_markers_overlay_image(); markersImageLayer.setBounds(map.getBounds());0') self._image_token = image_token = np.random.random() n_iters = np.ceil(len(visible) / self.N_POINTS_PER_ITER) def add_points(): nonlocal cur, image_token if image_token != self._image_token: return batch = visible[cur:cur + self.N_POINTS_PER_ITER] batch_lat = lat[batch] batch_lon = lon[batch] x, y = self.Projection.latlon_to_easting_northing(batch_lat, batch_lon) x, y = self.Projection.easting_northing_to_pixel(x, y, zoom, origin, map_pane_pos) if self._jittering: dx, dy = self._jittering_offsets[batch].T x, y = x + dx, y + dy colors = (self._colorgen.getRGB(self._scaled_color_values[batch]).tolist() if self._color_attr else repeat((0xff, 0, 0))) sizes = self._size_coef * \ (self._sizes[batch] if self._size_attr else np.tile(10, len(batch))) opacity_subset, opacity_rest = self._opacity, int(.8 * self._opacity) for x, y, is_selected, size, color, _in_subset in \ zip(x, y, selected[batch], sizes, colors, in_subset[batch]): pensize2, selpensize2 = (.35, 1.5) if size >= 5 else (.15, .7) pensize2 *= self._size_coef selpensize2 *= self._size_coef size2 = size / 2 if is_selected: painter.setPen(QPen(QBrush(Qt.green), 2 * selpensize2)) painter.drawEllipse(x - size2 - selpensize2, y - size2 - selpensize2, size + selpensize2, size + selpensize2) color = QColor(*color) if _in_subset: color.setAlpha(opacity_subset) painter.setBrush(QBrush(color)) painter.setPen(QPen(QBrush(color.darker(180)), 2 * pensize2)) else: color.setAlpha(opacity_rest) painter.setBrush(Qt.NoBrush) painter.setPen(QPen(QBrush(color.lighter(120)), 2 * pensize2)) painter.drawEllipse(x - size2 - pensize2, y - size2 - pensize2, size + pensize2, size + pensize2) im.save(self._overlay_image_path, 'PNG') self.evalJS('markersImageLayer.setUrl("{}#{}"); 0;' .format(self.toFileURL(self._overlay_image_path), np.random.random())) cur += self.N_POINTS_PER_ITER if cur < len(visible): QTimer.singleShot(10, add_points) self._owwidget.progressBarAdvance(100 / n_iters, None) else: self._owwidget.progressBarFinished(None) self._owwidget.progressBarFinished(None) self._owwidget.progressBarInit(None) QTimer.singleShot(10, add_points)
def redraw_markers_overlay_image(self, *args, new_image=False): if not args and not self._drawing_args or self.data is None: return if args: self._drawing_args = args north, east, south, west, width, height, zoom, origin, map_pane_pos = self._drawing_args lat, lon = self._latlon_data.T visible = ((lat <= north) & (lat >= south) & (lon <= east) & (lon >= west)).nonzero()[0] in_subset = (np.in1d(self.data.ids, self._subset_ids) if self._subset_ids.size else np.tile(True, len(lon))) is_js_path = self.is_js_path = len(visible) < self.N_POINTS_PER_ITER self._update_legend(is_js_path) np.random.shuffle(visible) # Sort points in subset to be painted last visible = visible[np.lexsort((in_subset[visible],))] if is_js_path: self.evalJS('clear_markers_overlay_image()') self._update_js_markers(visible, in_subset[visible]) self._owwidget.disable_some_controls(False) return self.evalJS('clear_markers_js();') self._owwidget.disable_some_controls(True) selected = (self._selected_indices if self._selected_indices is not None else np.zeros(len(lat), dtype=bool)) cur = 0 im = QImage(self._overlay_image_path) if im.isNull() or self._prev_origin != origin or new_image: im = QImage(width, height, QImage.Format_ARGB32) im.fill(Qt.transparent) else: dx, dy = self._prev_map_pane_pos - map_pane_pos im = im.copy(dx, dy, width, height) self._prev_map_pane_pos = np.array(map_pane_pos) self._prev_origin = origin painter = QPainter(im) painter.setRenderHint(QPainter.Antialiasing, True) self.evalJS('clear_markers_overlay_image(); markersImageLayer.setBounds(map.getBounds());0') self._image_token = image_token = np.random.random() n_iters = np.ceil(len(visible) / self.N_POINTS_PER_ITER) def add_points(): nonlocal cur, image_token if image_token != self._image_token: return batch = visible[cur:cur + self.N_POINTS_PER_ITER] batch_lat = lat[batch] batch_lon = lon[batch] x, y = self.Projection.latlon_to_easting_northing(batch_lat, batch_lon) x, y = self.Projection.easting_northing_to_pixel(x, y, zoom, origin, map_pane_pos) if self._jittering: dx, dy = self._jittering_offsets[batch].T x, y = x + dx, y + dy colors = (self._colorgen.getRGB(self._scaled_color_values[batch]).tolist() if self._color_attr else repeat((0xff, 0, 0))) sizes = self._size_coef * \ (self._sizes[batch] if self._size_attr else np.tile(10, len(batch))) opacity_subset, opacity_rest = self._opacity, int(.8 * self._opacity) for x, y, is_selected, size, color, _in_subset in \ zip(x, y, selected[batch], sizes, colors, in_subset[batch]): pensize2, selpensize2 = (.35, 1.5) if size >= 5 else (.15, .7) pensize2 *= self._size_coef selpensize2 *= self._size_coef size2 = size / 2 if is_selected: painter.setPen(QPen(QBrush(Qt.green), 2 * selpensize2)) painter.drawEllipse(x - size2 - selpensize2, y - size2 - selpensize2, size + selpensize2, size + selpensize2) color = QColor(*color) if _in_subset: color.setAlpha(opacity_subset) painter.setBrush(QBrush(color)) painter.setPen(QPen(QBrush(color.darker(180)), 2 * pensize2)) else: color.setAlpha(opacity_rest) painter.setBrush(Qt.NoBrush) painter.setPen(QPen(QBrush(color.lighter(120)), 2 * pensize2)) painter.drawEllipse(x - size2 - pensize2, y - size2 - pensize2, size + pensize2, size + pensize2) im.save(self._overlay_image_path, 'PNG') self.evalJS('markersImageLayer.setUrl("{}#{}"); 0;' .format(self.toFileURL(self._overlay_image_path), np.random.random())) cur += self.N_POINTS_PER_ITER if cur < len(visible): QTimer.singleShot(10, add_points) self._owwidget.progressBarAdvance(100 / n_iters, None) else: self._owwidget.progressBarFinished(None) self._image_token = None self._owwidget.progressBarFinished(None) self._owwidget.progressBarInit(None) QTimer.singleShot(10, add_points)
def redraw_markers_overlay_image(self, *args, new_image=False): if (not args and not self._drawing_args or self.lat_attr is None or self.lon_attr is None): return if args: self._drawing_args = args north, east, south, west, width, height, zoom, origin, map_pane_pos = self._drawing_args lat = self.data.get_column_view(self.lat_attr)[0] lon = self.data.get_column_view(self.lon_attr)[0] visible = ((lat <= north) & (lat >= south) & (lon <= east) & (lon >= west)).nonzero()[0] is_js_path = len(visible) <= 500 self.evalJS(''' window.legend_colors = %s; window.legend_shapes = %s; window.legend_sizes = %s; legendControl.remove(); legendControl.addTo(map); ''' % (self._legend_colors, self._legend_shapes if is_js_path else [], self._legend_sizes)) if is_js_path: self.evalJS('clear_markers_overlay_image()') self._update_js_markers(visible) self._owwidget.disable_some_controls(False) return self.evalJS('clear_markers_js();') self._owwidget.disable_some_controls(True) np.random.shuffle(visible) selected = (self._selected_indices if self._selected_indices is not None else np.zeros(len(lat), dtype=bool)) N_POINTS_PER_ITER = 1000 cur = 0 im = QImage(self._overlay_image_path) if im.isNull() or self._prev_origin != origin or new_image: im = QImage(width, height, QImage.Format_ARGB32) im.fill(Qt.transparent) else: dx, dy = self._prev_map_pane_pos - map_pane_pos im = im.copy(dx, dy, width, height) self._prev_map_pane_pos = np.array(map_pane_pos) self._prev_origin = origin painter = QPainter(im) painter.setRenderHint(QPainter.Antialiasing, True) self.evalJS( 'clear_markers_overlay_image(); markersImageLayer.setBounds(map.getBounds());0' ) self._image_token = image_token = np.random.random() n_iters = np.ceil(len(visible) / N_POINTS_PER_ITER) def add_points(): nonlocal cur, image_token if image_token != self._image_token: return batch = visible[cur:cur + N_POINTS_PER_ITER] batch_lat = lat[batch] batch_lon = lon[batch] batch_selected = selected[batch] x, y = self.Projection.latlon_to_easting_northing( batch_lat, batch_lon) x, y = self.Projection.easting_northing_to_pixel( x, y, zoom, origin, map_pane_pos) if self._jittering: x += (np.random.random(len(x)) - .5) * (self._jittering * width) y += (np.random.random(len(x)) - .5) * (self._jittering * height) colors = (self._colorgen.getRGB( self._scaled_color_values[batch]).tolist() if self._color_attr else repeat((0xff, 0, 0))) sizes = self._sizes[batch] if self._size_attr else repeat(10) zipped = zip(x, y, batch_selected, sizes, colors) sortkey, penkey, sizekey, brushkey = itemgetter( 2, 3, 4), itemgetter(2), itemgetter(3), itemgetter(4) for is_selected, points in groupby(sorted(zipped, key=sortkey), key=penkey): for size, points in groupby(points, key=sizekey): pensize, pencolor = ((3, Qt.green) if is_selected else (.7, QColor(0, 0, 0, self._opacity))) size *= self._size_coef if size < 5: pensize /= 3 size += pensize size2 = size / 2 painter.setPen(Qt.NoPen if size < 5 and not is_selected else QPen(QBrush(pencolor), pensize)) for color, points in groupby(points, key=brushkey): color = tuple(color) + (self._opacity, ) painter.setBrush(QBrush(QColor(*color))) for x, y, *_ in points: painter.drawEllipse(x - size2, y - size2, size, size) im.save(self._overlay_image_path, 'PNG') self.evalJS('markersImageLayer.setUrl("{}#{}"); 0;'.format( self._overlay_image_path, np.random.random())) cur += N_POINTS_PER_ITER if cur < len(visible): QTimer.singleShot(10, add_points) self._owwidget.progressBarAdvance(100 / n_iters, None) else: self._owwidget.progressBarFinished(None) self._owwidget.progressBarFinished(None) self._owwidget.progressBarInit(None) QTimer.singleShot(10, add_points)