Commit f39dcfb9 authored by Antoine Lima's avatar Antoine Lima

Player class, leaderboards (+ sorting)

parent f43cc32f
#! /bin/sh
# Modules
pyuic5 --import-from=ui ui/main.ui -o ui/main_ui.py
pyuic5 --import-from=ui ui/menu.ui -o ui/menu_ui.py
pyuic5 --import-from=ui ui/game.ui -o ui/game_ui.py
......@@ -8,4 +9,7 @@ pyuic5 --import-from=ui ui/options.ui -o ui/options_ui.py
pyuic5 --import-from=ui ui/auth2p.ui -o ui/auth2p_ui.py
pyuic5 --import-from=ui ui/leaderboard.ui -o ui/leaderboard_ui.py
# Custom widgets
pyuic5 --import-from=ui ui/playerlist.ui -o ui/playerlist_ui.py
pyrcc5 -root /ui ui/assets.qrc -o ui/assets_rc.py
......@@ -11,6 +11,7 @@ import logging
from PyQt5.QtCore import QTime, Qt
from module import Module
from player import Side
import modules
from ui.auth2p_ui import Ui_Form as Auth2pWidget
......@@ -20,21 +21,29 @@ class AuthModule(Module):
def load(self):
logging.debug('Loading AuthModule')
self.players = {Side.Left: list(), Side.Right: list()}
def unload(self):
logging.debug('Unloading AuthModule')
del self.players
def other(self, **kwargs):
logging.debug('Other AuthModule')
for key, val in kwargs.items():
if key=='ardl_rfid' or key=='ardr_rfid':
side = Side.Left if key.startswith('ardl') else Side.Right
self.players.append(Player(val))
def keyPressEvent(self, e):
if e.key() == Qt.Key_Escape:
self.ui_handleClick_btnCancel()
elif e.key() == Qt.Key_Enter or e.key() == Qt.Key_Return:
self.ui_handleClick_btnDone()
self.handleCancel()
elif e.key() == Qt.Key_Return:
self.handleDone()
def ui_handleClick_btnCancel(self):
def handleCancel(self):
self.switchModule(modules.MenuModule)
def ui_handleClick_btnDone(self):
def handleDone(self):
self.send(modules.GameModule, players=self.players)
self.switchModule(modules.GameModule)
......@@ -12,6 +12,7 @@ from PyQt5 import QtWidgets
from PyQt5.QtGui import QRegion
from PyQt5.QtCore import QTime, QTimer, QRect, Qt
from player import Side
from module import Module
import modules
from ui.endgame_ui import Ui_Form as GameWidget
......@@ -20,31 +21,50 @@ class EndGameModule(Module):
def __init__(self, parent=None):
super().__init__(parent, GameWidget())
self.screenTimeout = QTimer()
self.screenTimeout.timeout.connect(self.ui_handleClick_btnQuit)
self.screenTimeout.timeout.connect(self.handleQuit)
self.screenTimeout.setSingleShot(True)
def load(self):
logging.debug('Loading EndGameModule')
self.ui.lblP2_2.setText('Player {}'.format(self.winner+1))
if len(self.players[self.winSide])>1:
self.ui.lblP2_2.setText('{} Side'.format(self.winSide.name))
for side in [Side.Left, Side.Right]:
for player in self.players[side]:
player.victories += 1 if side==self.winSide else 0
player.goals_scored += self.scores[side]
player.time_played += self.time
player.games_played += 1
player.save()
# Quit the screen after 5 seconds if the user doesn't do it before
self.screenTimeout.start(5000)
#self.screenTimeout.start(5000)
def unload(self):
logging.debug('Unloading EndGameModule')
self.screenTimeout.stop()
self.winner = -1
del self.players
del self.winSide
def other(self, **kwargs):
logging.debug('Other EndGameModule')
if 'winner' in kwargs:
self.winner = kwargs['winner']
for key, val in kwargs.items():
if key=='players':
self.players = val
elif key=='winSide':
self.winSide = val
elif key=='scores':
self.scores = val
elif key=='time':
self.time = val
#else:
# raise ValueError('Unknown message identifier {}'.format(kwargs)
def keyPressEvent(self, e):
if e.key() == Qt.Key_Escape or e.key() == Qt.Key_Return:
self.ui_handleClick_btnQuit()
self.handleQuit()
def ui_handleClick_btnQuit(self):
def handleQuit(self):
self.switchModule(modules.MenuModule)
......@@ -15,6 +15,7 @@ from PyQt5.QtCore import QTime, QTimer, QRect, Qt, QUrl
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer
from PyQt5.QtMultimediaWidgets import QVideoWidget
from player import Side
from replay import Replay
from module import Module
import modules
......@@ -28,20 +29,20 @@ class GameOverChecker():
def check(self, time, scores):
'''
Checks if a game is over and return the winner if that's the case
Either returns -1 if the game is not finished or the highest score's index (0 or 1)
Returns the winning side or Side.Undef otherwise
Takes the game time is seconds and a list containing the two scores
'''
# Gets the index of the highest scoring player (either 0 or 1)
bestPlayer = scores.index(max(scores))
# Gets the index of the highest scoring player
bestPlayer = max(scores, key=scores.get)
if self.conditionType=='score' and scores[bestPlayer]>=self.limit:
return bestPlayer
elif self.conditionType=='time' and time>self.limit:
return bestPlayer
else:
return -1
return Side.Undef
class GameModule(Module):
def __init__(self, parent=None):
......@@ -52,8 +53,8 @@ class GameModule(Module):
self.timerUpdateChrono.timeout.connect(self.updateChrono)
# Button connections
self.ui.btnScore1.clicked.connect(lambda: self.goal(0))
self.ui.btnScore2.clicked.connect(lambda: self.goal(1))
self.ui.btnScore1.clicked.connect(lambda: self.goal(Side.Left))
self.ui.btnScore2.clicked.connect(lambda: self.goal(Side.Right))
self.replayer = Replay()
......@@ -64,9 +65,10 @@ class GameModule(Module):
self.timerUpdateChrono.start(1000)
self.ui.lcdChrono.display(QTime(0,0).toString("hh:mm:ss"))
self.showingReplay = False
self.gameoverChecker = GameOverChecker('score', 10)
self.scores = [0, 0]
self.scores = {Side.Left: 0, Side.Right: 0}
self.updateScores()
def unload(self):
......@@ -76,6 +78,9 @@ class GameModule(Module):
def other(self, **kwargs):
logging.debug('Other GameModule')
if 'players' in kwargs:
self.players = kwargs['players']
def resizeEvent(self, event):
# 40% of the window width to have (5% margin)-(40% circle)-(10% middle)-(40% circle)-(5% margin)
......@@ -90,11 +95,11 @@ class GameModule(Module):
def keyPressEvent(self, e):
if e.key() == Qt.Key_Escape:
self.ui_handleClick_btnCancel()
self.handleCancel()
elif e.key() == Qt.Key_Left:
self.goal(0)
self.goal(Side.Left)
elif e.key() == Qt.Key_Right:
self.goal(1)
self.goal(Side.Right)
def updateChrono(self):
# Updated each second
......@@ -108,30 +113,32 @@ class GameModule(Module):
return self.gameStartTime.secsTo(QTime.currentTime())
def updateScores(self):
self.ui.btnScore1.setText(str(self.scores[0]))
self.ui.btnScore2.setText(str(self.scores[1]))
self.ui.btnScore1.setText(str(self.scores[Side.Left]))
self.ui.btnScore2.setText(str(self.scores[Side.Right]))
self.checkEndGame()
def goal(self, side):
if side!=0 and side!=1:
if side not in Side:
logging.error('Wrong goal side: {}'.format(side))
else:
self.scores[side] += 1
# Show replay
# May require `sudo apt-get install qtmultimedia5-examples` in order to install the right libraries
replayFile = self.mainwin.getContent("replay{}.mp4".format(side))
replayFile = self.mainwin.getContent('Replay {}.mp4'.format(side.name))
if os.path.exists(replayFile):
self.showingReplay = True
self.player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.player.stateChanged.connect(self.endOfReplay)
self.player.setMuted(True)
self.player.setVideoOutput(self.ui.videoWidget)
self.player.setMedia(QMediaContent(QUrl.fromLocalFile(replayFile)))
self.player.play()
self.ui.videoWidget.setFullScreen(True)
if True: # Debug Mode
self.updateScores()
else:
self.showingReplay = True
self.player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.player.stateChanged.connect(self.endOfReplay)
self.player.setMuted(True)
self.player.setVideoOutput(self.ui.videoWidget)
self.player.setMedia(QMediaContent(QUrl.fromLocalFile(replayFile)))
self.player.play()
self.ui.videoWidget.setFullScreen(True)
def endOfReplay(self, status):
if status!=QMediaPlayer.PlayingState:
......@@ -140,12 +147,12 @@ class GameModule(Module):
self.updateScores()
def ui_handleClick_btnCancel(self):
def handleCancel(self):
self.switchModule(modules.MenuModule)
def checkEndGame(self):
win = self.gameoverChecker.check(self.getGameTime(), self.scores)
winSide = self.gameoverChecker.check(self.getGameTime(), self.scores)
if win>=0:
self.send(modules.EndGameModule, winner=win)
if winSide!=Side.Undef:
self.send(modules.EndGameModule, players=self.players, winSide=winSide, scores=self.scores, time=self.getGameTime())
self.switchModule(modules.EndGameModule)
......@@ -7,29 +7,117 @@ Created on Wed Apr 18 18:34:40 2018
"""
import logging
from operator import attrgetter
from PyQt5.QtCore import QTime, Qt
from PyQt5 import QtWidgets
from PyQt5.QtCore import QTime, Qt, QSize, QItemSelectionModel
from module import Module
import modules
from player import PlayerGuest, Player
from ui.leaderboard_ui import Ui_Form as LeaderboardWidget
from ui.playerlist_ui import Ui_Form as PlayerListWidget
class LeaderboardItemWidget(QtWidgets.QWidget):
def __init__(self, parent, player):
QtWidgets.QWidget.__init__(self, parent)
self.ui = PlayerListWidget()
self.ui.setupUi(self)
self.ui.picHolder.setStyleSheet('border-image: url({});'.format(player.pic_path))
self.ui.lblFName.setText(player.fname)
self.ui.lblLName.setText(player.lname)
self.ui.lblVictories.setText (self.ui.lblVictories.text().replace('####', str(player.stats.victories)))
self.ui.lblGamesPlayed.setText (self.ui.lblGamesPlayed.text().replace('####', str(player.stats.games_played)))
self.ui.lblGoalsScored.setText (self.ui.lblGoalsScored.text().replace('####', str(player.stats.goals_scored)))
self.ui.lblMinutesPlayed.setText(self.ui.lblMinutesPlayed.text().replace('####', str(player.stats.time_played)))
self.ui.pushButton.clicked.connect(lambda: logging.debug('clicked'))
class LeaderboardModule(Module):
def __init__(self, parent):
super().__init__(parent, LeaderboardWidget())
self.players = []
self.ui.rbName.clicked.connect(lambda: self.changeSort(self.ui.rbName))
self.ui.rbVictories.clicked.connect(lambda: self.changeSort(self.ui.rbVictories))
self.ui.rbScore.clicked.connect(lambda: self.changeSort(self.ui.rbScore))
self.ui.rbGamesPlayed.clicked.connect(lambda: self.changeSort(self.ui.rbGamesPlayed))
self.ui.rbTimePlayed.clicked.connect(lambda: self.changeSort(self.ui.rbTimePlayed))
self.selectedSort = 0
self.sortMethodRB = [self.ui.rbName, self.ui.rbVictories, self.ui.rbScore, self.ui.rbGamesPlayed, self.ui.rbTimePlayed]
self.sortMethodAttr = ['lname', 'stats.victories', 'stats.goals_scored', 'stats.games_played', 'stats.time_played']
self.sortMethodRB[self.selectedSort].setChecked(True)
def load(self):
logging.debug('Loading LeaderboardModule')
self.loadList()
def unload(self):
logging.debug('Unloading LeaderboardModule')
self.players = []
def other(self, **kwargs):
logging.debug('Other LeaderboardModule')
def changeSort(self, rbSort):
self.selectedSort = self.sortMethodRB.index(rbSort)
self.loadList()
def loadList(self):
if self.players:
self.ui.listWidget.clear()
else:
# Load from DB
dummy1 = Player(0, 'A', 'E', ':ui/img/placeholder_head.jpg')
dummy2 = Player(1, 'B', 'D', ':ui/img/placeholder_head.jpg')
dummy3 = Player(2, 'C', 'C', ':ui/img/placeholder_head.jpg')
dummy4 = Player(3, 'D', 'B', ':ui/img/placeholder_head.jpg')
dummy5 = Player(4, 'E', 'A', ':ui/img/placeholder_head.jpg')
dummy2.stats.victories = 1
dummy3.stats.time_played = 1
dummy4.stats.games_played = 1
dummy5.stats.goals_scored = 1
self.players = [PlayerGuest, dummy1, dummy2, dummy3, dummy4, dummy5]
self.players.sort(key=attrgetter(self.sortMethodAttr[self.selectedSort]), reverse=True)
for player in self.players:
item = QtWidgets.QListWidgetItem()
playerWidget = LeaderboardItemWidget(self.ui.listWidget, player)
item.setSizeHint(playerWidget.size())
self.ui.listWidget.addItem(item)
self.ui.listWidget.setItemWidget(item, playerWidget)
self.ui.listWidget.setCurrentRow(0, QItemSelectionModel.Select)
def keyPressEvent(self, e):
curRow = self.ui.listWidget.currentRow()
curSort = self.selectedSort
if e.key() == Qt.Key_Escape:
self.ui_handleClick_btnExit()
self.handleExit()
elif e.key() == Qt.Key_Up:
newRow = curRow-1 if curRow!=0 else self.ui.listWidget.count()-1
self.ui.listWidget.setCurrentRow(newRow, QItemSelectionModel.SelectCurrent)
elif e.key() == Qt.Key_Down:
newRow = curRow+1 if curRow!=self.ui.listWidget.count()-1 else 0
self.ui.listWidget.setCurrentRow(newRow, QItemSelectionModel.SelectCurrent)
elif e.key() == Qt.Key_Left:
newSort = curSort-1 if curSort!=0 else len(self.sortMethodRB)-1
self.sortMethodRB[newSort].animateClick()
elif e.key() == Qt.Key_Right:
newSort = curSort+1 if curSort!=len(self.sortMethodRB)-1 else 0
self.sortMethodRB[newSort].animateClick()
def ui_handleClick_btnExit(self):
def handleExit(self):
self.switchModule(modules.MenuModule)
......@@ -39,7 +39,7 @@ class MenuModule(Module):
def keyPressEvent(self, e):
if e.key() == Qt.Key_Escape:
self.ui_handleClick_btnExit()
self.handleExit()
elif e.key() == Qt.Key_Up:
self.parent().focusPreviousChild()
elif e.key() == Qt.Key_Down:
......@@ -50,6 +50,6 @@ class MenuModule(Module):
else:
QApplication.focusWidget().animateClick()
def ui_handleClick_btnExit(self):
def handleExit(self):
logging.info('Closing..')
self.mainwin.close()
......@@ -21,8 +21,8 @@ class OptionsModule(Module):
super().__init__(parent, OptionsWidget())
# Button connections
self.ui.btnSave.clicked.connect(self.ui_handleClick_btnSave)
self.ui.btnBack.clicked.connect(self.ui_handleClick_btnBack)
self.ui.btnSave.clicked.connect(self.handleSave)
self.ui.btnBack.clicked.connect(self.handleBack)
def load(self):
logging.debug('Loading OptionsModule')
......@@ -43,15 +43,15 @@ class OptionsModule(Module):
def keyPressEvent(self, e):
if e.key() == Qt.Key_Escape:
self.ui_handleClick_btnBack()
self.handleBack()
elif e.key() == Qt.Key_Return:
self.ui_handleClick_btnSave()
self.handleSave()
def ui_handleClick_btnSave(self):
def handleSave(self):
Settings['ui.fullscreen'] = self.ui.options.cellWidget(0, 1).currentText().lower() == 'true'
self.mainwin._refreshAfterSettings()
self.switchModule(modules.MenuModule)
def ui_handleClick_btnBack(self):
def handleBack(self):
# ToDo: Maybe add a warning
self.switchModule(modules.MenuModule)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne
"""
import logging
from enum import Enum
class Side(Enum):
Undef = 0
Left = 1
Right = 2
class Player():
def __init__(self, id):
fname, lname, pic_url = '','','' # Replace with DB calls
self.__init__(id, fname, lname, pic_url)
def __init__(self, id, fname, lname, pic_path):
self.id = id
self.fname = fname
self.lname = lname
self.pic_path = pic_path
self.stats = Stat(id)
def save(self):
'''
Update or create the player in database
'''
# TODO
pass
@property
def name(self):
return self.lname.upper() + self.fname
@property
def pic(self):
return QPixmap(self.pic_path)
class Stat():
def __init__(self, player_id):
self.victories = 0
self.time_played = 0
self.games_played = 0
self.goals_scored = 0
if player_id >= 0:
self.victories = 0
self.time_played = 0
self.games_played = 0
self.goals_scored = 0
PlayerGuest = Player(-1, 'Guest', '', ':ui/img/placeholder_head.jpg')
......@@ -6,33 +6,41 @@ Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne
"""
import picamera
import settings import Settings
import os
from settings import Settings
onRasp = os.uname()[1] == 'raspberrypi'
if onRasp:
import picamera
class Replay():
def __init__(self):
self.cam = picamera.PiCamera()
self.cam.resolution = Settings['picam.resolution']
self.cam.framerate = Settings['picam.fps']
self.cam.hflip = Settings['picam.hflip']
self.cam.vflip = Settings['picam.vflip']
self.format = Settings['picam.format']
self.continue_recording = False
self.stream = picamera.PiCameraCircularIO(self.cam, seconds=Settings['replay.duration'])
if onRasp:
self.cam = picamera.PiCamera()
self.cam.resolution = Settings['picam.resolution']
self.cam.framerate = Settings['picam.fps']
self.cam.hflip = Settings['picam.hflip']
self.cam.vflip = Settings['picam.vflip']
self.format = Settings['picam.format']
self.continue_recording = False
self.stream = picamera.PiCameraCircularIO(self.cam, seconds=Settings['replay.duration'])
def capture(self, fileToSave):
self.cam.start_recording(self.stream, self.format)
self.continue_recording = True
try:
while self.continue_recording:
self.cam.wait_recording(1)
finally:
self.cam.stop_recording()
if onRasp:
self.cam.start_recording(self.stream, self.format)
self.continue_recording = True
self.stream.copy_to(fileToSave)
self.cam.close()
self.stream.close()
try:
while self.continue_recording:
self.cam.wait_recording(1)
finally:
self.cam.stop_recording()
self.stream.copy_to(fileToSave)
self.cam.close()
self.stream.close()
def stop():
self.continue_recording = False
<RCC>
<qresource prefix="ui">
<file>img/icons/red-cross.png</file>
<file>img/bg/bg1.jpg</file>
<file>img/bg/bg2.png</file>
<file>img/bg/bg3.jpg</file>
......
......@@ -108,7 +108,53 @@
</spacer>
</item>
<item>
<widget class="QRadioButton" name="radioButton_3">
<widget class="QRadioButton" name="rbName">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QRadioButton" name="rbVictories">
<property name="text">
<string>Victories</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_10">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QRadioButton" name="rbScore">
<property name="text">
<string>Score</string>
</property>
......@@ -131,9 +177,9 @@
</spacer>
</item>
<item>
<widget class="QRadioButton" name="radioButton_2">
<widget class="QRadioButton" name="rbGamesPlayed">
<property name="text">
<string>Time Played</string>
<string>Games Played</string>
</property>
</widget>
</item>
......@@ -154,12 +200,25 @@
</spacer>
</item>
<item>
<widget class="QRadioButton" name="radioButton">
<widget class="QRadioButton" name="rbTimePlayed">
<property name="text">
<string>Games Played</string>
<string>Time Played</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<spacer name="horizontalSpacer_8">
<property name="orientation">
......@@ -220,7 +279,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QTableWidget" name="tableWidget"/>
<widget class="QListWidget" name="listWidget"/>
</item>
</layout>
</widget>
......@@ -228,6 +287,16 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>rbName</tabstop>
<tabstop>rbVictories</tabstop>
<tabstop>rbScore</tabstop>
<tabstop>rbGamesPlayed</tabstop>
<tabstop>rbTimePlayed</tabstop>
<tabstop>comboBox</tabstop>
<tabstop>listWidget</tabstop>
<tabstop>scrollArea</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1270</width>
<height>150</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>50</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QWidget" name="picHolder" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>125</height>