class LineChart(object): """ linegraph """ def __init__(self, data=None): if data is None: data = [] self.data = data self.rect = QRect() self.color = QColor(0, 0, 200) self.background_color = QColor(255, 255, 255) def paint(self, painter): painter.fillRect(self.rect, self.background_color) if not self.data: return min_data = float(min(self.data)) max_data = float(max(self.data)) max_data += max_data / 2 min_data -= min_data / 2 diff = (max_data - min_data) multplier = self.rect.height() / diff w_multiplier = self.rect.width() / float(len(self.data)) for i, d in enumerate(self.data): y = self.rect.height() - ((d - min_data) * multplier) x = i * w_multiplier draw_width = w_multiplier if w_multiplier > 1 else 1 painter.fillRect(int(self.rect.x() + x), int(self.rect.y() + y), int(math.ceil(draw_width)), int(math.ceil(self.rect.height() - y)), self.color)
def adjust_resizers(self, geometry: QRect) -> None: """Top, Left, Right, Bottom resizers are 4 px wide, corner resizers are 8x8 pixels""" radius: int = self.__radius if self.__parent.use_shadow and not self.__parent.maximized else 0 cr = QRect(0 + radius, 0 + radius, geometry.width() - radius * 2, geometry.height() - radius * 2) r_width: int = 4 # resizer width/height c_width: int = 8 # corner width/height crx: int = cr.x() cry: int = cr.y() crw: int = cr.width() crw_sr: int = crw + radius - r_width crh: int = cr.height() crh_sr: int = crh + radius - r_width self.__parent.resizer_top.setGeometry(crx + c_width, cry, crw - c_width * 2, r_width) self.__parent.resizer_bot.setGeometry(crx + c_width, crh_sr, crw - c_width * 2, r_width) self.__parent.resizer_lef.setGeometry(crx, cry + c_width, r_width, crh - c_width * 2) self.__parent.resizer_rig.setGeometry(crw_sr, cry + c_width, r_width, crh - c_width * 2) self.__parent.resizer_tr.setGeometry(crw - c_width + radius, cry, c_width, c_width) self.__parent.resizer_br.setGeometry(crw - c_width + radius, crh - c_width + radius, c_width, c_width) self.__parent.resizer_bl.setGeometry(crx, crh - c_width + radius, c_width, c_width) self.__parent.resizer_tl.setGeometry(crx, cry, c_width, c_width)
def paintEvent(self, event): super(PixmapWidget, self).paintEvent(event) if not self.pixmap or self.pixmap.isNull(): return p = QPainter(self) source = QRect(0, 0, self.pixmap.width(), self.pixmap.height()) sw = float(source.width()) sh = float(source.height()) tw = float(self.width())+1 th = float(self.height())+1 tx = 0 ty = 0 if sw/tw > sh/th: ntw = tw nth = sh/sw*tw ty = (th-nth)/2 else: nth = th ntw = sw/sh*th tx = (tw-ntw)/2 target = QRect(tx, ty, ntw, nth) p.setBrush(self.bgBrush) p.setPen(self.bgPen) p.drawRect(self.rect()) p.drawPixmap(target, self.pixmap, source)
def drawCropTool(self, img): """ Draws the 8 crop buttons around the displayed image, with their current margins. @param img: @type img: QImage """ r = self.parent().img.resize_coeff(self.parent()) left = self.btnDict['left'] top = self.btnDict['top'] bottom = self.btnDict['bottom'] right = self.btnDict['right'] cRect = QRect(round(left.margin), round(top.margin), img.width() - round(right.margin + left.margin), img.height() - round(bottom.margin + top.margin)) p = cRect.topLeft() * r + QPoint(img.xOffset, img.yOffset) x, y = p.x(), p.y() w, h = cRect.width() * r, cRect.height() * r left.move(x - left.width(), y + h // 2) right.move(x + w, y + h // 2) top.move(x + w // 2, y - top.height()) bottom.move(x + w // 2, y + h) topLeft = self.btnDict['topLeft'] topLeft.move(x - topLeft.width(), y - topLeft.height()) topRight = self.btnDict['topRight'] topRight.move(x + w, y - topRight.height()) bottomLeft = self.btnDict['bottomLeft'] bottomLeft.move(x - bottomLeft.width(), y + h) bottomRight = self.btnDict['bottomRight'] bottomRight.move(x + w, y + h)
def draw_wfm_pillow(rect: QRect, wfm_arr: WFMArray) -> QImage: vmin, vmax = -20, 120 vsize = vmax - vmin in_height, in_width = wfm_arr.shape in_rect = QRect(0, 0, in_width, in_height) rect_h, rect_w = rect.height(), rect.width() h_scale = rect_h - 1 w_scale = rect_w - 1 wfm_arr['ypos'] = (wfm_arr['ypos'] * 100 / vmax * vsize - vmin) / vsize * h_scale wfm_arr['xpos'] *= w_scale img = Image.new('RGBA', (rect_w, rect_h), (0, 0, 0, 0)) d = ImageDraw.Draw(img) xy_arr = rfn.structured_to_unstructured(wfm_arr[['xpos', 'ypos']]) for y in range(in_height): d.line(xy_arr[y], fill=(255, 255, 255, 255), width=1) qimg = im.toqimage() if in_rect != rect: qimg = qimg.scaled(rect.size()) return qimg.mirrored(False, True)
def is_inside_limit(limit: QRect, pos: QPoint): if pos.x() < limit.x() or pos.x() > limit.width(): return False elif pos.y() < limit.y() or pos.y() > limit.height(): return False return True
def _rect_to_area(self, rect: QRect): return { "x": rect.left(), "y": rect.top(), "width": rect.width(), "height": rect.height() }
class VoronoiDiagram: def __init__(self, width, height, total_points=20): self.width = width self.height = height self.total_points = total_points self.points = [] self.colors = [] self.rect = QRect() def generate(self): self.points = [] self.colors = [] for i in range(self.total_points): v = QVector2D(random.random(), random.random()) self.points.append(v) c = QColor(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) self.colors.append(c) def get_closest_point(self, v): closest_length = 0 is_first = True index = -1 size_vector = QVector2D(self.width, self.height) for i, p in enumerate(self.points): length = (p * size_vector - v).length() if is_first: closest_length = length is_first = False index = i continue elif length < closest_length: closest_length = length index = i return index def paint(self, painter): chunk_x = float(self.rect.width()) / float(self.width) chunk_y = float(self.rect.height()) / float(self.height) size_vector = QVector2D(self.width, self.height) for y in range(self.height): for x in range(self.width): v = QVector2D(x, y) index = self.get_closest_point(v) c = self.colors[index] draw_x = math.floor(chunk_x * x) dray_y = math.floor(chunk_y * y) painter.setPen(QPen(c)) painter.fillRect(draw_x, dray_y, math.ceil(chunk_x), math.ceil(chunk_y), c) painter.setPen(QPen(QColor(100, 100, 100))) for v in self.points: painter.drawRect(v.x() * size_vector.x() * chunk_x, v.y() * size_vector.y() * chunk_y, 2, 2)
def updateLineNumberArea(self, rect: QRect, dy: int = 0): # todo: move this into LineNumberArea if dy: self.lineNumberArea.scroll(0, dy) else: self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height()) if rect.contains(self.viewport().rect()): self.setViewportMargins(self.lineNumberArea.numberWidth(), 0, 0, 0)
def drawHeader(self): """Draw logo/copyright in the header""" pHeight = 90 pMargin = 15 icon_path = cm.DIR_ICONS + "app.png" self.header_lbl.setMinimumHeight(pHeight) self.header_lbl.setFrameShape(QFrame.StyledPanel) self.header_lbl.setContentsMargins(0, 0, 0, 0) pixmap = QPixmap(450, pHeight) pixmap.fill(Qt.transparent) iconY = (pHeight - 64) / 2 logoRect = QRect(pMargin, iconY, 64, 64) painter = QPainter(pixmap) painter.setBrush(QBrush(Qt.red)) painter.drawPixmap( logoRect, QPixmap(icon_path).scaled( logoRect.width(), logoRect.height(), Qt.KeepAspectRatio, Qt.SmoothTransformation, ), ) titleRect = QRect(logoRect.right() + 10, iconY, 200, pHeight) font = QFont() font.setBold(True) font.setPixelSize(16) painter.setFont(font) painter.setPen(QPen(QApplication.instance().palette().text().color())) painter.drawText(titleRect, Qt.AlignTop, "Cutevariant") font_metrics = QFontMetrics(font) font.setBold(False) font.setPixelSize(12) painter.setFont(font) painter.setPen(QPen(Qt.darkGray)) titleRect.setY(titleRect.y() + font_metrics.height()) painter.drawText( titleRect, Qt.AlignTop, f"Version %s\nGPL3 Copyright (C) 2018-2020\nLabsquare.org" % __version__, ) self.header_lbl.setPixmap(pixmap) # Painting is finished ! # Avoid Segfault: # QPaintDevice: Cannot destroy paint device that is being painted painter.end()
def get_graticules( rect: QRect) -> Tuple[Dict[float, float], Dict[float, QLineF]]: """Get a set of graticules scaled to fit within the given :class:`QtCore.QRect` The scale ranges from -20 to 120 (ire) in increments of 10. An extra value of 7.5 ire is included (NTSC setup level) Arguments: rect (:class:`QtCore.QRect`): The bounding box as a :class:`QtCore.QRect` Returns ------- ire_vals : dict A mapping of ire values to their normalized positions lines : dict A mapping of :class:`QtCore.QLineF` objects with their ire values as keys """ # Overall scale: -20 to 120 # ire_vals = { # 0: 0, # 7.5: 16 / 255, # NTSC black # 100: 235 / 255, # } ire_vals = {} # scale_factor = 255/219 vmax = 120 vmin = -20 vsize = vmax - vmin ires = [ -20, -10, 0, 7.5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120 ] def ire_to_pos_norm(ire): v = ire # v = (ire * 219 + 16) / 255# * scale_factor return (v - vmin) / vsize for ire in ires: ire_vals[ire] = ire_to_pos_norm(ire) lines = {} rect_w = rect.width() rect_h = rect.height() w_scale = rect_w - 1 h_scale = rect_h - 1 for ire, pos_norm in ire_vals.items(): pos_y = (pos_norm * h_scale - h_scale) * -1 lines[float(ire)] = QLineF(0, pos_y, rect_w, pos_y) return ire_vals, lines
def __init__(self): super().__init__(flags=Qt.Widget | Qt.FramelessWindowHint | Qt.BypassWindowManagerHint | Qt.WindowTransparentForInput | Qt.WindowStaysOnTopHint) self.logger = logging.getLogger(__name__ + "." + self.__class__.__name__) self.setAttribute(Qt.WA_NoSystemBackground, True) self.setAttribute(Qt.WA_TranslucentBackground, True) self.setAttribute(Qt.WA_DeleteOnClose, True) self.setStyleSheet("background: transparent") self._instances = {} virtual_screen = QRect(0, 0, 0, 0) for screen in QGuiApplication.screens(): # TODO: Handle screen change geom = screen.virtualGeometry() virtual_screen = virtual_screen.united(geom) self.scene = QGraphicsScene(0, 0, virtual_screen.width(), virtual_screen.height(), parent=self) self.view = QGraphicsView(self.scene, self) self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.view.setStyleSheet("background: transparent") self.view.setGeometry(0, 0, virtual_screen.width(), virtual_screen.height()) self.view.setInteractive(False) self.transparent_pen = QPen() self.transparent_pen.setBrush(Qt.NoBrush) self.setGeometry(virtual_screen)
def erase_cursor_rect(self, pos: Tuple[int, int]): painter = QPainter(self.cursors_pixmap) cursor_rect = QRect(self.byte_rect) h = cursor_rect.height() i, j = pos cursor_rect.moveTopLeft(QPoint(i * self.byte_advance, j * h)) painter.setCompositionMode(QPainter.CompositionMode_Source) painter.setPen(Qt.NoPen) painter.setBrush(Qt.transparent) painter.drawRect(cursor_rect) painter.setCompositionMode(QPainter.CompositionMode_SourceOver)
def fit_solution_in_view(self, solution): if not solution.rect: return solution_rect = QRect( QPoint(solution.rect.left, solution.rect.top), QPoint(solution.rect.right, solution.rect.bottom)) viewport_rect = self.viewport.rect() # compute zoom level if solution_rect.width() > solution_rect.height(): zoom = viewport_rect.width() / solution_rect.width( ) if viewport_rect.width() else 1 else: zoom = viewport_rect.height() / solution_rect.height( ) if viewport_rect.height() else 1 self.zoom = 0.80 * zoom if zoom else 1 self.zoom = clamp(self.zoom, MIN_ZOOM, MAX_ZOOM) # limit zoom level # center view on rect center self.pos = -QPointF(solution_rect.center())
def paintEvent(self, e): """ Draws the color bar and arrow """ if self.item(): painter = QPainter(self) # Draws the color bar painter.setPen(Qt.NoPen) rect = QRect(0, 0, self.COLOR_BAR_WIDTH, self.COLOR_BAR_HEIGHT) painter.fillRect(rect, self.item().data(renderSetupRoles.NODE_COLOR_BAR)) oldBrush = painter.brush() if self.item().type( ) == renderSetup.RENDER_OVERRIDE_TYPE and self.item( ).isLocalRender(): diameter = rect.width() - 2 rect2 = QRect(rect.x() + 1, rect.y() + (rect.height() - diameter) / 2, diameter, diameter) brush = painter.brush() pen = painter.pen() hints = painter.renderHints() painter.setRenderHint(QPainter.Antialiasing, on=True) painter.setPen(Qt.NoPen) painter.setBrush( QBrush(QColor(67, 79, 70), style=Qt.SolidPattern)) painter.drawEllipse(rect2) painter.setRenderHints(hints) painter.setPen(pen) painter.setBrush(brush) # Draws the arrow painter.setBrush(self.ARROW_COLOR) if self.arrowPoints == BaseDelegate.EXPANDED_ARROW: painter.translate(self.EXPANDED_ARROW_OFFSET, 0.0) else: painter.translate(self.COLLAPSED_ARROW_OFFSET, 0.0) painter.drawPolygon(self.arrowPoints) painter.setBrush(oldBrush) # Draws the node type text painter.setPen(QPen(self.item().data(Qt.TextColorRole))) text = self.item().data(renderSetupRoles.NODE_TYPE_STR) painter.setFont(self.item().data(Qt.FontRole)) painter.drawText(self.NODE_TYPE_TEXT_RECT, text, QTextOption(Qt.AlignLeft | Qt.AlignVCenter))
def calculate_screen_limits(self): screen = QRect(self.app.desktop().x(), self.app.desktop().y(), self.app.desktop().width(), self.app.desktop().availableGeometry().height()) width_margin = round(self.geometry().width() / 2) height_margin = round(self.geometry().height() / 2) # Case where secondary screen has negative values desktop_width = screen.x() + screen.width() min_x = screen.x() - width_margin min_y = screen.y() - height_margin max_x = desktop_width - width_margin max_y = screen.height() - height_margin return QRect(min_x, min_y, max_x, max_y)
class Physics2D: def __init__(self): self.objects = [] self.rect = QRect() self.debug = False self.gravity = 9.81 self.last_time = time.time() def circle_intersects(self, p0, r0, p1, r2): distance = p0 - p1 if distance.length() < (r0 + r2): return True else: return False @classmethod def line_intersects(cls, p0, p1, p2, p3): """does the lines intersect """ return True @classmethod def box_intersects(cls, boxa, boxb): return False def add_sphere(self, radius=10.0): sphere = Sphere(self) self.objects.append(sphere) sphere.radius = radius sphere.pos.setX(self.rect.width() / 2.0) sphere.pos.setY(self.rect.height() / 2.0) return sphere def tick(self): current_time = time.time() dt = current_time - self.last_time for obj in self.objects: obj.tick(dt) self.last_time = current_time def reflect(self, point, normal): v = point - (normal * QVector2D.dotProduct(point, normal) * 2) v.normalize() return v
def print_to_pixmap(self, painter, byte_addr, string, pen): index = self.byte_index(byte_addr) byte_rect = QRect(self.byte_rect) h = byte_rect.height() painter.setBrush(BRUSH_EMPTY) for digit1, digit2 in pairs(string): i, j = next(index) txt = digit1 + digit2 byte_rect.moveTopLeft(QPoint(i * self.byte_advance, j * h)) painter.setPen(Qt.NoPen) painter.drawRect(byte_rect) painter.setPen(pen) painter.drawText(byte_rect, Qt.AlignCenter, f'{txt}')
def draw_cursor_rect(self, pos: Tuple[int, int], brush, addr): painter = QPainter(self.cursors_pixmap) cursor_rect = QRect(self.byte_rect) h = cursor_rect.height() i, j = pos cursor_rect.moveTopLeft(QPoint(i * self.byte_advance, j * h)) painter.setPen(Qt.NoPen) painter.setBrush(brush) painter.drawRect(cursor_rect) painter.setFont(self.font) txt = self.bytes_field[addr][0] painter.setPen(PEN_BCK) painter.drawText(cursor_rect, Qt.AlignCenter, f'{txt}') painter.end()
def paint_waveform_arr(rect: QRect, wfm_arr: WFMArray) -> QPainterPath: rect_w = rect.width() rect_h = rect.height() w_scale = rect_w - 1 h_scale = rect_h - 1 vmin, vmax = -20, 120 vsize = vmax - vmin ypos = wfm_arr['ypos'] ypos = (wfm_arr['ypos'] * 100 / vmax * vsize - vmin) / vsize ypos_w = (ypos * h_scale - h_scale) * -1 wfm_range = (wfm_arr['ypos'].min(), wfm_arr['ypos'].max()) ypos_range = (ypos.min(), ypos.max()) ypos_w_range = (ypos_w.min(), ypos_w.max()) # print(f'{wfm_range=}, {ypos_range=}, {ypos_w_range=}, {rect_h=}') wfm_arr['ypos'] = ypos_w h, w = wfm_arr.shape wfm_arr['xpos'] *= w_scale xy_arr = rfn.structured_to_unstructured(wfm_arr[['xpos', 'ypos']]) paths = QPainterPath() for y in range(h): path = QPainterPath() y_arr = xy_arr[y] for x in range(w): xy = y_arr[x] if x > 0: path.lineTo(xy[0], xy[1]) else: path.moveTo(xy[0], xy[1]) paths.addPath(path) return paths
def _locate_win_result(self, target_rect: QRect): if target_rect.width() < 2 or target_rect.height() < 2: return # Trigger a resize to regular size if previous size was fitted to a window self.img_view.change_viewer_size() if self.locate_btn.key_modifier == 0 and self.img_view.height() > 0: # No Key Modifier tries to keep aspect ratio height_factor: float = self.img_view.height() / self.img_view.width() height: int = round(target_rect.width() * height_factor) target_rect.setHeight(height) LOGGER.debug('Trying to keep aspect ratio at %s height factor. %s %s', height_factor, target_rect.width(), height) elif self.locate_btn.key_modifier == Qt.ControlModifier: # Ctrl Key only moves window self.img_view.move(target_rect.topLeft()) return elif self.locate_btn.key_modifier == Qt.ShiftModifier: # Shift/Any Key Modifier fits to window without respecting aspect ratio of image pass self.img_view.resize(target_rect.size()) self.img_view.move(target_rect.topLeft())
class LevyFlight(object): MOVE_UP = 0 MOVE_DOWN = 1 MOVE_RIGHT = 2 MOVE_LEFT = 3 def create_random_vector2d(self): a = random.random() * math.pi * 2 return QVector2D(math.cos(a), math.sin(a)) def __init__(self): self.rect = QRect() self.path = [] self.start_pos = QVector2D() self.pos = QVector2D() self.resolution = QSize() self.up_vector = QVector2D(0, -1) self.down_vector = QVector2D(0, 1) self.left_vector = QVector2D(-1, 0) self.right_vector = QVector2D(1, 0) self.head_color = QColor(250, 60, 50) self.tail_color = QColor(70, 70, 70) def reset(self): self.path = [] self.pos = self.start_pos def setup(self): self.reset() def get_next_pos(self): pos_copy = QVector2D(self.pos) v = self.create_random_vector2d() if random.random() < 0.01: pos_copy += v * random.randint(20, 40) else: pos_copy += v * random.randint(1, 5) return pos_copy def tick(self): """ Tick the random walker to do something """ while True: pos = self.get_next_pos() is_valid = True if pos.x() > self.resolution.width(): is_valid = False elif pos.x() < 0: is_valid = False elif pos.y() > self.resolution.height(): is_valid = False elif pos.y() < 0: is_valid = False if is_valid: self.path.append(pos) self.pos = pos break def paint(self, painter): chunk_x = float(self.rect.width()) / self.resolution.width() chunk_y = float(self.rect.height()) / self.resolution.height() painter_path = QPainterPath() for i, pos in enumerate(self.path): x = pos.x() * chunk_x y = pos.y() * chunk_y p = QPoint(x, y) if i == 0: painter_path.moveTo(p) else: painter_path.lineTo(p) painter.drawPath(painter_path)
def on_instance_moved(self, instance, pos: QRect): rect = self._instances[instance.wid] rect.setRect(0, 0, pos.width(), pos.height()) rect.setPos(pos.x(), pos.y())
def mouseMoveEvent(self, event: QMouseEvent) -> None: """Qt Mouse-Move Event""" def get_top_resize_values() -> Tuple[int, int]: """calculates vertial position and height""" py: int = (event.globalPos() - self.__mouse_pos).y() if self.__window_geometry_height - py < self.__parent.minimumSize( ).height(): height: int = self.__parent.minimumSize().height() pos_y: int = self.__window_pos_y + self.__window_geometry_height - height else: height = self.__window_geometry_height - py pos_y = self.__window_pos_y + py return pos_y, height def get_left_resize_values() -> Tuple[int, int]: """calculates horizontal position and width""" px: int = (event.globalPos() - self.__mouse_pos).x() if self.__window_geometry_width - px < self.__parent.minimumSize( ).width(): width: int = self.__parent.minimumSize().width() pos_x: int = self.__window_pos_x + self.__window_geometry_width - width else: width = self.__window_geometry_width - px pos_x = self.__window_pos_x + px return pos_x, width if self.__mouse_pressed: if self.__type == self.BOTTOM: py = (event.globalPos() - self.__mouse_pos).y() geometry = QRect(self.__window_geometry_x, self.__window_geometry_y, self.__window_geometry_width, self.__window_geometry_height + py) elif self.__type == self.RIGHT: px = (event.globalPos() - self.__mouse_pos).x() geometry = QRect(self.__window_geometry_x, self.__window_geometry_y, self.__window_geometry_width + px, self.__window_geometry_height) elif self.__type == self.TOP: pos_y, height = get_top_resize_values() geometry = QRect(self.__window_geometry_x, pos_y, self.__window_geometry_width, height) elif self.__type == self.LEFT: pos_x, width = get_left_resize_values() geometry = QRect(pos_x, self.__window_geometry_y, width, self.__window_geometry_height) elif self.__type == self.TOP_LEFT: pos_x, width = get_left_resize_values() pos_y, height = get_top_resize_values() geometry = QRect(pos_x, pos_y, width, height) elif self.__type == self.TOP_RIGHT: px = (event.globalPos() - self.__mouse_pos).x() pos_y, height = get_top_resize_values() geometry = QRect(self.__window_pos_x, pos_y, self.__window_geometry_width + px, height) elif self.__type == self.BOTTOM_LEFT: pos_x, width = get_left_resize_values() py = (event.globalPos() - self.__mouse_pos).y() geometry = QRect(pos_x, self.__window_pos_y, width, self.__window_geometry_height + py) elif self.__type == self.BOTTOM_RIGHT: px = (event.globalPos() - self.__mouse_pos).x() py = (event.globalPos() - self.__mouse_pos).y() geometry = QRect(self.__window_geometry_x, self.__window_geometry_y, self.__window_geometry_width + px, self.__window_geometry_height + py) if geometry.width() < self.__parent.minimumSize().width(): geometry.setWidth(self.__parent.minimumSize().width()) if geometry.height() < self.__parent.minimumSize().height(): geometry.setHeight(self.__parent.minimumSize().height()) self.__parent.setGeometry(geometry) self.adjust_resizers(geometry)
class SlippyMap(QObject): updated = Signal(QRect) def __init__(self, parent=None): """ :param parent: """ super(SlippyMap, self).__init__(parent) self._offset = QPoint() self._tiles_rectangle = QRect() self._tile_pixmaps = {} # Point(x, y) to QPixmap mapping self._manager = QNetworkAccessManager() self._url = QUrl() # public vars self.width = 400 self.height = 300 self.zoom = 4 self.latitude = 59.9138204 self.longitude = 10.7387413 self._emptyTile = QPixmap(TDIM, TDIM) self._emptyTile.fill(Qt.lightGray) self.request = QNetworkRequest() self.cache = QNetworkDiskCache() self.cache.setCacheDirectory( QStandardPaths.writableLocation(QStandardPaths.CacheLocation)) self._manager.setCache(self.cache) self._manager.finished.connect(self.handle_network_data) def invalidate(self): """ :return: """ if self.width <= 0 or self.height <= 0: return ct = tile_for_coordinate(self.latitude, self.longitude, self.zoom) tx = ct.x() ty = ct.y() # top-left corner of the center tile xp = int(self.width / 2 - (tx - math.floor(tx)) * TDIM) yp = int(self.height / 2 - (ty - math.floor(ty)) * TDIM) # first tile vertical and horizontal xa = (xp + TDIM - 1) / TDIM ya = (yp + TDIM - 1) / TDIM xs = int(tx) - xa ys = int(ty) - ya # offset for top-left tile self._offset = QPoint(int(xp - xa * TDIM), int(yp - ya * TDIM)) # last tile vertical and horizontal xe = int(tx) + (self.width - xp - 1) / TDIM ye = int(ty) + (self.height - yp - 1) / TDIM # build a rect self._tiles_rectangle = QRect(int(xs), int(ys), int(xe - xs + 1), int(ye - ys + 1)) if self._url.isEmpty(): self.download() self.updated.emit(QRect(0, 0, self.width, self.height)) def render(self, p: QPainter, rect: QRect): """ Render a tile :param p: QPainter instance, place where to pain the tiles :param rect: QRect instance, dimensions of the painter (the window that renders the tiles) :return: Nothing """ rx = range(self._tiles_rectangle.width()) ry = range(self._tiles_rectangle.height()) for x, y in product(rx, ry): tp = Point(x + self._tiles_rectangle.left(), y + self._tiles_rectangle.top()) box = self.tile_rectangle(tp) if rect.intersects(box): p.drawPixmap(box, self._tile_pixmaps.get(tp, self._emptyTile)) def pan(self, delta: QPoint): """ Move the map :param delta: x, y delta as a QPoint instance :return: Nothing """ dx = QPointF(delta) / float(TDIM) center = tile_for_coordinate(self.latitude, self.longitude, self.zoom) - dx self.latitude = latitude_from_tile(center.y(), self.zoom) self.longitude = longitude_from_tile(center.x(), self.zoom) self.invalidate() # slots def handle_network_data(self, reply: QNetworkReply): """ This function is called automatically by a QNetworkAccessManager object (self._manager) :param reply: QNetworkReply instance :return: Nothing """ img = QImage() tp = Point(reply.request().attribute(QNetworkRequest.User)) url = reply.url() if not reply.error(): # if there was no url error... if img.load(reply, None): # if the image loading went well... self._tile_pixmaps[tp] = QPixmap.fromImage( img) # store the image in the tiles dictionary reply.deleteLater() self.updated.emit(self.tile_rectangle(tp)) # purge unused tiles bound = self._tiles_rectangle.adjusted(-2, -2, 2, 2) for tp in list(self._tile_pixmaps.keys()): if not bound.contains(tp): del self._tile_pixmaps[tp] self.download() def download(self): """ Download tile :return: Nothing """ grab = None rx = range(self._tiles_rectangle.width()) ry = range(self._tiles_rectangle.height()) for x, y in product(rx, ry): tp = Point(self._tiles_rectangle.topLeft() + QPoint(x, y)) if tp not in self._tile_pixmaps: grab = QPoint(tp) break if grab is None: self._url = QUrl() return path = 'http://tile.openstreetmap.org/%d/%d/%d.png' % ( self.zoom, grab.x(), grab.y()) self._url = QUrl(path) self.request = QNetworkRequest() self.request.setUrl(self._url) self.request.setRawHeader(b'User-Agent', b'Nokia (PyQt) Graphics Dojo 1.0') self.request.setAttribute(QNetworkRequest.User, grab) self._manager.get(self.request) print('downloading z:', self.zoom, 'x:', grab.x(), 'y:', grab.y()) def tile_rectangle(self, tp: Point): """ Get tile rectangle :param tp: Tile point :return: QRect instance """ t = tp - self._tiles_rectangle.topLeft() x = t.x() * TDIM + self._offset.x() y = t.y() * TDIM + self._offset.y() return QRect(x, y, TDIM, TDIM)
class PaletteWidget(QtWidgets.QWidget): # Color changed signal changed = QtCore.Signal() @property def color(self): return QtGui.QColor.fromHsvF(self._hue, self._saturation, self._value) @color.setter def color(self, value): # If this is an integer, assume is RGBA if not isinstance(value, QtGui.QColor): r = (value & 0xff000000) >> 24 g = (value & 0xff0000) >> 16 b = (value & 0xff00) >> 8 value = QtGui.QColor.fromRgb(r, g, b) self._set_color(value) self.RGBvalue.setText(value.name()) def __init__(self, parent=None, RGBvalue=None): super(PaletteWidget, self).__init__(parent) self._hue = 1.0 self._saturation = 1.0 self._value = 1.0 self._hue_width = 24 self._gap = 8 self._color = QtGui.QColor.fromHslF(self._hue, 1.0, 1.0) self._calculate_bounds() self._draw_palette() self.RGBvalue = RGBvalue # Calculate the sizes of various bits of our UI def _calculate_bounds(self): width = self.width() height = self.height() # Hue palette self._hue_rect = QRect(width - self._hue_width, 0, self._hue_width, height) # Shades palette self._shades_rect = QRect(0, 0, width - (self._hue_width + self._gap), height) # Render our palette to an image def _draw_palette(self): # Create an image with a white background self._image = QtGui.QImage(QtCore.QSize(self.width(), self.height()), QtGui.QImage.Format.Format_RGB32) self._image.fill(QtGui.QColor.fromRgb(0xff, 0xff, 0xff)) # Draw on our image with no pen qp = QtGui.QPainter() qp.begin(self._image) qp.setPen(QtCore.Qt.NoPen) # Render hues rect = self._hue_rect for x in xrange(rect.x(), rect.x() + rect.width()): for y in xrange(rect.y(), rect.y() + rect.height(), 8): h = float(y) / rect.height() s = 1.0 v = 1.0 c = QtGui.QColor.fromHsvF(h, s, v) qp.setBrush(c) qp.drawRect(x, y, 8, 8) # Render hue selection marker qp.setBrush(QtGui.QColor.fromRgb(0xff, 0xff, 0xff)) qp.drawRect(rect.x(), self._hue * rect.height(), rect.width(), 2) # Render shades rect = self._shades_rect width = float(rect.width()) steps = int(round(width / 8.0)) step_size = width / steps x = rect.x() while x < rect.width() + rect.x(): w = int(round(step_size)) for y in xrange(rect.y(), rect.y() + rect.height(), 8): h = self._hue s = 1 - float(y) / rect.height() v = float(x) / rect.width() c = QtGui.QColor.fromHsvF(h, s, v) qp.setBrush(c) qp.drawRect(x, y, w, 8) x += w width -= w steps -= 1 if steps > 0: step_size = width / steps # Render color selection marker qp.setBrush(QtGui.QColor.fromRgb(0xff, 0xff, 0xff)) qp.drawRect(rect.x(), (1 - self._saturation) * rect.height(), rect.width(), 1) qp.drawRect(self._value * rect.width(), rect.y(), 1, rect.height()) qp.end() def paintEvent(self, event): # Render our palette image to the screen qp = QtGui.QPainter() qp.begin(self) qp.drawImage(QPoint(0, 0), self._image) qp.end() def mousePressEvent(self, event): mouse = QPoint(event.pos()) if event.buttons() & QtCore.Qt.LeftButton: # Click on hues? if self._hue_rect.contains(mouse.x(), mouse.y()): y = mouse.y() c = QtGui.QColor.fromHsvF(float(y) / self.height(), self._saturation, self._value) self.color = c # Click on colors? elif self._shades_rect.contains(mouse.x(), mouse.y()): # calculate saturation and value x = mouse.x() y = mouse.y() c = QtGui.QColor.fromHsvF(self._hue, 1 - float(y) / self._shades_rect.height(), float(x) / self._shades_rect.width()) self.color = c def mouseMoveEvent(self, event): if event.buttons() & QtCore.Qt.LeftButton: self.mousePressEvent(event) def resizeEvent(self, event): self._calculate_bounds() self._draw_palette() # Set the current color def _set_color(self, c): h, s, v, _ = c.getHsvF() self._hue = h self._saturation = s self._value = v self._draw_palette() self.repaint() self.changed.emit()
class DLA(object): def __init__(self): super(DLA, self).__init__() self.on_stick_func = None self.walker_radius = WALKER_RADIUS self.rect = QRect() self.sticking_walkers = [] self.active_walkers = [] self.max_active_walkers = MAX_ACTIVE_WALKERS self.max_sticking_walkers = MAX_STICKING_WALKER self.finished = False self.walker_pen = QPen() self.walker_brush = QBrush(QColor()) self.active_walker_color = QColor(150, 150, 150) def get_progress(self): progress = (len(self.sticking_walkers) - 1) / float( self.max_sticking_walkers) return progress def reset(self): self.setup() def setup(self): w = Walker(self, self.rect.width() / 2, self.rect.height() / 2) w.radius = self.walker_radius w.color = QColor(200, 0, 0) self.sticking_walkers = [w] self.active_walkers = [] for i in range(self.max_active_walkers): self.active_walkers.append(self.spawn_new_walker()) self.finished = False self.request_new_walker() def paint_walker(self, painter, w, color=None): painter.setPen(QPen(color or w.color)) painter.setBrush(QBrush(color or w.color)) painter.drawEllipse(math.floor(w.pos.x() - w.radius), math.floor(w.pos.y() - w.radius), math.ceil(w.radius * 2.0), math.ceil(w.radius * 2.0)) def paint(self, painter): for w in self.active_walkers: self.paint_walker(painter, w, self.active_walker_color) for w in self.sticking_walkers: self.paint_walker(painter, w) def spawn_new_walker(self): side = random.randint(0, 3) rand_value = random.random() * self.rect.width() if side == 0: walker = Walker(self, rand_value, self.rect.y()) elif side == 1: walker = Walker(self, rand_value, self.rect.height()) elif side == 2: walker = Walker(self, self.rect.x(), rand_value) elif side == 3: walker = Walker(self, self.rect.width(), rand_value) else: raise RuntimeError('not a valid choice') progress = self.get_progress() walker.radius = self.walker_radius # walker.radius = (1.0 - self.get_progress()) * self.walker_radius walker.color = QColor((1.0 - progress) * 150, 0, 150 * progress) return walker def request_new_walker(self): if len(self.sticking_walkers) <= self.max_sticking_walkers: walker = self.spawn_new_walker() self.active_walkers.append(walker) def tick(self): if not self.active_walkers: return for w in self.active_walkers: w.walk() for active_walker in self.active_walkers: active_sticks = False for sticking_walker in self.sticking_walkers: if active_walker.intersects(sticking_walker): active_sticks = True break if active_sticks: if self.on_stick_func: self.on_stick_func() self.sticking_walkers.append(active_walker) self.active_walkers.pop( self.active_walkers.index(active_walker)) self.request_new_walker()
def paintCell( self, painter: QtGui.QPainter, rect: QtCore.QRect, date: QtCore.QDate ): # Paint background if self.selectedDate() == date: painter.fillRect(rect, self.Color.selected_background) # Paint date text if self.selectedDate() != date: if self.monthShown() != date.month(): painter.setPen(self.Color.unselected_month_date_text) elif qdate_is_weekend(date): painter.setPen(self.Color.unselected_weekend_date_text) else: painter.setPen(self.Color.unselected_date_text) else: painter.setPen(self.Color.selected_date_text) painter.setFont(QtGui.QFont("Helvetica", 20)) painter.drawText(rect, QtCore.Qt.AlignTop, str(date.day())) # Get todo items to paint items = [] try: items = self.todo_item_dict[date.year()][date.month()][date.day()] except KeyError: pass if len(items) != 0: # Paint number of todo items # Text color is same is date's completed_items_num = len([item for item in items if item["completed"]]) x = 40 y = 5 width = rect.width() - x painter.setFont(QtGui.QFont("Helvetica", 14)) item_number_str = f"{completed_items_num}/{len(items)} items" painter.drawText( rect.x() + x, rect.y() + y, width, rect.height(), 0, item_number_str ) # Paint todo items x = 5 y = 32 width = rect.width() - x painter.setFont(QtGui.QFont("Helvetica", 10)) for item in items: if y > rect.height(): break if item["completed"]: painter.setPen(self.Color.completed_todo_item) else: painter.setPen(self.Color.uncompleted_todo_item) height = rect.height() - y bounding_rect = painter.boundingRect( rect.x() + x, rect.y() + y, width, height, QtCore.Qt.TextWordWrap, item["name"], ) painter.drawText( rect.x() + x, rect.y() + y, width, height, QtCore.Qt.TextWordWrap, item["name"], ) y += bounding_rect.height()
def paintCell( self, painter: QtGui.QPainter, rect: QtCore.QRect, qdate: QtCore.QDate ): date = qdate_to_date(qdate) # Paint date text if self.monthShown() != qdate.month(): painter.setPen(self.Color.unselected_month_date_text) else: painter.setPen(self.Color.unselected_date_text) painter.setFont(QtGui.QFont("Helvetica", 20)) painter.drawText(rect, QtCore.Qt.AlignTop, str(qdate.day())) # Get all of the chains for the current week for date_range, chains in self.month_chains.items(): if date_range[0] <= date and date <= date_range[1]: display_chains = chains break else: display_chains = self.generate_week_info(qdate_to_date(qdate)) # Paint completed chains completed_chains_num = 0 x = 5 y = 32 width = rect.width() - x painter.setFont(QtGui.QFont("Helvetica", 10)) for chain in display_chains: if y > rect.height(): break height = rect.height() - y bounding_rect = painter.boundingRect( rect.x() + x, rect.y() + y, width, height, QtCore.Qt.TextWordWrap, chain, ) if chain_handler.get_chain(chain, qdate.year(), qdate.month(), qdate.day()): # Draw colored rect painter.fillRect( rect.x(), rect.y() + y, rect.width(), min(bounding_rect.height(), rect.height() - y), self.chain_color_dict[chain], ) # Draw text painter.setPen(self.Color.chain_name_color) completed_chains_num += 1 painter.drawText( rect.x() + x, rect.y() + y, width, height, QtCore.Qt.TextWordWrap, chain, ) y += bounding_rect.height() # Paint number of completed todo items if completed_chains_num > 0: if self.monthShown() != qdate.month(): painter.setPen(self.Color.unselected_month_date_text) else: painter.setPen(self.Color.unselected_date_text) x = 40 y = 5 width = rect.width() - x painter.setFont(QtGui.QFont("Helvetica", 14)) item_number_str = f"{completed_chains_num}" painter.drawText( rect.x() + x, rect.y() + y, width, rect.height(), 0, item_number_str ) # Surround current date in a blue border if datetime.date.today() == date: painter.setPen(self.Color.selected_background) painter.drawRect(rect.x(), rect.y(), rect.width(), rect.height())
def __transform_for_paint(self, image: Image.Image, rect: QRect) -> Image.Image: return image.crop((rect.x() / self._zoom, rect.y() / self._zoom, (rect.x() + rect.width()) / self._zoom, (rect.y() + rect.height()) / self._zoom)).resize( (rect.width(), rect.height()))
def paint_graticules( painter: 'QtGui.QPainter', rect: QRect) -> Tuple[Dict[float, float], Dict[float, QLineF]]: """Draw graticules and text markers using the QPainter api Graticules are calculated using :func:`get_graticules` then drawn on the given :class:`QtGui.QPainter`. IRE value labels are then drawn alternating on the left and right sides Arguments: painter (:class:`QtGui.QPainter`): The QPainter to draw with rect (:class:`QtGui.QRect`): The bounding box to use for drawing Returns ------- ire_vals : dict A mapping of ire values to their normalized positions lines : dict A mapping of :class:`QtCore.QLineF` objects with their ire values as keys """ ire_vals, graticules = get_graticules(rect) rect_w = rect.width() rect_h = rect.height() vertical_sp = graticules[10].y1() - graticules[20].y1() txt_flags = Qt.TextSingleLine bounding_rect = QtCore.QRectF(0, 0, rect_w, rect_h) font = QtGui.QFont('monospace', 12) fmetrics = QtGui.QFontMetrics(font) txt_box = fmetrics.size(txt_flags, '100') # logger.debug(f'{vertical_sp=}, {txt_box=}') while txt_box.height() > vertical_sp * .75: if font.pointSize() - 1 < 1: break font.setPointSize(font.pointSize() - 1) fmetrics = QtGui.QFontMetrics(font) txt_box = fmetrics.size(txt_flags, '100') # logger.debug(f'{vertical_sp=}, {txt_box=}') # logger.info('graticules font size: {}'.format(font.pointSize())) lh_flags = Qt.AlignLeft | Qt.AlignTop # | Qt.TextSingleLine rh_flags = Qt.AlignRight | Qt.AlignTop # | Qt.TextSingleLine painter.setFont(font) painter.setPen(QColor('yellow')) # logger.debug(f'graticule rect: {rect}') # logger.debug(f'graticules: {graticules}') txt_left = True for ire, line in graticules.items(): ypos = line.y1() if ire == 0 or ire == 100: painter.setPen(QColor('white')) else: painter.setPen(QColor('yellow')) painter.drawLine(line) _txt_box = QRectF(0, 0, txt_box.width(), txt_box.height()) if txt_left: _txt_box.moveTopLeft(QPointF(0, ypos)) if bounding_rect | _txt_box == bounding_rect: painter.drawText(_txt_box, f'{ire:g}', lh_flags) else: _txt_box.moveTopRight(QPointF(rect_w, ypos)) if bounding_rect | _txt_box == bounding_rect: painter.drawText(_txt_box, f'{ire:g}', rh_flags) txt_left = not txt_left return ire_vals, graticules