I'm trying to create a round label with border, containing an image inside. So far I tried setting a stylesheet, overlapping the round image QLabel on a bigger round QLabel with the colored background and changing it's size policy but can't get a non-mediocre result:
With parent background label (marked as FIX 2):
With stylesheet set (marked as FIX 1):
this is the MRE. in this case I use a blue background in the same dir:
"steel_blue.png"
from pathlib import Path
import sys
from PyQt5 import QtCore, QtWidgets as qtw
from PyQt5 import QtGui
from PyQt5.QtGui import QFont, QImage, QPainter, QPainterPath, QPixmap
import os
import urllib.request
RUNTIME_DIR = Path(os.path.split(sys.argv[0])[0])
class QCustomQWidget(qtw.QWidget):
def __init__(self, parent=None, video=""):
super(QCustomQWidget, self).__init__(parent)
self.textQVBoxLayout = qtw.QVBoxLayout()
self.textUpQLabel = qtw.QLabel()
font = QFont()
font.setPointSize(12)
self.textUpQLabel.setFont(font)
color_path = str(Path.joinpath(RUNTIME_DIR, 'steel_blue.png'))
base_size = 40
border_width = 2
# FIX 2 : PARENT LABEL OFFSET BY border_width
self.textDownQLabelBorder = RoundLabelImage(
path=color_path, size=base_size + 2 * border_width
)
# Overlap filled border-label with image
self.textDownQLabel = RoundLabelImage(
parent=self.textDownQLabelBorder, urlpath=video.author_thumbnail, size=base_size
)
self.textDownQLabel.move(border_width, border_width)
#test:
# self.textDownQLabelBorder = RoundLabelImage(urlpath=video.author_thumbnail, size=base_size)
self.textQVBoxLayout.addWidget(self.textUpQLabel)
self.textQVBoxLayout.addWidget(self.textDownQLabelBorder)
self.allQHBoxLayout = qtw.QHBoxLayout()
self.iconQLabel = qtw.QLabel()
self.allQHBoxLayout.addWidget(self.iconQLabel, 0)
self.allQHBoxLayout.addLayout(self.textQVBoxLayout, 1)
self.setLayout(self.allQHBoxLayout)
self.textUpQLabel.setStyleSheet('''
color: rgb(70,130,180);
''')
def setTextUp(self, text):
self.textUpQLabel.setText(text)
def setIcon(self, imagePath):
img = QPixmap(imagePath)
img = img.scaledToWidth(200)
self.iconQLabel.setPixmap(img)
self.iconQLabel.setSizePolicy(
qtw.QSizePolicy(qtw.QSizePolicy.Maximum, qtw.QSizePolicy.MinimumExpanding)
)
# TODO cut off parent border
class RoundLabelImage(qtw.QLabel):
"""Based on:
https://stackoverflow.com/questions/50819033/qlabel-with-image-in-round-shape/50821539"""
def __init__(self, *args, path="", urlpath="", size=50, antialiasing=True, **kwargs):
super().__init__(*args, **kwargs)
self.Antialiasing = antialiasing
self.setMaximumSize(size, size)
self.setMinimumSize(size, size)
self.radius = size / 2
if path != "":
p = QPixmap(path).scaled(
size, size, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation
)
elif urlpath != "":
data = urllib.request.urlopen(urlpath).read()
pixmap_author = QPixmap()
pixmap_author.loadFromData(data)
p = pixmap_author.scaled(
size, size, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation
)
##### POSSIBLE FIX FOR CHILD
if self.parent is not None:
self.target = QPixmap(self.size())
self.target.fill(QtCore.Qt.transparent)
else:
self.target = QPixmap(self.size())
self.target.fill(QtCore.Qt.transparent)
painter = QPainter(self.target)
if self.Antialiasing:
painter.setRenderHint(QPainter.Antialiasing, True)
painter.setRenderHint(QPainter.HighQualityAntialiasing, True)
painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
painter_path = QPainterPath()
if self.parent is not None:
painter_path.addRoundedRect(0, 0, self.width(), self.height(), self.radius, self.radius)
else:
painter_path.addRoundedRect(0, 0, self.width(), self.height(), self.radius, self.radius)
# FIX 1 : STYLESHEET
# self.setStyleSheet(
# f"""
# border: 2px solid blue;
# border-radius: {size/2}px;
# min-height: {size/2}px;
# min-width: {size/2}px;
# """
# )
painter.setClipPath(painter_path)
painter.drawPixmap(0, 0, p)
self.setPixmap(self.target)
class Video():
"""Store video information for ease of use
"""
def __init__(self, id, title="", time="", author="", thumbnail="", author_thumbnail=""):
self.id = str(id)
self.url = "https://www.youtube.com/watch?v=" + self.id
self.title = str(title)
self.time = str(time)
self.author = str(author)
self.thumbnail = str(thumbnail)
self.author_thumbnail = str(author_thumbnail)
class MainWindow(qtw.QWidget):
def __init__(self):
super().__init__()
self.setLayout(qtw.QVBoxLayout())
self.checkbox_dict = dict()
self.button_dict = dict()
listWidget = qtw.QListWidget()
self.layout().addWidget(listWidget)
data = {
'id':
'MpufpgwiW-0',
'url':
'https://www.youtube.com/watch?v=MpufpgwiW-0',
'title':
'Lycoriscoris - Seimei (生命)',
'time':
'2 hours ago',
'author':
'Anjunadeep',
'thumbnail':
'https://i.ytimg.com/vi/MpufpgwiW-0/hqdefault.jpg?sqp=-oaymwEbCNIBEHZIVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLDI8RjBj8u6GvY_bXeBqAOSCRNsqA',
'author_thumbnail':
'https://yt3.ggpht.com/ytc/AAUvwnie-87z6V-8oWCHVp4fn4CN17H5if1IqolvJaSL6g=s68-c-k-c0x00ffffff-no-rj',
}
my_videos = dict()
for key, val in data.items():
if key == "id":
video_id = val
my_videos[val] = Video(video_id)
else:
setattr(my_videos[video_id], key, val)
for video_id, video in my_videos.items():
myQCustomQWidget = QCustomQWidget(video=video)
myQCustomQWidget.setTextUp(video.title)
url = video.thumbnail
data = urllib.request.urlopen(url).read()
img_thumbnail = QImage()
img_thumbnail.loadFromData(data)
myQCustomQWidget.setIcon(img_thumbnail)
myQListWidgetItem = qtw.QListWidgetItem(listWidget)
myQListWidgetItem.setSizeHint(myQCustomQWidget.sizeHint())
listWidget.addItem(myQListWidgetItem)
listWidget.setItemWidget(myQListWidgetItem, myQCustomQWidget)
self.resize(800, 300)
self.show()
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
w = MainWindow()
sys.exit(app.exec())