...
 
Commits (3)
......@@ -75,7 +75,7 @@ class Database():
self._connection.commit()
def delete_playerpic(self, playerID):
self._cursor.execute('UPDATE Players SET pic=null WHERE id==?', (playerID,))
self._cursor.execute('UPDATE Players SET login=null WHERE id==?', (playerID,))
self._connection.commit()
def make_player_private(self, playerID):
......
......@@ -46,9 +46,13 @@ class Downloader(Thread, QObject):
if nAttemps==None:
nAttemps = Downloader.N_ATTEMPS
print('Adding "{}". {} before it'.format(url_in, len(self._request_stack)))
self._request_stack.append((url_in, uri_out, nAttemps))
# Add if not already queued
req = (url_in, uri_out, nAttemps)
if req not in self._request_stack:
logging.debug('Adding "{}". {} before it'.format(url_in, len(self._request_stack)))
self._request_stack.append(req)
else:
logging.debug('"{}" is already queued, ignoring.'.format(url_in))
def run(self):
while not self._close:
......
......@@ -49,7 +49,7 @@ class Input(QObject):
def __init__(self):
QObject.__init__(self)
if ON_RASP:
GPIO.setmode(GPIO.BCM)
for pin in Input._keyButtonBindings.keys():
......@@ -62,12 +62,14 @@ class Input(QObject):
self.goalThread = GoalThread(self, **Input._GoalPins)
def start(self):
self.rfidThread.start()
self.goalThread.start()
if ON_RASP:
self.rfidThread.start()
self.goalThread.start()
def stop(self):
self.rfidThread.stop(); self.rfidThread.join()
self.goalThread.stop(); self.goalThread.join()
if ON_RASP:
self.rfidThread.stop(); self.rfidThread.join()
self.goalThread.stop(); self.goalThread.join()
def _handleButtonPress(self, button_pin):
if button_pin not in Input._keyButtonBindings.keys():
......
......@@ -4,6 +4,7 @@
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne
"""
import os
import logging
from enum import Enum
from http import HTTPStatus
......@@ -13,6 +14,7 @@ from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QDialog, QApplication
from Babyfut.babyfut import getMainWin, IMG_PATH
from Babyfut.core.downloader import Downloader
from Babyfut.core.ginger import Ginger
from Babyfut.core.database import Database, DatabaseError
from Babyfut.ui.consent_dialog_ui import Ui_Dialog as ConsentDialogUI
......@@ -57,19 +59,24 @@ class ConsentDialog(QDialog):
self.reject()
class Player(QObject):
__query_infos = 'SELECT id, fname, lname, pic FROM Players WHERE rfid==?'
__query_infos = 'SELECT id, login, fname, lname FROM Players WHERE rfid==?'
__query_time_goals_games = 'SELECT SUM(Matchs.duration) AS timePlayed, SUM(Teams.nGoals) AS goalsScored, COUNT(*) AS gamesPlayed FROM Teams INNER JOIN Matchs ON (Teams.id==Matchs.winningTeam OR Teams.id==Matchs.losingTeam) WHERE (Teams.player1==? OR player2==?)'
__query_victories = 'SELECT COUNT(*) AS victories FROM Players INNER JOIN Teams ON (Players.id==Teams.player1 OR Players.id==Teams.player2) INNER JOIN Matchs ON (Teams.id==Matchs.winningTeam) WHERE Players.id==?'
_placeholder_pic_path = ':ui/img/placeholder_head.jpg'
_utcPictureURL = 'https://demeter.utc.fr/portal/pls/portal30/portal30.get_photo_utilisateur?username={}'
def __init__(self, id, rfid, fname, lname, pic_path, stats=None):
def __init__(self, id, rfid, login, fname, lname, stats=None):
QObject.__init__(self)
self.id = id
self.rfid = rfid
self.login = login
self.fname = fname
self.lname = lname
self.pic_path = pic_path if pic_path else Player._placeholder_pic_path # Default pic if None
self.pic_path = Player._placeholder_pic_path
if self.login:
self.pic_path = Player._utcPictureURL.format(self.login)
if stats==None:
self.stats = { 'time_played': 0, 'goals_scored': 0, 'games_played': 0, 'victories': 0 }
......@@ -89,7 +96,7 @@ class Player(QObject):
if consentDialog.result()==QDialog.Accepted:
player = Player._loadFromAPI(rfid)
else:
logging.debug('Consent refused when retrieving a player, returning Guest')
logging.info('Consent refused when retrieving a player, returning Guest')
player = PlayerGuest
return player
......@@ -99,7 +106,7 @@ class Player(QObject):
db = Database.instance()
try:
# Retrieve generic informations
id, fname, lname, pic = db.select_one(Player.__query_infos, rfid)
id, login, fname, lname = db.select_one(Player.__query_infos, rfid)
# Retrieve stats
stats = {}
......@@ -110,7 +117,7 @@ class Player(QObject):
if val==None:
stats[key] = 0
return Player(id, rfid, fname, lname, pic, stats)
return Player(id, rfid, login, fname, lname, stats)
except DatabaseError as e:
logging.warn('DB Error: {}'.format(e))
......@@ -123,11 +130,11 @@ class Player(QObject):
'''
response = Ginger.instance.get('badge/{}'.format(rfid))
if isinstance(response, HTTPStatus):
logging.debug('Request to Ginger failed ({}): returning Guest'.format(response.value))
logging.warn('Request to Ginger failed ({}): returning Guest'.format(response.value))
return PlayerGuest
else:
infos = json.loads(response)
Database.instance().insert_player(infos['rfid'], infos['nom'], infos['prenom'])
Database.instance().insert_player(infos['rfid'], infos['login'], infos['nom'], infos['prenom'])
return Player._loadFromDB(rfid)
......@@ -135,24 +142,28 @@ class Player(QObject):
self.pic_container = container_widget
if self.pic_path.startswith('http'):
# Download from the internet
# Download from the internet but display a temporary image between
self.pic_container.setStyleSheet('border-image: url({});'.format(Player._placeholder_pic_path))
Downloader.instance().request(self.pic_path, os.path.join(IMG_PATH, '{}.jpg'.format(self.id)))
Downloader.instance().finished.connect(self._downloader_callback)
else:
# Already downloaded and stored locally
self.pic_container.setStyleSheet('border-image: url({});'.format(self.pic_path))
self._forceWidgetUpdate()
QApplication.processEvents()
@pyqtSlot(str)
def _downloader_callback(self, path):
self.pic_path = path
Downloader.instance().finished.disconnect(self._downloader_callback)
self.displayImg(self.pic_container)
# Take the callback if not already done and we are the targer
if IMG_PATH in path and str(self.id) in path and IMG_PATH not in self.pic_path:
self.pic_path = path
Downloader.instance().finished.disconnect(self._downloader_callback)
if self.pic_container!=None:
self.displayImg(self.pic_container)
def forgetPicture(self):
self.pic_path = Player._placeholder_pic_path
self.login = None
Database.instance().delete_playerpic(self.id)
def make_private(self):
......
......@@ -3,37 +3,37 @@
<context>
<name>Dialog</name>
<message>
<location filename="delete_dialog_ui.py" line="70"/>
<location filename="../ui/delete_dialog_ui.py" line="70"/>
<source>Dialog</source>
<translation>Supression</translation>
</message>
<message>
<location filename="consent_dialog_ui.py" line="39"/>
<location filename="../ui/consent_dialog_ui.py" line="39"/>
<source>Consent Approval Needed</source>
<translation>Validation de Consentement</translation>
</message>
<message>
<location filename="delete_dialog_ui.py" line="71"/>
<location filename="../ui/delete_dialog_ui.py" line="71"/>
<source>Changing {}&apos;s profile</source>
<translation>Modification du profile de {}</translation>
</message>
<message>
<location filename="delete_dialog_ui.py" line="72"/>
<location filename="../ui/delete_dialog_ui.py" line="72"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;Select an option&lt;br/&gt;then validate by passing your badge on the table&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;Choisissez une option&lt;br/&gt;et validez en passant votre carte sur la table&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="delete_dialog_ui.py" line="73"/>
<location filename="../ui/delete_dialog_ui.py" line="73"/>
<source>Delete all records</source>
<translation>Supprimer toutes les données</translation>
</message>
<message>
<location filename="delete_dialog_ui.py" line="74"/>
<location filename="../ui/delete_dialog_ui.py" line="74"/>
<source>Remove the Picture</source>
<translation>Suprrimer la photo</translation>
</message>
<message>
<location filename="delete_dialog_ui.py" line="75"/>
<location filename="../ui/delete_dialog_ui.py" line="75"/>
<source>Make the accout private</source>
<translation>Rendre privé</translation>
</message>
......@@ -41,210 +41,210 @@
<context>
<name>Form</name>
<message>
<location filename="privacy_ui.py" line="52"/>
<location filename="../ui/privacy_ui.py" line="52"/>
<source>Form</source>
<translation>Form</translation>
</message>
<message>
<location filename="authquick_ui.py" line="235"/>
<location filename="../ui/authquick_ui.py" line="222"/>
<source>Identify yourselves! </source>
<translation>Identifiez vous ! </translation>
</message>
<message>
<location filename="authquick_ui.py" line="236"/>
<location filename="../ui/authquick_ui.py" line="223"/>
<source>Use the table&apos;s RFID reader with your card.</source>
<translation>Utilisez les lecteurs RFID de la table avec votre carte étu.</translation>
</message>
<message>
<location filename="playerlist_ui.py" line="95"/>
<location filename="../ui/playerlist_ui.py" line="95"/>
<source>Name</source>
<translation>Nom</translation>
</message>
<message>
<location filename="authleague_ui.py" line="148"/>
<location filename="../ui/authleague_ui.py" line="138"/>
<source>Stat</source>
<translation>Stat</translation>
</message>
<message>
<location filename="endgame_ui.py" line="106"/>
<location filename="../ui/endgame_ui.py" line="92"/>
<source>Player 1</source>
<translation>Joueur 1</translation>
</message>
<message>
<location filename="endgame_ui.py" line="107"/>
<location filename="../ui/endgame_ui.py" line="93"/>
<source>Player 2</source>
<translation>Joueur 2</translation>
</message>
<message>
<location filename="authquick_ui.py" line="239"/>
<location filename="../ui/authquick_ui.py" line="226"/>
<source>vs.</source>
<translation>vs .</translation>
</message>
<message>
<location filename="authquick_ui.py" line="240"/>
<location filename="../ui/authquick_ui.py" line="227"/>
<source>Player 3</source>
<translation>Joueur 3</translation>
</message>
<message>
<location filename="authquick_ui.py" line="241"/>
<location filename="../ui/authquick_ui.py" line="228"/>
<source>Player 4</source>
<translation>Joueur 4</translation>
</message>
<message>
<location filename="endgame_ui.py" line="105"/>
<location filename="../ui/endgame_ui.py" line="91"/>
<source>Congratulations!</source>
<translation>Félicitations !</translation>
</message>
<message>
<location filename="game_ui.py" line="79"/>
<location filename="../ui/game_ui.py" line="79"/>
<source>0</source>
<translation>0</translation>
</message>
<message>
<location filename="menu_ui.py" line="137"/>
<location filename="../ui/menu_ui.py" line="137"/>
<source>Leaderboard</source>
<translation>Classements</translation>
</message>
<message>
<location filename="leaderboard_ui.py" line="110"/>
<location filename="../ui/leaderboard_ui.py" line="109"/>
<source>Sort By</source>
<translation>Trier Par</translation>
</message>
<message>
<location filename="leaderboard_ui.py" line="112"/>
<location filename="../ui/leaderboard_ui.py" line="111"/>
<source>Victories</source>
<translation>Victoires</translation>
</message>
<message>
<location filename="leaderboard_ui.py" line="113"/>
<location filename="../ui/leaderboard_ui.py" line="112"/>
<source>Score</source>
<translation>Score</translation>
</message>
<message>
<location filename="leaderboard_ui.py" line="114"/>
<location filename="../ui/leaderboard_ui.py" line="113"/>
<source>Games Played</source>
<translation>Parties Jouées</translation>
</message>
<message>
<location filename="leaderboard_ui.py" line="115"/>
<location filename="../ui/leaderboard_ui.py" line="114"/>
<source>Time Played</source>
<translation>Temps Joué</translation>
</message>
<message>
<location filename="leaderboard_ui.py" line="116"/>
<location filename="../ui/leaderboard_ui.py" line="115"/>
<source>Jump to</source>
<translation>Sauter à</translation>
</message>
<message>
<location filename="menu_ui.py" line="134"/>
<location filename="../ui/menu_ui.py" line="134"/>
<source>Babyf&apos; UT</source>
<translation>Babyf&apos;UT</translation>
</message>
<message>
<location filename="menu_ui.py" line="135"/>
<location filename="../ui/menu_ui.py" line="135"/>
<source>Start Quick Game</source>
<translation>Partie Rapide</translation>
</message>
<message>
<location filename="menu_ui.py" line="136"/>
<location filename="../ui/menu_ui.py" line="136"/>
<source>Start League Mode</source>
<translation>Championnat</translation>
</message>
<message>
<location filename="options_ui.py" line="219"/>
<location filename="../ui/options_ui.py" line="219"/>
<source>Options</source>
<translation>Options</translation>
</message>
<message>
<location filename="options_ui.py" line="220"/>
<location filename="../ui/options_ui.py" line="220"/>
<source>Game Over Condition</source>
<translation>Condition de Fin de Jeu</translation>
</message>
<message>
<location filename="options_ui.py" line="221"/>
<location filename="../ui/options_ui.py" line="221"/>
<source>By Score</source>
<translation>Par Score</translation>
</message>
<message>
<location filename="options_ui.py" line="222"/>
<location filename="../ui/options_ui.py" line="222"/>
<source>By Time</source>
<translation>Par Temps</translation>
</message>
<message>
<location filename="options_ui.py" line="223"/>
<location filename="../ui/options_ui.py" line="223"/>
<source>{} minutes</source>
<translation>{} minutes</translation>
</message>
<message>
<location filename="options_ui.py" line="224"/>
<location filename="../ui/options_ui.py" line="224"/>
<source>League - Number of players per side</source>
<translation>Championnat - Nombre de joueurs par coté</translation>
</message>
<message>
<location filename="options_ui.py" line="225"/>
<location filename="../ui/options_ui.py" line="225"/>
<source>1</source>
<translation>1</translation>
</message>
<message>
<location filename="options_ui.py" line="226"/>
<location filename="../ui/options_ui.py" line="226"/>
<source>2</source>
<translation>2</translation>
</message>
<message>
<location filename="options_ui.py" line="227"/>
<location filename="../ui/options_ui.py" line="227"/>
<source>Language</source>
<translation>Langage</translation>
</message>
<message>
<location filename="options_ui.py" line="228"/>
<location filename="../ui/options_ui.py" line="228"/>
<source>English</source>
<translation>English</translation>
</message>
<message>
<location filename="playerlist_ui.py" line="96"/>
<location filename="../ui/playerlist_ui.py" line="96"/>
<source>Surname</source>
<translation>Prénom</translation>
</message>
<message>
<location filename="privacy_ui.py" line="53"/>
<location filename="../ui/privacy_ui.py" line="53"/>
<source>Privacy</source>
<translation>Vie Privée</translation>
</message>
<message>
<location filename="authleague.py" line="51"/>
<location filename="../modules/authleague.py" line="51"/>
<source>{} Victories</source>
<translation>{} Victoires</translation>
</message>
<message>
<location filename="authleague.py" line="52"/>
<location filename="../modules/authleague.py" line="52"/>
<source>{} Games Played</source>
<translation>{} Parties Jouées</translation>
</message>
<message>
<location filename="authleague.py" line="53"/>
<location filename="../modules/authleague.py" line="53"/>
<source>{} Goals Scored</source>
<translation>{} Buts Marqués</translation>
</message>
<message>
<location filename="playerlist_ui.py" line="97"/>
<location filename="../ui/playerlist_ui.py" line="97"/>
<source>{} Victories</source>
<comment>Form</comment>
<translation>{} Victoires</translation>
</message>
<message>
<location filename="playerlist_ui.py" line="98"/>
<location filename="../ui/playerlist_ui.py" line="98"/>
<source>{} Goals Scored</source>
<comment>Form</comment>
<translation>{} Buts Marqués</translation>
</message>
<message>
<location filename="playerlist_ui.py" line="99"/>
<location filename="../ui/playerlist_ui.py" line="99"/>
<source>{} Games Played</source>
<comment>Form</comment>
<translation>{} Parties Jouées</translation>
</message>
<message>
<location filename="playerlist_ui.py" line="100"/>
<location filename="../ui/playerlist_ui.py" line="100"/>
<source>{} Minutes Played</source>
<comment>Form</comment>
<translation>{} Minutes Jouées</translation>
......@@ -255,7 +255,7 @@
<translation type="obsolete">Français</translation>
</message>
<message encoding="UTF-8">
<location filename="options_ui.py" line="229"/>
<location filename="../ui/options_ui.py" line="229"/>
<source>Français</source>
<translation type="unfinished"></translation>
</message>
......@@ -263,7 +263,7 @@
<context>
<name>MainWindow</name>
<message>
<location filename="main_ui.py" line="83"/>
<location filename="../ui/main_ui.py" line="83"/>
<source>Babyfoot</source>
<translation>Babyf&apos;UT</translation>
</message>
......@@ -271,7 +271,7 @@
<context>
<name>consent</name>
<message>
<location filename="privacy.py" line="18"/>
<location filename="../modules/privacy.py" line="18"/>
<source>&lt;p&gt;
<byte value="x9"/><byte value="x9"/>This software uses personnal information in accordance to GDPR, such as:
<byte value="x9"/><byte value="x9"/>&lt;ul&gt;
......
......@@ -97,36 +97,15 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="img">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<widget class="QWidget" name="img" native="true">
<property name="minimumSize">
<size>
<width>300</width>
<height>300</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="assets.qrc">:/ui/img/placeholder_head.jpg</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<property name="styleSheet">
<string notr="true">border-image: url(:/ui/img/placeholder_head.jpg);</string>
</property>
</widget>
</item>
......
......@@ -107,9 +107,9 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="imgP1">
<widget class="QWidget" name="imgP1" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
......@@ -126,17 +126,8 @@
<height>200</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="assets.qrc">:/ui/img/placeholder_head.jpg</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<property name="styleSheet">
<string notr="true">border-image: url(:/ui/img/placeholder_head.jpg);</string>
</property>
</widget>
</item>
......@@ -185,9 +176,9 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="imgP2">
<widget class="QWidget" name="imgP2" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
......@@ -204,14 +195,8 @@
<height>200</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="assets.qrc">:/ui/img/placeholder_head.jpg</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
<property name="styleSheet">
<string notr="true">border-image: url(:/ui/img/placeholder_head.jpg);</string>
</property>
</widget>
</item>
......@@ -337,9 +322,9 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="imgP3">
<widget class="QWidget" name="imgP3" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
......@@ -356,14 +341,8 @@
<height>200</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="assets.qrc">:/ui/img/placeholder_head.jpg</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
<property name="styleSheet">
<string notr="true">border-image: url(:/ui/img/placeholder_head.jpg);</string>
</property>
</widget>
</item>
......@@ -412,9 +391,9 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLabel" name="imgP4">
<widget class="QWidget" name="imgP4" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
......@@ -431,14 +410,8 @@
<height>200</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="assets.qrc">:/ui/img/placeholder_head.jpg</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
<property name="styleSheet">
<string notr="true">border-image: url(:/ui/img/placeholder_head.jpg);</string>
</property>
</widget>
</item>
......
......@@ -69,13 +69,7 @@
<widget class="QWidget" name="widgetLayoutP1" native="true">
<layout class="QVBoxLayout" name="layoutP1">
<item>
<widget class="QLabel" name="imgP1">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<widget class="QWidget" name="imgP1" native="true">
<property name="minimumSize">
<size>
<width>400</width>
......@@ -84,18 +78,18 @@
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>300</height>
<width>400</width>
<height>400</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="assets.qrc">:/ui/img/placeholder_head.jpg</pixmap>
<property name="baseSize">
<size>
<width>400</width>
<height>400</height>
</size>
</property>
<property name="scaledContents">
<bool>true</bool>
<property name="styleSheet">
<string notr="true">border-image: url(:/ui/img/placeholder_head.jpg);</string>
</property>
</widget>
</item>
......@@ -159,13 +153,7 @@
<widget class="QWidget" name="widgetLayoutP2" native="true">
<layout class="QVBoxLayout" name="layoutP2">
<item>
<widget class="QLabel" name="imgP2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<widget class="QWidget" name="ImgP2" native="true">
<property name="minimumSize">
<size>
<width>400</width>
......@@ -174,18 +162,18 @@
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>300</height>
<width>400</width>
<height>400</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="assets.qrc">:/ui/img/placeholder_head.jpg</pixmap>
<property name="baseSize">
<size>
<width>400</width>
<height>400</height>
</size>
</property>
<property name="scaledContents">
<bool>true</bool>
<property name="styleSheet">
<string notr="true">border-image: url(:/ui/img/placeholder_head.jpg);</string>
</property>
</widget>
</item>
......