Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add icons for users, mirrors in publisher list #242

Merged
merged 5 commits into from
May 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions koordinates/gui/gui_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import math
import os
import re
from typing import Optional

from qgis.PyQt.QtCore import Qt
Expand Down Expand Up @@ -146,6 +147,20 @@ def scale_icon_size(standard_size: int) -> int:
return int(math.floor(max(Qgis.UI_SCALE_FACTOR * fm.height() * scale,
float(standard_size))))

@staticmethod
def get_default_font() -> QFont:
"""
Returns the best font match for the Koordinates default font
families which is available on the system
"""
for family in FONT_FAMILIES.split(','):
family_cleaned = re.match(r'^\s*\'?(.*?)\'?\s*$', family).group(1)
font = QFont(family_cleaned)
if font.exactMatch():
return font

return QFont()

@staticmethod
def get_font_path(font: str) -> str:
"""
Expand Down
26 changes: 18 additions & 8 deletions koordinates/gui/publisher_filter_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
)
from ..api import PublisherType
from .rounded_highlight_box import RoundedHighlightBox
from .user_avatar_generator import UserAvatarGenerator
from .gui_utils import GuiUtils


class PublisherDelegate(QStyledItemDelegate):
Expand Down Expand Up @@ -130,7 +132,10 @@ def paint(self, painter: QPainter, option: QStyleOptionViewItem,
if publisher.theme:
background_color = publisher.theme.background_color()
if not background_color:
background_color = QColor('#f5f5f7')
if publisher.publisher_type == PublisherType.User:
background_color = QColor('#f5f5f7')
else:
background_color = QColor('#555657')

thumbnail_image = index.data(PublisherModel.ThumbnailRole)
painter.setPen(Qt.NoPen)
Expand All @@ -149,13 +154,18 @@ def paint(self, painter: QPainter, option: QStyleOptionViewItem,
scaled = DatasetGuiUtils.crop_image_to_circle(
thumbnail_image, thumbnail_image.height()
)

center_x = int((thumbnail_rect.width() - scaled.width()) / 2)
center_y = int((thumbnail_rect.height() - scaled.height()) / 2)
painter.drawImage(QRectF(thumbnail_rect.left() + center_x,
thumbnail_rect.top() + center_y,
scaled.width(), scaled.height()),
scaled)
elif publisher.publisher_type == PublisherType.User:
scaled = UserAvatarGenerator.get_avatar(publisher.name())
else:
scaled = GuiUtils.get_svg_as_image('globe.svg',
40, 40)

center_x = int((thumbnail_rect.width() - scaled.width()) / 2)
center_y = int((thumbnail_rect.height() - scaled.height()) / 2)
painter.drawImage(QRectF(thumbnail_rect.left() + center_x,
thumbnail_rect.top() + center_y,
scaled.width(), scaled.height()),
scaled)

heading_font_size = 10
if platform.system() == 'Darwin':
Expand Down
55 changes: 46 additions & 9 deletions koordinates/gui/results_panel/filter_banner.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
Publisher,
PublisherType
)
from ..user_avatar_generator import UserAvatarGenerator


class FilterBannerWidget(QWidget):
Expand All @@ -47,6 +48,7 @@ def __init__(self, parent: Optional[QWidget] = None):
super().__init__(parent)
self._icon: Optional[QImage] = None
self._background: Optional[QColor] = None
self._foreground: Optional[QColor] = None
self.setMouseTracking(True)

def set_icon(self, icon: QImage):
Expand All @@ -63,6 +65,13 @@ def set_background_color(self, color: QColor):
self._background = color
self.update()

def set_foreground_color(self, color: QColor):
"""
Sets the foreground color for the banner
"""
self._foreground = color
self.update()

def setThumbnail(self, image: QImage):
self.set_icon(image)

Expand Down Expand Up @@ -92,7 +101,10 @@ def paintEvent(self, event):
painter.setRenderHint(QPainter.Antialiasing, True)

painter.setPen(Qt.NoPen)
painter.setBrush(QBrush(QColor(255, 255, 255)))
if self._foreground:
painter.setBrush(QBrush(self._foreground))
else:
painter.setBrush(QBrush(QColor(255, 255, 255)))

rect = QRectF(option.rect)
inner_rect = rect
Expand Down Expand Up @@ -138,7 +150,11 @@ def paintEvent(self, event):
painter = QStylePainter(self)
painter.setRenderHint(QPainter.Antialiasing, True)

image = GuiUtils.get_svg_as_image('close-reversed.svg', 16, 16)
if self._background.lightnessF() < 0.5:
image = GuiUtils.get_svg_as_image('close-reversed.svg', 16, 16)
else:
image = GuiUtils.get_svg_as_image('close.svg', 16, 16)

center_y = (self.height() - image.height()) / 2
painter.drawImage(QRectF(
option.rect.right() - image.width() - self.HORIZONTAL_MARGIN,
Expand All @@ -152,41 +168,62 @@ class PublisherFilterBannerWidget(FilterBannerWidget):
Shows a publisher filter as a banner widget
"""

TEXT_LEFT_EDGE = 130
TEXT_LEFT_EDGE_PUBLISHER = 130
TEXT_LEFT_EDGE_USER = 70

def __init__(self, publisher: Publisher, parent: Optional[QWidget] = None):
super().__init__(parent)
self.publisher = publisher

self.set_background_color(self.publisher.theme.background_color())
if self.publisher.publisher_type == PublisherType.Publisher:
self.set_foreground_color(QColor(255, 255, 255))
if self.publisher.theme.background_color():
self.set_background_color(self.publisher.theme.background_color())
else:
self.set_background_color(
QColor('#555657'))
elif self.publisher.publisher_type == PublisherType.User:
self.set_foreground_color(QColor(0, 0, 0))
self.set_background_color(QColor(255, 255, 255))

if self.publisher.theme.logo():
downloadThumbnail(self.publisher.theme.logo(), self)
elif self.publisher.publisher_type == PublisherType.User:
self.set_icon(UserAvatarGenerator.get_avatar(publisher.name()))

def _draw_content(self, event):
option = QStyleOption()
option.initFrom(self)

painter = QStylePainter(self)

heading_font_size = 10
if platform.system() == 'Darwin':
if self.publisher.publisher_type == PublisherType.Publisher:
heading_font_size = 10
if platform.system() == 'Darwin':
heading_font_size = 12
else:
heading_font_size = 12
if platform.system() == 'Darwin':
heading_font_size = 14

font = self.font()
metrics = QFontMetrics(font)
font.setPointSizeF(heading_font_size)
font.setBold(True)
painter.setFont(font)

left_text_edge = option.rect.left() + self.TEXT_LEFT_EDGE

if self.publisher.publisher_type == PublisherType.Publisher:
line_heights = [1.2, 2.1]
left_text_edge = option.rect.left() + self.TEXT_LEFT_EDGE_PUBLISHER
else:
line_heights = [1.6, 0]
left_text_edge = option.rect.left() + self.TEXT_LEFT_EDGE_USER

painter.setBrush(Qt.NoBrush)
painter.setPen(QPen(QColor(255, 255, 255)))
if self._foreground:
painter.setPen(QPen(self._foreground))
else:
painter.setPen(QPen(QColor(255, 255, 255)))
painter.drawText(QPointF(left_text_edge,
option.rect.top() + int(
metrics.height() * line_heights[0])),
Expand Down
86 changes: 86 additions & 0 deletions koordinates/gui/user_avatar_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from qgis.PyQt.QtCore import (
Qt,
QRectF,
QPointF
)
from qgis.PyQt.QtGui import (
QImage,
QPainter,
QColor,
QBrush,
QPen,
QFontMetrics
)

from .gui_utils import GuiUtils


class UserAvatarGenerator:
"""
Generates user avatar thumbnails
"""

AVATAR_SIZE = 65
BACK_COLOR = QColor(255, 183, 0)
FORE_COLOR = QColor(119, 119, 119)
AVATAR_CACHE = {}

@classmethod
def get_avatar(cls, name: str) -> QImage:
"""
Returns the avatar image for the given initials
"""
initials = UserAvatarGenerator.name_to_initials(name)
if initials in cls.AVATAR_CACHE:
return cls.AVATAR_CACHE[initials]

image = UserAvatarGenerator.generate_avatar(
initials,
UserAvatarGenerator.FORE_COLOR,
UserAvatarGenerator.BACK_COLOR,
UserAvatarGenerator.AVATAR_SIZE)
cls.AVATAR_CACHE[initials] = image
return image

@staticmethod
def name_to_initials(name: str) -> str:
"""
Extracts initials from a name
"""
words = name.split()
initials = [word[0].upper() for word in words]
return ''.join(initials)[:3]

@staticmethod
def generate_avatar(initials: str,
foreground_color: QColor,
background_color: QColor,
size: int) -> QImage:
"""
Generates an avatar image
"""
image = QImage(size, size, QImage.Format_ARGB32_Premultiplied)
image.fill(Qt.transparent)

painter = QPainter(image)
painter.setRenderHint(QPainter.Antialiasing, True)

painter.setPen(Qt.NoPen)
painter.setBrush(QBrush(background_color))
painter.drawEllipse(QRectF(0, 0, image.width(), image.height()))

painter.setBrush(Qt.NoBrush)
painter.setPen(QPen(foreground_color))
font = GuiUtils.get_default_font()
font.setPixelSize(int(image.height() * 0.4))
metrics = QFontMetrics(font)

baseline = int((image.height() + metrics.capHeight()) / 2)
left = int(image.width() - metrics.horizontalAdvance(initials)) / 2
painter.setFont(font)
painter.drawText(QPointF(left, baseline),
initials)

painter.end()

return image
1 change: 1 addition & 0 deletions koordinates/icons/close.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions koordinates/icons/globe.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.