...
 
Commits (3)
/__pycache__/ /__pycache__/
*/__pycache__/ */__pycache__/
/.spyproject/
*/*.log */*.log
*/*_ui.py */*_ui.py
*/*_rc.py */*_rc.py
[codestyle]
indentation = True
[main]
version = 0.1.0
[encoding]
text_encoding = utf-8
[main]
version = 0.1.0
[vcs]
use_version_control = False
version_control_system =
[main]
version = 0.1.0
[workspace]
restore_data_on_startup = True
save_data_on_exit = True
save_history = True
save_non_project_files = False
[main]
version = 0.1.0
recent_files = ['/home/antoine/prog/PR/source-rasp/main.py', '/home/antoine/prog/PR/source-rasp/modules/game.py', '/home/antoine/prog/PR/content/settings.json', '/home/antoine/prog/PR/source-rasp/replay.py', '/home/antoine/prog/PR/source-rasp/com.py']
...@@ -103,4 +103,4 @@ For the time being, those are provided by the PI camera module. ...@@ -103,4 +103,4 @@ For the time being, those are provided by the PI camera module.
## Todo ## Todo
* [ ] Filter serial ports to get arduinos or specific serial id * [ ] Add the possibility to stop the replay mid-video by pushing any button
#!/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
import sqlite3
class DatabaseError(Exception):
pass
class Database():
__db = None
def __init__(self):
if not Database.__db:
from main import MainWin
db_path = MainWin.getContent('babyfut.sqlite')
self._connection = sqlite3.connect(db_path)
@staticmethod
def instance():
'''
Singleton
'''
if not Database.__db:
Database.__db = Database()
return Database.__db
@property
def _cursor(self):
return self._connection.cursor()
def select_one(self, query, *args):
res = self._cursor.execute(query, args).fetchone()
if not res:
raise DatabaseError('Query \"{}\" returned nothing with args {}'.format(query, args))
return res
def select_guest_team(self):
return self.select_one('SELECT id FROM players WHERE fname LIKE "guest"')[0]
def insert_team(self, players, goals):
if len(players)<2:
players.append(None)
logging.debug('DB Inserting {} to table Teams'.format((goals, players[0], players[1],)))
self._cursor.execute('INSERT INTO Teams (nGoals, player1, player2) VALUES (?, ?, ?)', (goals, players[0], players[1],))
self._connection.commit()
return self._cursor.execute('SELECT seq FROM sqlite_sequence WHERE name="Teams"').fetchone()[0]
def insert_match(self, start_time, duration, team1, team2):
logging.debug('DB Inserting {} to table Matchs'.format((start_time, duration, team1, team2,)))
self._cursor.execute('INSERT INTO Matchs (timestamp, duration, winningTeam, losingTeam) VALUES (?, ?, ?, ?)', (start_time, duration, team1, team2,))
self._connection.commit()
def close(self):
self._connection.close()
...@@ -9,7 +9,11 @@ Created on Wed Apr 18 18:34:40 2018 ...@@ -9,7 +9,11 @@ Created on Wed Apr 18 18:34:40 2018
import logging import logging
import pyautogui # PyPi library import pyautogui # PyPi library
from threading import Thread from threading import Thread
import RPi.GPIO as GPIO
from main import OnRasp
if OnRasp:
import RPi.GPIO as GPIO
from player import Side from player import Side
...@@ -27,21 +31,23 @@ class GPIOThread(Thread): ...@@ -27,21 +31,23 @@ class GPIOThread(Thread):
Thread.__init__(self) Thread.__init__(self)
self.dispatcher = dispatcher self.dispatcher = dispatcher
self.continueRunning = True self.continueRunning = True
if OnRasp:
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False) for pin in GPIOThread._keyButtonBindings.keys():
GPIO.setmode(GPIO.BCM) print(pin)
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
for pin in GPIOThread._keyButtonBindings.keys(): GPIO.add_event_detect(pin, GPIO.RISING, callback=self.handleButtonPress)
print(pin)
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(pin, GPIO.RISING, callback=self.handleButtonPress)
def run(self): def run(self):
try: if OnRasp:
while self.continueRunning: try:
pass while self.continueRunning:
finally: pass
GPIOThread.clean() finally:
GPIOThread.clean()
def handleButtonPress(self, button_pin): def handleButtonPress(self, button_pin):
if button_pin not in GPIOThread._keyButtonBindings.keys(): if button_pin not in GPIOThread._keyButtonBindings.keys():
...@@ -56,4 +62,5 @@ class GPIOThread(Thread): ...@@ -56,4 +62,5 @@ class GPIOThread(Thread):
@staticmethod @staticmethod
def clean(): def clean():
GPIO.cleanup() if OnRasp:
GPIO.cleanup()
...@@ -6,6 +6,9 @@ Created on Wed Apr 18 18:34:40 2018 ...@@ -6,6 +6,9 @@ Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
import os
OnRasp = os.uname()[1] == 'raspberrypi'
import sys import sys
import logging import logging
import threading import threading
...@@ -19,6 +22,7 @@ from ui.main_ui import Ui_MainWindow ...@@ -19,6 +22,7 @@ from ui.main_ui import Ui_MainWindow
from modules import * from modules import *
from player import Side from player import Side
from input import GPIOThread from input import GPIOThread
from database import Database
class MainWin(QtWidgets.QMainWindow): class MainWin(QtWidgets.QMainWindow):
def __init__(self, parent=None): def __init__(self, parent=None):
...@@ -52,6 +56,8 @@ class MainWin(QtWidgets.QMainWindow): ...@@ -52,6 +56,8 @@ class MainWin(QtWidgets.QMainWindow):
self.ui.panels.currentWidget().load() self.ui.panels.currentWidget().load()
self.displaySystemTime() self.displaySystemTime()
self.startTimer(1000) self.startTimer(1000)
self._loadSettings()
#def eventFilter(target, event): #def eventFilter(target, event):
# return event.type()==QEvent.KeyPress and event.key() not in acceptedKeys # return event.type()==QEvent.KeyPress and event.key() not in acceptedKeys
...@@ -80,10 +86,10 @@ class MainWin(QtWidgets.QMainWindow): ...@@ -80,10 +86,10 @@ class MainWin(QtWidgets.QMainWindow):
contentFolder = join(dirname(dirname(abspath(__file__))), 'content') contentFolder = join(dirname(dirname(abspath(__file__))), 'content')
return join(contentFolder, path) return join(contentFolder, path)
def _refreshAfterSettings(self): def _loadSettings(self):
from settings import Settings from settings import Settings
if Settings.ui['fullscreen']: if Settings['ui.fullscreen']:
self.showFullScreen() self.showFullScreen()
QApplication.setOverrideCursor(Qt.BlankCursor); QApplication.setOverrideCursor(Qt.BlankCursor);
else: else:
...@@ -122,3 +128,4 @@ if __name__=='__main__': ...@@ -122,3 +128,4 @@ if __name__=='__main__':
finally: finally:
GPIOThread.clean() GPIOThread.clean()
Database.instance().close()
...@@ -40,12 +40,11 @@ class Module(QtWidgets.QWidget): ...@@ -40,12 +40,11 @@ class Module(QtWidgets.QWidget):
self.mainwin.ui.panels.setCurrentIndex(newmod_idx) self.mainwin.ui.panels.setCurrentIndex(newmod_idx)
self.mainwin.ui.panels.setFocusProxy(self.mainwin.modules[newmod_idx]) self.mainwin.ui.panels.setFocusProxy(self.mainwin.modules[newmod_idx])
self.mainwin.modules[newmod_idx].setFocus() self.mainwin.modules[newmod_idx].setFocus()
self.mainwin.modules[newmod_idx].load()
# Select first element of the Module # Select first element of the Module
self.mainwin.modules[newmod_idx].focusNextChild() self.mainwin.modules[newmod_idx].focusNextChild()
self.mainwin.modules[newmod_idx].focusPreviousChild() self.mainwin.modules[newmod_idx].focusPreviousChild()
self.mainwin.modules[newmod_idx].focusPreviousChild() self.mainwin.modules[newmod_idx].load()
def send(self, to, **kwargs): def send(self, to, **kwargs):
mod_idx = self.mainwin.findMod(to) mod_idx = self.mainwin.findMod(to)
......
...@@ -48,9 +48,9 @@ class AuthLeagueModule(AuthModuleBase): ...@@ -48,9 +48,9 @@ class AuthLeagueModule(AuthModuleBase):
# Update the left side description # Update the left side description
player.displayImg(self.ui.img) player.displayImg(self.ui.img)
self.ui.lblName.setText(player.name) self.ui.lblName.setText(player.name)
self.ui.lblStat1.setText('{} Victories'.format(player.stats.victories)) self.ui.lblStat1.setText('{} Victories'.format(player.stats['victories']))
self.ui.lblStat2.setText('{} Games Played'.format(player.stats.games_played)) self.ui.lblStat2.setText('{} Games Played'.format(player.stats['games_played']))
self.ui.lblStat3.setText('{} Goals Scored'.format(player.stats.goals_scored)) self.ui.lblStat3.setText('{} Goals Scored'.format(player.stats['goals_scored']))
if player!=PlayerEmpty: if player!=PlayerEmpty:
# Update the right side list, making sure that the added player is showed # Update the right side list, making sure that the added player is showed
......
...@@ -12,7 +12,8 @@ from PyQt5 import QtWidgets ...@@ -12,7 +12,8 @@ from PyQt5 import QtWidgets
from PyQt5.QtGui import QRegion from PyQt5.QtGui import QRegion
from PyQt5.QtCore import QTime, QTimer, QRect, Qt from PyQt5.QtCore import QTime, QTimer, QRect, Qt
from player import Side from database import Database, DatabaseError
from player import Side, PlayerGuest
from module import Module from module import Module
import modules import modules
from ui.endgame_ui import Ui_Form as EndGameWidget from ui.endgame_ui import Ui_Form as EndGameWidget
...@@ -31,13 +32,16 @@ class EndGameModule(Module): ...@@ -31,13 +32,16 @@ class EndGameModule(Module):
self.setActiveP2(len(self.players[self.winSide])>1) self.setActiveP2(len(self.players[self.winSide])>1)
self.displayPlayers() self.displayPlayers()
db = Database.instance()
idTeams = {}
for side in [Side.Left, Side.Right]: for side in [Side.Left, Side.Right]:
for player in self.players[side]: if PlayerGuest in self.players[side]:
player.stats.victories += 1 if side==self.winSide else 0 idTeams[side] = db.select_guest_team()
player.stats.goals_scored += self.scores[side] else:
player.stats.time_played += self.time idTeams[side] = db.insert_team([player.id for player in self.players[side]], self.scores[side])
player.stats.games_played += 1
player.save() db.insert_match(int(self.start_time), int(self.duration), idTeams[self.winSide], idTeams[self.winSide.opposite])
# Quit the screen after 5 seconds if the user doesn't do it before # Quit the screen after 5 seconds if the user doesn't do it before
#self.screenTimeout.start(5000) #self.screenTimeout.start(5000)
...@@ -58,8 +62,12 @@ class EndGameModule(Module): ...@@ -58,8 +62,12 @@ class EndGameModule(Module):
self.winSide = val self.winSide = val
elif key=='scores': elif key=='scores':
self.scores = val self.scores = val
elif key=='time': elif key=='start_time':
self.time = val self.start_time = val
print('start_time {}'.format(val))
elif key=='duration':
self.duration = val
print('duration {}'.format(val))
#else: #else:
# raise ValueError('Unknown message identifier {}'.format(kwargs) # raise ValueError('Unknown message identifier {}'.format(kwargs)
......
...@@ -11,7 +11,7 @@ import logging ...@@ -11,7 +11,7 @@ import logging
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
from PyQt5.QtGui import QRegion from PyQt5.QtGui import QRegion
from PyQt5.QtCore import QTime, QTimer, QRect, Qt, QUrl from PyQt5.QtCore import QDateTime, QDate, QTime, QTimer, QRect, Qt, QUrl
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer
from PyQt5.QtMultimediaWidgets import QVideoWidget from PyQt5.QtMultimediaWidgets import QVideoWidget
...@@ -40,7 +40,7 @@ class GameOverChecker(): ...@@ -40,7 +40,7 @@ class GameOverChecker():
if self.conditionType=='score' and scores[bestPlayer]>=self.limit: if self.conditionType=='score' and scores[bestPlayer]>=self.limit:
return bestPlayer return bestPlayer
elif self.conditionType=='time' and time>self.limit: elif self.conditionType=='time' and time>=self.limit:
return bestPlayer return bestPlayer
else: else:
return Side.Undef return Side.Undef
...@@ -69,7 +69,14 @@ class GameModule(Module): ...@@ -69,7 +69,14 @@ class GameModule(Module):
self.showingReplay = False self.showingReplay = False
if self.replayer: if self.replayer:
self.replayer.start_recording() self.replayer.start_recording()
self.gameoverChecker = GameOverChecker('score', 10)
gameover_type = Settings['gameover.type']
gameover_value = Settings['gameover.value']
if gameover_type=='time':
gameover_value *= 60
self.gameoverChecker = GameOverChecker(gameover_type, gameover_value)
if all([len(val)==0 for val in self.players.values()]): if all([len(val)==0 for val in self.players.values()]):
self.players[Side.Left ].append(PlayerGuest) self.players[Side.Left ].append(PlayerGuest)
...@@ -174,5 +181,8 @@ class GameModule(Module): ...@@ -174,5 +181,8 @@ class GameModule(Module):
winSide = self.gameoverChecker.check(self.getGameTime(), self.scores) winSide = self.gameoverChecker.check(self.getGameTime(), self.scores)
if winSide!=Side.Undef: if winSide!=Side.Undef:
self.send(modules.EndGameModule, players=self.players, winSide=winSide, scores=self.scores, time=self.getGameTime()) start_timestamp = int(QDateTime(QDate.currentDate(), self.gameStartTime).toMSecsSinceEpoch()/1000)
self.send(modules.EndGameModule, players=self.players, winSide=winSide, scores=self.scores)
self.send(modules.EndGameModule, start_time=start_timestamp, duration=self.getGameTime())
self.switchModule(modules.EndGameModule) self.switchModule(modules.EndGameModule)
...@@ -28,10 +28,10 @@ class LeaderboardItemWidget(QtWidgets.QWidget): ...@@ -28,10 +28,10 @@ class LeaderboardItemWidget(QtWidgets.QWidget):
self.ui.lblFName.setText(player.fname) self.ui.lblFName.setText(player.fname)
self.ui.lblLName.setText(player.lname) self.ui.lblLName.setText(player.lname)
self.ui.lblVictories.setText (self.ui.lblVictories.text().replace('####', str(player.stats.victories))) 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.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.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.lblMinutesPlayed.setText(self.ui.lblMinutesPlayed.text().replace('####', str(player.stats['time_played'])))
self.ui.pushButton.clicked.connect(lambda: logging.debug('clicked')) self.ui.pushButton.clicked.connect(lambda: logging.debug('clicked'))
...@@ -48,13 +48,14 @@ class LeaderboardModule(Module): ...@@ -48,13 +48,14 @@ class LeaderboardModule(Module):
self.selectedSort = 0 self.selectedSort = 0
self.sortMethodRB = [self.ui.rbName, self.ui.rbVictories, self.ui.rbScore, self.ui.rbGamesPlayed, self.ui.rbTimePlayed] 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.sortMethodAttr = ['lname', 'stats_property.victories', 'stats_property.goals_scored', 'stats_property.games_played', 'stats_property.time_played']
self.sortMethodRB[self.selectedSort].setChecked(True) self.sortMethodRB[self.selectedSort].setChecked(True)
def load(self): def load(self):
logging.debug('Loading LeaderboardModule') logging.debug('Loading LeaderboardModule')
self.loadList() self.loadList()
self.setFocus()
def unload(self): def unload(self):
logging.debug('Unloading LeaderboardModule') logging.debug('Unloading LeaderboardModule')
......
...@@ -56,7 +56,7 @@ class MenuModule(Module): ...@@ -56,7 +56,7 @@ class MenuModule(Module):
self.send(modules.MenuModule, rfid=-2, source=Side.Left) self.send(modules.MenuModule, rfid=-2, source=Side.Left)
elif e.key() == Qt.Key_Right: elif e.key() == Qt.Key_Right:
self.send(modules.MenuModule, rfid=-4, source=Side.Right) self.send(modules.MenuModule, rfid=-3, source=Side.Right)
elif e.key() == Qt.Key_Return: elif e.key() == Qt.Key_Return:
if QApplication.focusWidget()==None: if QApplication.focusWidget()==None:
......
...@@ -8,8 +8,8 @@ Created on Wed Apr 18 18:34:40 2018 ...@@ -8,8 +8,8 @@ Created on Wed Apr 18 18:34:40 2018
import logging import logging
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt, QEvent
from PyQt5.QtWidgets import QTableWidgetItem, QComboBox, QApplication from PyQt5.QtWidgets import QRadioButton, QSlider
from settings import Settings from settings import Settings
from module import Module from module import Module
...@@ -19,39 +19,85 @@ from ui.options_ui import Ui_Form as OptionsWidget ...@@ -19,39 +19,85 @@ from ui.options_ui import Ui_Form as OptionsWidget
class OptionsModule(Module): class OptionsModule(Module):
def __init__(self, parent): def __init__(self, parent):
super().__init__(parent, OptionsWidget()) super().__init__(parent, OptionsWidget())
self.ui.sliderGameOverValue.valueChanged.connect(self.updateGameOverLabel)
# Button connections self.ui.rbGameOver_Score.clicked.connect(self.updateGameOverLabel)
self.ui.btnSave.clicked.connect(self.handleSave) self.ui.rbGameOver_Time.clicked.connect(self.updateGameOverLabel)
self.ui.btnBack.clicked.connect(self.handleBack)
self.ui.sliderGameOverValue.installEventFilter(self)
def load(self): def load(self):
logging.debug('Loading OptionsModule') logging.debug('Loading OptionsModule')
cbb = QComboBox()
cbb.addItem('true') # Set Gameover condition from settings
cbb.addItem('false') self.ui.rbGameOver_Score.setChecked(Settings['gameover.type']=='score')
self.ui.options.insertRow(self.ui.options.rowCount()) self.ui.rbGameOver_Time.setChecked(Settings['gameover.type']=='time')
self.ui.options.setItem(self.ui.options.rowCount()-1, 0, QTableWidgetItem('FullScreen')) self.ui.sliderGameOverValue.setValue(Settings['gameover.value'])
self.ui.options.setCellWidget(self.ui.options.rowCount()-1, 1, cbb)
# Set League players from settings
self.ui.rbNumPlayerLeague_1.setChecked(Settings['league.playerPerTeam']==1)
self.ui.rbNumPlayerLeague_2.setChecked(Settings['league.playerPerTeam']==2)
# Set Language from settings
self.ui.rbLanguage_English.setChecked(Settings['ui.language']=='en')
self.ui.rbLanguage_French.setChecked(Settings['ui.language']=='fr')
self.selectIndex = 0
self.updateSelection()
self.updateGameOverLabel(0)
def unload(self): def unload(self):
logging.debug('Unloading OptionsModule') logging.debug('Unloading OptionsModule')
# Delete the table's content
self.ui.options.setRowCount(0)
def other(self, **kwargs): def other(self, **kwargs):
logging.debug('Other OptionsModule') logging.debug('Other OptionsModule')
def eventFilter(self, obj, event):
if obj==self.ui.sliderGameOverValue and event.type()==QEvent.KeyPress and (event.key()==Qt.Key_Up or event.key()==Qt.Key_Down):
self.keyPressEvent(event)
return True
return False
def keyPressEvent(self, e): def keyPressEvent(self, e):
if e.key() == Qt.Key_Escape: if e.key() == Qt.Key_Escape:
self.handleBack() self.handleBack()
elif e.key() == Qt.Key_Return: elif e.key() == Qt.Key_Return:
self.handleSave() self.handleSave()
elif e.key() == Qt.Key_Up:
#self.parent().focusPreviousChild()
self.selectIndex = self.selectIndex-1 if self.selectIndex!=0 else len(self.selectables)-1
self.updateSelection()
elif e.key() == Qt.Key_Down:
#self.parent().focusNextChild()
self.selectIndex = self.selectIndex+1 if self.selectIndex!=len(self.selectables)-1 else 0
self.updateSelection()
def updateSelection(self):
self.selectables = []
isSelectable = lambda widget: (isinstance(widget, QRadioButton) and widget.isChecked()) or isinstance(widget, QSlider)
for gb in [self.ui.gbGameOver, self.ui.gbLeaguePlayers, self.ui.gbLanguage]:
self.selectables.extend([child for child in gb.children() if isSelectable(child)])
self.selectables[self.selectIndex].setFocus()
def updateGameOverLabel(self, val):
sliderVal = self.ui.sliderGameOverValue.value()
strPoints = '{} point{}'.format(sliderVal, 's' if sliderVal>1 else '')
strTime = '{} minute{}'.format(sliderVal, 's' if sliderVal>1 else '')
self.ui.lblGameOverValue.setText(strPoints if self.ui.rbGameOver_Score.isChecked() else strTime)
def handleSave(self): def handleSave(self):
Settings['ui.fullscreen'] = self.ui.options.cellWidget(0, 1).currentText().lower() == 'true' Settings['ui.language'] = 'en' if self.ui.rbLanguage_English.isChecked() else 'fr'
self.mainwin._refreshAfterSettings() Settings['gameover.type'] = 'score' if self.ui.rbGameOver_Score.isChecked() else 'time'
Settings['gameover.value'] = self.ui.sliderGameOverValue.value()
Settings['league.playerPerTeam'] = 1 if self.ui.rbNumPlayerLeague_1.isChecked() else 2
Settings.saveSettingsToJSON()
self.mainwin._loadSettings()
self.switchModule(modules.MenuModule) self.switchModule(modules.MenuModule)
def handleBack(self): def handleBack(self):
# ToDo: Maybe add a warning
self.switchModule(modules.MenuModule) self.switchModule(modules.MenuModule)
...@@ -7,7 +7,10 @@ Created on Wed Apr 18 18:34:40 2018 ...@@ -7,7 +7,10 @@ Created on Wed Apr 18 18:34:40 2018
""" """
import logging import logging
import sqlite3
from enum import Enum from enum import Enum
from database import Database, DatabaseError
class Side(Enum): class Side(Enum):
''' '''
...@@ -16,52 +19,43 @@ class Side(Enum): ...@@ -16,52 +19,43 @@ class Side(Enum):
Undef = -1 Undef = -1
Left = 0 Left = 0
Right = 1 Right = 1
@property
def opposite(self):
return Side.Right if self==Side.Left else Side.Left
class Player(): class Player():
def __init__(self, id, fname='', lname='', pic_path=':ui/img/placeholder_head.jpg'): __query_infos = 'SELECT id, fname, lname, pic 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'
def __init__(self, id, fname, lname, pic_path, stats):
self.id = id self.id = id
self.fname = fname self.fname = fname
self.lname = lname self.lname = lname
self.pic_path = pic_path self.pic_path = pic_path if pic_path else Player._placeholder_pic_path # Default pic if None
self.stats = Stat(id) self.stats = stats
@staticmethod @staticmethod
def fromRFID(id): def fromRFID(rfid):
fname, lname, pic_url = '','','' # Replace with DB calls db = Database.instance()
if id==-1: try:
player = Player(id, 'Guest') # Retrieve generic informations
id, fname, lname, pic = db.select_one(Player.__query_infos, rfid)
elif id==-2:
player = Player(id, 'Alfredo', 'Enrique')
elif id==-3:
player = Player(id, 'Bastien', 'Dali')
player.stats.victories = 1
elif id==-4:
player = Player(id, 'Carim', 'Cuebache')
player.stats.time_played = 1
elif id==-5:
player = Player(id, 'Dorian', 'Boulet')
player.stats.games_played = 1
elif id==-6:
player = Player(id, 'Enzo', 'Arobaz')
player.stats.goals_scored = 1
elif id==-10: # Retrieve stats
player = Player(id, 'Name', '') stats = {}
player.stats.victories = '' stats['time_played'], stats['goals_scored'], stats['games_played'] = db.select_one(Player.__query_time_goals_games, id, id)
player.stats.time_played = '' stats['victories'], = db.select_one(Player.__query_victories, id)
player.stats.games_played = ''
player.stats.goals_scored = ''
else: return Player(id, fname, lname, pic, stats)
player = Player(id, fname, lname, pic_url)
return player except DatabaseError as e:
logging.warn('DB Error: {}'.format(e))
return PlayerGuest
def displayImg(self, containerWidget): def displayImg(self, containerWidget):
containerWidget.setStyleSheet('border-image: url({});'.format(self.pic_path)) containerWidget.setStyleSheet('border-image: url({});'.format(self.pic_path))
...@@ -70,29 +64,21 @@ class Player(): ...@@ -70,29 +64,21 @@ class Player():
''' '''
Update or create the player in database Update or create the player in database
''' '''
# TODO
pass
@property @property
def name(self): def name(self):
return '{} {}'.format(self.fname, self.lname.upper()) return '{} {}'.format(self.fname, self.lname.upper())
@property @property
def pic(self): def stats_property(self):
return QPixmap(self.pic_path) class Stat:
def __init__(self, stats):
class Stat(): self.victories = stats['victories']
def __init__(self, player_id): self.time_played = stats['time_played']
self.victories = 0 self.goals_scored = stats['goals_scored']
self.time_played = 0 self.games_played = stats['games_played']
self.games_played = 0
self.goals_scored = 0
if player_id >= 0: return Stat(self.stats)
self.victories = 0
self.time_played = 0
self.games_played = 0
self.goals_scored = 0
PlayerGuest = Player.fromRFID(-1) PlayerGuest = Player.fromRFID(-1)
PlayerEmpty = Player.fromRFID(-10) PlayerEmpty = Player(-1, '', '', Player._placeholder_pic_path, {'time_played':'', 'goals_scored':'', 'games_played':'', 'victories': ''})
...@@ -6,15 +6,12 @@ Created on Wed Apr 18 18:34:40 2018 ...@@ -6,15 +6,12 @@ Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
import os
from threading import Thread, Event from threading import Thread, Event
from main import MainWin from main import MainWin, OnRasp
from settings import Settings from settings import Settings
onRasp = os.uname()[1] == 'raspberrypi' if OnRasp:
if onRasp:
import picamera import picamera
class Replay(Thread): class Replay(Thread):
...@@ -27,7 +24,7 @@ class Replay(Thread): ...@@ -27,7 +24,7 @@ class Replay(Thread):
self.stop_flag = Event() self.stop_flag = Event()
self.stopped_flag = Event() self.stopped_flag = Event()
if onRasp: if OnRasp:
self.cam = picamera.PiCamera() self.cam = picamera.PiCamera()
self.cam.resolution = Settings['picam.resolution'] self.cam.resolution = Settings['picam.resolution']
self.cam.framerate = Settings['picam.fps'] self.cam.framerate = Settings['picam.fps']
...@@ -36,14 +33,15 @@ class Replay(Thread): ...@@ -36,14 +33,15 @@ class Replay(Thread):
self.stream = picamera.PiCameraCircularIO(self.cam, seconds=Settings['replay.duration']) self.stream = picamera.PiCameraCircularIO(self.cam, seconds=Settings['replay.duration'])
def start_recording(self): def start_recording(self):
if onRasp: if OnRasp:
self.start_flag.set() self.start_flag.set()
def stop_recording(self): def stop_recording(self):
if onRasp: if OnRasp:
self.stop_flag.set() self.stop_flag.set()
self.stopped_flag.wait() self.stopped_flag.wait()
# Clear all control flags
self.stop_flag.clear() self.stop_flag.clear()
self.start_flag.clear() self.start_flag.clear()
self.stopped_flag.clear() self.stopped_flag.clear()
...@@ -69,6 +67,8 @@ class Replay(Thread): ...@@ -69,6 +67,8 @@ class Replay(Thread):
self.stream.copy_to(self.replayPath) self.stream.copy_to(self.replayPath)
self.stream.clear() self.stream.clear()
# Set this flag to tell the calling thread that replay is saved
self.stopped_flag.set() self.stopped_flag.set()
self.cam.close() self.cam.close()
...@@ -76,4 +76,4 @@ class Replay(Thread): ...@@ -76,4 +76,4 @@ class Replay(Thread):
@staticmethod @staticmethod
def isCamAvailable(): def isCamAvailable():
return onRasp # and other checks (ToDo) return OnRasp # and other checks (ToDo)
...@@ -9,10 +9,20 @@ Created on Wed Apr 18 18:34:40 2018 ...@@ -9,10 +9,20 @@ Created on Wed Apr 18 18:34:40 2018
import json import json
import main import main
class MyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Setting):
return obj.__dict__
elif isinstance(obj, String) and obj=='settingsPath':
return None
else:
return json.JSONEncoder.default(self, obj)
class Setting(object): class Setting(object):
TypeName = '' TypeName = ''
def __init__(self, value): def __init__(self, value):
self.type = type(self).TypeName
self.value = value self.value = value
class SettingBoolean(Setting): class SettingBoolean(Setting):
...@@ -36,10 +46,9 @@ class SettingRange(Setting): ...@@ -36,10 +46,9 @@ class SettingRange(Setting):
def __init__(self, value, limits): def __init__(self, value, limits):
Setting.__init__(self, value) Setting.__init__(self, value)
self.lower_limit = min(limits) self.range = [min(limits), max(limits)]
self.upper_limit = max(limits)
if self.value<self.lower_limit or self.value>self.upper_limit: if self.value<self.range[0] or self.value>self.range[1]:
raise ValueError('Setting value {} not in range {}'.format(self.value, (self.lower_limit, self.upper_limit))) raise ValueError('Setting value {} not in range {}'.format(self.value, (self.lower_limit, self.upper_limit)))
class SettingsHolder(object): class SettingsHolder(object):
...@@ -73,13 +82,8 @@ class SettingsHolder(object): ...@@ -73,13 +82,8 @@ class SettingsHolder(object):
@staticmethod @staticmethod
def _parseKey(key): def _parseKey(key):
return [k for k in key.split('.') if k] return [k for k in key.split('.') if k]
def _init_ui(self):
self.ui.append(Setting())
def loadSettingsFromJSON(self): def loadSettingsFromJSON(self):
settings = list()
with open(self.settingsPath, 'r') as f: with open(self.settingsPath, 'r') as f:
content = json.load(f) content = json.load(f)
...@@ -106,4 +110,14 @@ class SettingsHolder(object): ...@@ -106,4 +110,14 @@ class SettingsHolder(object):
getattr(self, cat)[name] = setting getattr(self, cat)[name] = setting
def saveSettingsToJSON(self):
# Deletes the settings path member to prevent it from being saved in the JSON
settingsPath = self.settingsPath
del self.settingsPath
with open(settingsPath, 'w') as f:
content = json.dump(self.__dict__, f, cls=MyEncoder, indent=4)
self.settingsPath = settingsPath
Settings = SettingsHolder(main.MainWin.getContent('settings.json')) Settings = SettingsHolder(main.MainWin.getContent('settings.json'))
...@@ -141,7 +141,7 @@ ...@@ -141,7 +141,7 @@
color: rgb(229, 229, 229); color: rgb(229, 229, 229);
} }
QPushButton { QPushButton, QGroupBox {
background-color: rgba(44, 44, 63, 150); background-color: rgba(44, 44, 63, 150);
border: 1px; border: 1px;
border-radius: 10px; border-radius: 10px;
......
...@@ -31,12 +31,9 @@ ...@@ -31,12 +31,9 @@
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="font"> <property name="font">
<font> <font>
<family>FreeSerif</family> <family>Schoolbook Uralic [pyrs]</family>
<pointsize>72</pointsize> <pointsize>72</pointsize>
<weight>50</weight>
<italic>true</italic> <italic>true</italic>
<bold>false</bold>
<underline>false</underline>
</font> </font>
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
......
...@@ -6,13 +6,29 @@ ...@@ -6,13 +6,29 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>728</width> <width>1280</width>
<height>540</height> <height>720</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
<property name="styleSheet">
<string notr="true">QRadioButton {
border: 2px solid rgb(74, 74, 107);
border-radius: 10px;
border-color: rgb(74, 74, 107);
}
QRadioButton::checked {
background-color: rgba(44, 44, 63, 240);
}
QRadioButton::indicator {
background-color: transparent;
border: 0px solid transparent;
}</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin"> <property name="leftMargin">
<number>15</number> <number>15</number>
...@@ -27,145 +43,476 @@ ...@@ -27,145 +43,476 @@
<number>9</number> <number>9</number>
</property> </property>
<item> <item>
<widget class="QScrollArea" name="scrollArea"> <widget class="QLabel" name="label">
<property name="verticalScrollBarPolicy"> <property name="font">
<enum>Qt::ScrollBarAsNeeded</enum> <font>
<pointsize>40</pointsize>
</font>
</property>
<property name="text">
<string>Options</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gbGameOver">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>18</pointsize>
<weight>75</weight>
<bold>true</bold>
<underline>true</underline>
</font>
</property> </property>
<property name="horizontalScrollBarPolicy"> <property name="autoFillBackground">
<enum>Qt::ScrollBarAlwaysOff</enum> <bool>true</bool>
</property> </property>
<property name="widgetResizable"> <property name="title">
<bool>false</bool> <string>Game Over Condition</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignCenter</set> <set>Qt::AlignCenter</set>
</property> </property>
<widget class="QWidget" name="scrollAreaWidgetContents"> <layout class="QVBoxLayout" name="verticalLayout_2">
<property name="geometry"> <property name="topMargin">
<rect> <number>40</number>
<x>0</x>
<y>0</y>
<width>696</width>
<height>477</height>
</rect>
</property> </property>
<property name="sizePolicy"> <item>
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <layout class="QHBoxLayout" name="horizontalLayout">
<horstretch>0</horstretch> <item>
<verstretch>0</verstretch> <spacer name="horizontalSpacer_3">
</sizepolicy> <property name="orientation">
</property> <enum>Qt::Horizontal</enum>
<layout class="QVBoxLayout" name="verticalLayout_2"> </property>
<property name="margin"> <property name="sizeHint" stdset="0">
<number>0</number> <size>
</property> <width>40</width>
<item> <height>20</height>
<widget class="QTableWidget" name="options"> </size>
<property name="frameShape"> </property>
<enum>QFrame::NoFrame</enum> </spacer>
</property> </item>
<property name="frameShadow"> <item>
<enum>QFrame::Plain</enum> <widget class="QRadioButton" name="rbGameOver_Score">
</property> <property name="sizePolicy">
<property name="showGrid"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<bool>true</bool> <horstretch>0</horstretch>
</property> <verstretch>0</verstretch>
<attribute name="horizontalHeaderVisible"> </sizepolicy>
<bool>true</bool> </property>
</attribute> <property name="minimumSize">
<attribute name="horizontalHeaderStretchLastSection"> <size>
<bool>true</bool> <width>280</width>
</attribute> <height>65</height>
<attribute name="verticalHeaderVisible"> </size>
<bool>false</bool> </property>
</attribute> <property name="font">
<column> <font>
<pointsize>18</pointsize>
<weight>50</weight>
<bold>false</bold>
<underline>false</underline>
</font>
</property>
<property name="text"> <property name="text">
<string>Name</string> <string>By Score</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<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>
<widget class="QRadioButton" name="rbGameOver_Time">
<property name="minimumSize">
<size>
<width>280</width>
<height>65</height>
</size>
</property>
<property name="font">
<font>
<pointsize>18</pointsize>
<weight>50</weight>
<bold>false</bold>
<underline>false</underline>
</font>
</property>
<property name="text">
<string>By Time</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<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>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSlider" name="sliderGameOverValue">
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>60</number>
</property>
<property name="value">
<number>10</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>5</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_12">
<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="QLabel" name="lblGameOverValue">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property> </property>
</column>
<column>
<property name="text"> <property name="text">
<string>Value</string> <string>{} minutes</string>
</property> </property>
</column> <property name="alignment">
</widget> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</item> </property>
</layout> </widget>
</widget> </item>
<item>
<spacer name="horizontalSpacer_2">
<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>
</layout>
</item>
</layout>
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <widget class="QGroupBox" name="gbLeaguePlayers">
<item> <property name="sizePolicy">
<spacer name="horizontalSpacer"> <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<property name="orientation"> <horstretch>0</horstretch>
<enum>Qt::Horizontal</enum> <verstretch>0</verstretch>
</property> </sizepolicy>
<property name="sizeHint" stdset="0"> </property>
<size> <property name="font">
<width>40</width> <font>
<height>20</height> <pointsize>18</pointsize>
</size> <weight>75</weight>
</property> <bold>true</bold>
</spacer> <underline>true</underline>
</item> </font>
<item> </property>
<spacer name="horizontalSpacer_2"> <property name="autoFillBackground">
<property name="orientation"> <bool>true</bool>
<enum>Qt::Horizontal</enum> </property>
</property> <property name="title">
<property name="sizeHint" stdset="0"> <string>Number of players per side</string>
<size> </property>
<width>40</width> <property name="alignment">
<height>20</height> <set>Qt::AlignCenter</set>
</size> </property>
</property> <layout class="QHBoxLayout" name="horizontalLayout_4">
</spacer> <property name="topMargin">
</item> <number>40</number>
<item> </property>
<spacer name="horizontalSpacer_3"> <item>
<property name="orientation"> <spacer name="horizontalSpacer_9">
<enum>Qt::Horizontal</enum> <property name="orientation">
</property> <enum>Qt::Horizontal</enum>
<property name="sizeHint" stdset="0"> </property>
<size> <property name="sizeHint" stdset="0">
<width>40</width> <size>
<height>20</height> <width>40</width>
</size> <height>20</height>
</property> </size>
</spacer> </property>
</item> </spacer>
<item> </item>
<widget class="QPushButton" name="btnSave"> <item>
<property name="sizePolicy"> <widget class="QRadioButton" name="rbNumPlayerLeague_1">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <property name="minimumSize">
<horstretch>0</horstretch> <size>
<verstretch>0</verstretch> <width>280</width>
</sizepolicy> <height>65</height>
</property> </size>
<property name="text"> </property>
<string>Save</string> <property name="font">
</property> <font>
</widget> <pointsize>18</pointsize>
</item> <weight>50</weight>
<item> <bold>false</bold>
<widget class="QPushButton" name="btnBack"> <underline>false</underline>
<property name="sizePolicy"> </font>
<sizepolicy hsizetype="Expanding" vsizetype="Preferred"> </property>
<horstretch>0</horstretch> <property name="text">
<verstretch>0</verstretch> <string>1</string>
</sizepolicy> </property>
</property> </widget>
<property name="text"> </item>
<string>Back</string> <item>
</property> <spacer name="horizontalSpacer_10">
</widget> <property name="orientation">
</item> <enum>Qt::Horizontal</enum>
</layout> </property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QRadioButton" name="rbNumPlayerLeague_2">
<property name="minimumSize">
<size>
<width>280</width>
<height>65</height>
</size>
</property>
<property name="font">
<font>
<pointsize>18</pointsize>
<weight>50</weight>
<bold>false</bold>
<underline>false</underline>
</font>
</property>
<property name="text">
<string>2</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_11">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gbLanguage">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>18</pointsize>
<weight>75</weight>
<bold>true</bold>
<underline>true</underline>
</font>
</property>
<property name="title">
<string>Language</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="topMargin">
<number>40</number>
</property>
<item>
<spacer name="horizontalSpacer_6">
<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>
<widget class="QRadioButton" name="rbLanguage_English">
<property name="minimumSize">
<size>
<width>280</width>
<height>65</height>
</size>
</property>
<property name="font">
<font>
<pointsize>18</pointsize>
<weight>50</weight>
<bold>false</bold>
<underline>false</underline>
</font>
</property>
<property name="text">
<string>English</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<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>
<widget class="QRadioButton" name="rbLanguage_French">
<property name="minimumSize">
<size>
<width>280</width>
<height>65</height>
</size>
</property>
<property name="font">
<font>
<pointsize>18</pointsize>
<weight>50</weight>
<bold>false</bold>
<underline>false</underline>
</font>
</property>
<property name="text">
<string>Français</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>
<tabstops>
<tabstop>rbGameOver_Score</tabstop>
<tabstop>rbGameOver_Time</tabstop>
<tabstop>sliderGameOverValue</tabstop>
<tabstop>rbNumPlayerLeague_1</tabstop>
<tabstop>rbNumPlayerLeague_2</tabstop>
<tabstop>rbLanguage_English</tabstop>
<tabstop>rbLanguage_French</tabstop>
</tabstops>
<resources> <resources>
<include location="assets.qrc"/> <include location="assets.qrc"/>
</resources> </resources>
......