...
 
Commits (3)
...@@ -11,6 +11,7 @@ pyuic5 --import-from=ui ui/authleague.ui -o ui/authleague_ui.py ...@@ -11,6 +11,7 @@ pyuic5 --import-from=ui ui/authleague.ui -o ui/authleague_ui.py
pyuic5 --import-from=ui ui/leaderboard.ui -o ui/leaderboard_ui.py pyuic5 --import-from=ui ui/leaderboard.ui -o ui/leaderboard_ui.py
# Custom widgets # Custom widgets
pyuic5 --import-from=ui ui/playerlist.ui -o ui/playerlist_ui.py pyuic5 --import-from=ui ui/playerlist.ui -o ui/playerlist_ui.py
pyuic5 --import-from=ui ui/delete_dialog.ui -o ui/delete_dialog_ui.py
pyrcc5 -root /ui ui/assets.qrc -o ui/assets_rc.py pyrcc5 -root /ui ui/assets.qrc -o ui/assets_rc.py
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
import logging
import sqlite3 import sqlite3
class DatabaseError(Exception): class DatabaseError(Exception):
...@@ -17,8 +14,8 @@ class Database(): ...@@ -17,8 +14,8 @@ class Database():
def __init__(self): def __init__(self):
if not Database.__db: if not Database.__db:
from main import MainWin from main import getContent
db_path = MainWin.getContent('babyfut.sqlite') db_path = getContent('babyfut.sqlite')
self._connection = sqlite3.connect(db_path) self._connection = sqlite3.connect(db_path)
@staticmethod @staticmethod
...@@ -50,15 +47,24 @@ class Database(): ...@@ -50,15 +47,24 @@ class Database():
if len(players)<2: if len(players)<2:
players.append(None) 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._cursor.execute('INSERT INTO Teams (nGoals, player1, player2) VALUES (?, ?, ?)', (goals, players[0], players[1],))
self._connection.commit() self._connection.commit()
return self._cursor.execute('SELECT seq FROM sqlite_sequence WHERE name="Teams"').fetchone()[0] return self._cursor.execute('SELECT seq FROM sqlite_sequence WHERE name="Teams"').fetchone()[0]
def insert_match(self, start_time, duration, team1, team2): 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._cursor.execute('INSERT INTO Matchs (timestamp, duration, winningTeam, losingTeam) VALUES (?, ?, ?, ?)', (start_time, duration, team1, team2,))
self._connection.commit() self._connection.commit()
def select_all_rfid(self, debug=False):
from settings import Settings
if Settings['app.mode']=='prod':
return self._cursor.execute('SELECT rfid FROM Players WHERE rfid>0').fetchall()
else:
return self._cursor.execute('SELECT rfid FROM Players WHERE rfid<-1').fetchall()
def delete_player(self, playerID):
self._cursor.execute('DELETE FROM Players WHERE id==?', (playerID,))
self._connection.commit()
def close(self): def close(self):
self._connection.close() self._connection.close()
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
......
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
...@@ -11,93 +9,21 @@ OnRasp = os.uname()[1] == 'raspberrypi' ...@@ -11,93 +9,21 @@ OnRasp = os.uname()[1] == 'raspberrypi'
import sys import sys
import logging import logging
import threading
from os.path import dirname, abspath, join from os.path import dirname, abspath, join
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QGraphicsBlurEffect, QApplication
from PyQt5.QtCore import QTime, Qt
from ui.main_ui import Ui_MainWindow
from modules import *
from player import Side
from input import GPIOThread
from database import Database
class MainWin(QtWidgets.QMainWindow): def getContent(path):
def __init__(self, parent=None): contentFolder = join(dirname(dirname(abspath(__file__))), 'content')
QtWidgets.QWidget.__init__(self, parent) return join(contentFolder, path)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
#Background blur
bgBlur = QGraphicsBlurEffect()
bgBlur.setBlurHints(QGraphicsBlurEffect.QualityHint)
#bgBlur.setBlurRadius(5)
#self.ui.panels.setGraphicsEffect(bgBlur)
# Module loading
self.modules = [
MenuModule(self),
AuthQuickModule(self),
AuthLeagueModule(self),
GameModule(self),
EndGameModule(self),
LeaderboardModule(self),
OptionsModule(self)
]
for mod in self.modules:
self.ui.panels.addWidget(mod)
self.ui.panels.setCurrentIndex(0)
self.ui.panels.currentWidget().setFocus()
self.ui.panels.setFocusProxy(self.ui.panels.currentWidget())
self.ui.panels.currentWidget().load()
self.displaySystemTime()
self.startTimer(1000)
self._loadSettings()
#def eventFilter(target, event):
# return event.type()==QEvent.KeyPress and event.key() not in acceptedKeys
def timerEvent(self, e):
self.displaySystemTime()
def displaySystemTime(self):
self.ui.lcdTime.display(QTime.currentTime().toString("hh:mm:ss"))
def findMod(self, type):
mod_idx = [i for i, x in enumerate(self.modules) if isinstance(x, type)]
return -1 if len(mod_idx)==0 else mod_idx[0]
def dispatchMessage(self, msg, toType=None, toAll=False):
if toType!=None:
modulesIdx = [self.findMod(toType)]
else:
modulesIdx = self.modules if toAll else [self.findMod(type(self.ui.panels.currentWidget()))]
for modIdx in modulesIdx:
self.modules[modIdx].other(**msg)
@staticmethod
def getContent(path):
contentFolder = join(dirname(dirname(abspath(__file__))), 'content')
return join(contentFolder, path)
def _loadSettings(self):
from settings import Settings
if Settings['ui.fullscreen']:
self.showFullScreen()
QApplication.setOverrideCursor(Qt.BlankCursor);
else:
self.showNormal()
QApplication.setOverrideCursor(Qt.ArrowCursor);
if __name__=='__main__': if __name__=='__main__':
from settings import Settings from ui.mainwin import MainWin
from modules import GameModule
from player import Side
from input import GPIOThread
from database import Database
from replay import Replay as ReplayThread from replay import Replay as ReplayThread
try: try:
......
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
import logging import logging
from PyQt5 import QtWidgets from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtCore import QTime, QTimer, Qt
from PyQt5.QtWidgets import QTableWidgetItem, QComboBox, QApplication
from modules import *
class Module(QtWidgets.QWidget): class Module(QWidget):
def __init__(self, parent, widget): def __init__(self, parent, widget):
# UI Setup # UI Setup
QtWidgets.QWidget.__init__(self, parent) QWidget.__init__(self, parent)
self.mainwin = parent self.mainwin = parent
self.ui = widget self.ui = widget
self.ui.setupUi(self) self.ui.setupUi(self)
...@@ -31,11 +25,11 @@ class Module(QtWidgets.QWidget): ...@@ -31,11 +25,11 @@ class Module(QtWidgets.QWidget):
elif newmod_idx<0: elif newmod_idx<0:
logging.error('Unknown panel {}'.format(new_type)) logging.error('Unknown panel {}'.format(new_type))
else: else:
# Unfocus the current module # Unfocus the current module
if QApplication.focusWidget() != None: if QApplication.focusWidget() != None:
QApplication.focusWidget().clearFocus() QApplication.focusWidget().clearFocus()
# Swap modules by unloading, changing the ui then loading # Swap modules by unloading, changing the ui then loading
self.mainwin.modules[curmod_idx].unload() self.mainwin.modules[curmod_idx].unload()
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])
......
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
...@@ -12,7 +10,7 @@ from PyQt5.QtCore import Qt ...@@ -12,7 +10,7 @@ from PyQt5.QtCore import Qt
import modules import modules
from module import Module from module import Module
from player import Side, Player, PlayerGuest from player import Side, Player
class AuthModuleBase(Module): class AuthModuleBase(Module):
def __init__(self, parent, widget): def __init__(self, parent, widget):
......
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
import logging import logging
from PyQt5.QtWidgets import QAbstractItemView from PyQt5.QtWidgets import QAbstractItemView
from module import Module
from modules.auth import AuthModuleBase from modules.auth import AuthModuleBase
from ui.authleague_ui import Ui_Form as AuthLeagueWidget from ui.authleague_ui import Ui_Form as AuthLeagueWidget
...@@ -53,7 +50,7 @@ class AuthLeagueModule(AuthModuleBase): ...@@ -53,7 +50,7 @@ class AuthLeagueModule(AuthModuleBase):
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
self.ui.playersList.addItem('{}. {}'.format(len(self.players[side]), player.name)) self.ui.playersList.addItem('{}. {}'.format(len(self.players[side]), player.name))
widgetItem = self.ui.playersList.item(self.ui.playersList.count()-1) widgetItem = self.ui.playersList.item(self.ui.playersList.count()-1)
self.ui.playersList.scrollToItem(widgetItem, QAbstractItemView.PositionAtBottom) self.ui.playersList.scrollToItem(widgetItem, QAbstractItemView.PositionAtBottom)
......
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
...@@ -10,10 +8,8 @@ import logging ...@@ -10,10 +8,8 @@ import logging
from PyQt5.QtWidgets import QSizePolicy from PyQt5.QtWidgets import QSizePolicy
import modules
from module import Module
from modules.auth import AuthModuleBase from modules.auth import AuthModuleBase
from player import Side, Player, PlayerGuest from player import Side, PlayerGuest
from ui.authquick_ui import Ui_Form as AuthQuickWidget from ui.authquick_ui import Ui_Form as AuthQuickWidget
class AuthQuickModule(AuthModuleBase): class AuthQuickModule(AuthModuleBase):
...@@ -44,7 +40,7 @@ class AuthQuickModule(AuthModuleBase): ...@@ -44,7 +40,7 @@ class AuthQuickModule(AuthModuleBase):
self.players = {Side.Left: list(), Side.Right: list()} self.players = {Side.Left: list(), Side.Right: list()}
def addPlayer(self, side, player): def addPlayer(self, side, player):
# If there is a placeholder Guest, clear it from the list, we don't need it anymore # If there is a placeholder Guest, clear it from the list, we don't need it anymore
if len(self.players[side])>0 and self.players[side][0]==PlayerGuest: if len(self.players[side])>0 and self.players[side][0]==PlayerGuest:
self.players[side].clear() self.players[side].clear()
......
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
import logging import logging
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
from PyQt5.QtGui import QRegion from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtCore import QTime, QTimer, QRect, Qt
from database import Database, DatabaseError from database import Database
from player import Side, PlayerGuest from player import Side, PlayerGuest
from module import Module from module import Module
import modules import modules
...@@ -49,8 +46,13 @@ class EndGameModule(Module): ...@@ -49,8 +46,13 @@ class EndGameModule(Module):
def unload(self): def unload(self):
logging.debug('Unloading EndGameModule') logging.debug('Unloading EndGameModule')
self.screenTimeout.stop() self.screenTimeout.stop()
del self.players del self.players
del self.gameType
del self.winSide del self.winSide
del self.scores
del self.start_time
del self.duration
def other(self, **kwargs): def other(self, **kwargs):
logging.debug('Other EndGameModule') logging.debug('Other EndGameModule')
...@@ -58,18 +60,16 @@ class EndGameModule(Module): ...@@ -58,18 +60,16 @@ class EndGameModule(Module):
for key, val in kwargs.items(): for key, val in kwargs.items():
if key=='players': if key=='players':
self.players = val self.players = val
elif key=='gameType':
self.gameType = val
elif key=='winSide': elif key=='winSide':
self.winSide = val self.winSide = val
elif key=='scores': elif key=='scores':
self.scores = val self.scores = val
elif key=='start_time': elif key=='start_time':
self.start_time = val self.start_time = val
print('start_time {}'.format(val))
elif key=='duration': elif key=='duration':
self.duration = val self.duration = val
print('duration {}'.format(val))
#else:
# raise ValueError('Unknown message identifier {}'.format(kwargs)
def keyPressEvent(self, e): def keyPressEvent(self, e):
if e.key() == Qt.Key_Escape or e.key() == Qt.Key_Return: if e.key() == Qt.Key_Escape or e.key() == Qt.Key_Return:
......
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
...@@ -10,6 +8,7 @@ import os ...@@ -10,6 +8,7 @@ import os
import logging import logging
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtGui import QRegion from PyQt5.QtGui import QRegion
from PyQt5.QtCore import QDateTime, QDate, 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
...@@ -45,6 +44,34 @@ class GameOverChecker(): ...@@ -45,6 +44,34 @@ class GameOverChecker():
else: else:
return Side.Undef return Side.Undef
class ReplayHolder(QVideoWidget):
def __init__(self, mediaPlayer, parent):
super().__init__(parent)
self.mediaPlayer = mediaPlayer
def keyPressEvent(self, e):
self.mediaPlayer.stop_replay(QMediaPlayer.StoppedState)
class ReplayPlayer(QMediaPlayer):
def __init__(self, parent):
super().__init__(parent, QMediaPlayer.VideoSurface)
self.stateChanged.connect(self.stop_replay)
self.setMuted(True)
def start_replay(self, video_file):
self.setMedia(QMediaContent(QUrl.fromLocalFile(video_file)))
self._playerWidget = ReplayHolder(self, self.parent())
self.setVideoOutput(self._playerWidget)
self.play()
self._playerWidget.setFullScreen(True)
def stop_replay(self, status):
if status==QMediaPlayer.StoppedState:
self._playerWidget.setFullScreen(False);
self._playerWidget.setVisible(False);
self.parent().endOfReplay()
class GameModule(Module): class GameModule(Module):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent, GameWidget()) super().__init__(parent, GameWidget())
...@@ -57,7 +84,8 @@ class GameModule(Module): ...@@ -57,7 +84,8 @@ class GameModule(Module):
self.ui.btnScore1.clicked.connect(lambda: self.goal(Side.Left)) self.ui.btnScore1.clicked.connect(lambda: self.goal(Side.Left))
self.ui.btnScore2.clicked.connect(lambda: self.goal(Side.Right)) self.ui.btnScore2.clicked.connect(lambda: self.goal(Side.Right))
self.replayer = None self.camera = None
self.video_player = None
def load(self): def load(self):
logging.debug('Loading GameModule') logging.debug('Loading GameModule')
...@@ -66,9 +94,9 @@ class GameModule(Module): ...@@ -66,9 +94,9 @@ class GameModule(Module):
self.timerUpdateChrono.start(1000) self.timerUpdateChrono.start(1000)
self.ui.lcdChrono.display(QTime(0,0).toString("hh:mm:ss")) self.ui.lcdChrono.display(QTime(0,0).toString("hh:mm:ss"))
self.showingReplay = False self.video_player = None
if self.replayer: if self.camera:
self.replayer.start_recording() self.camera.start_recording()
gameover_type = Settings['gameover.type'] gameover_type = Settings['gameover.type']
gameover_value = Settings['gameover.value'] gameover_value = Settings['gameover.value']
...@@ -87,10 +115,12 @@ class GameModule(Module): ...@@ -87,10 +115,12 @@ class GameModule(Module):
def unload(self): def unload(self):
logging.debug('Unloading GameModule') logging.debug('Unloading GameModule')
del self.gameStartTime
self.timerUpdateChrono.stop() self.timerUpdateChrono.stop()
if self.replayer: self.gameStartTime = None
self.replayer.stop_recording()
if self.camera:
self.camera.stop_recording()
def other(self, **kwargs): def other(self, **kwargs):
logging.debug('Other GameModule') logging.debug('Other GameModule')
...@@ -104,8 +134,7 @@ class GameModule(Module): ...@@ -104,8 +134,7 @@ class GameModule(Module):
elif key=='replayThread': elif key=='replayThread':
self.replayer = val self.replayer = val
def resizeEvent(self, event): def resizeEvent(self, event):
# 40% of the window width to have (5% margin)-(40% circle)-(10% middle)-(40% circle)-(5% margin) # 40% of the window width to have (5% margin)-(40% circle)-(10% middle)-(40% circle)-(5% margin)
btnDiameter = self.mainwin.width()*0.4 btnDiameter = self.mainwin.width()*0.4
...@@ -119,9 +148,13 @@ class GameModule(Module): ...@@ -119,9 +148,13 @@ class GameModule(Module):
def keyPressEvent(self, e): def keyPressEvent(self, e):
if e.key() == Qt.Key_Escape: if e.key() == Qt.Key_Escape:
self.handleCancel() ret = QMessageBox.question(self, 'Stop the match?', 'Do you really want to stop this match? It wont be saved.')
if ret == QMessageBox.Yes:
self.handleCancel()
elif e.key() == Qt.Key_Left: elif e.key() == Qt.Key_Left:
self.goal(Side.Left) self.goal(Side.Left)
elif e.key() == Qt.Key_Right: elif e.key() == Qt.Key_Right:
self.goal(Side.Right) self.goal(Side.Right)
...@@ -130,7 +163,7 @@ class GameModule(Module): ...@@ -130,7 +163,7 @@ class GameModule(Module):
self.ui.lcdChrono.display(QTime(0,0).addSecs(self.getGameTime()).toString("hh:mm:ss")) self.ui.lcdChrono.display(QTime(0,0).addSecs(self.getGameTime()).toString("hh:mm:ss"))
# Don't check scores while showing a replay to avoid closing the engame screen too soon # Don't check scores while showing a replay to avoid closing the engame screen too soon
if not self.showingReplay: if not self.video_player:
self.checkEndGame() self.checkEndGame()
def getGameTime(self): def getGameTime(self):
...@@ -149,30 +182,27 @@ class GameModule(Module): ...@@ -149,30 +182,27 @@ class GameModule(Module):
# Show replay # Show replay
# May require `sudo apt-get install qtmultimedia5-examples` in order to install the right libraries # May require `sudo apt-get install qtmultimedia5-examples` in order to install the right libraries
if self.camera:
replayFile = self.camera.stop_recording()
elif Settings['replay.debug']:
replayFile = Replay.Dummy()
else:
replayFile = ''
if replayFile and os.path.exists(replayFile):
if self.replayer: self.video_player = ReplayPlayer(self)
replayFile = self.replayer.stop_recording() self.video_player.start_replay(replayFile)
if not (self.replayer and Settings['replay.show'] and os.path.exists(replayFile)):
self.updateScores()
else: else:
self.showingReplay = True self.updateScores()
self.player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.player.stateChanged.connect(self.endOfReplay) def endOfReplay(self):
self.player.setMuted(True) self.video_player = None
self.player.setVideoOutput(self.ui.videoWidget)
self.player.setMedia(QMediaContent(QUrl.fromLocalFile(replayFile))) if self.gameStartTime:
self.player.play()
self.ui.videoWidget.setFullScreen(True)
def endOfReplay(self, status):
if status!=QMediaPlayer.PlayingState:
self.ui.videoWidget.setFullScreen(False);
self.showingReplay = False
self.updateScores() self.updateScores()
if self.replayer:
self.replayer.start_recording() if self.camera:
self.camera.start_recording()
def handleCancel(self): def handleCancel(self):
self.switchModule(modules.MenuModule) self.switchModule(modules.MenuModule)
...@@ -184,5 +214,5 @@ class GameModule(Module): ...@@ -184,5 +214,5 @@ class GameModule(Module):
start_timestamp = int(QDateTime(QDate.currentDate(), self.gameStartTime).toMSecsSinceEpoch()/1000) 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, players=self.players, winSide=winSide, scores=self.scores)
self.send(modules.EndGameModule, start_time=start_timestamp, duration=self.getGameTime()) self.send(modules.EndGameModule, start_time=start_timestamp, duration=self.getGameTime(), gameType=self)
self.switchModule(modules.EndGameModule) self.switchModule(modules.EndGameModule)
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
import logging import logging
from operator import attrgetter from operator import attrgetter
from PyQt5 import QtWidgets from PyQt5.QtWidgets import QWidget, QDialog, QListWidgetItem
from PyQt5.QtCore import QTime, Qt, QSize, QItemSelectionModel from PyQt5.QtCore import Qt, QItemSelectionModel
from module import Module
import modules import modules
from player import PlayerGuest, Player from module import Module
from ui.leaderboard_ui import Ui_Form as LeaderboardWidget from player import Player, Side
from ui.playerlist_ui import Ui_Form as PlayerListWidget from database import Database
class LeaderboardItemWidget(QtWidgets.QWidget): from ui.leaderboard_ui import Ui_Form as LeaderboardWidget
from ui.playerlist_ui import Ui_Form as PlayerListWidget
from ui.delete_dialog_ui import Ui_Dialog as PlayerDeleteDialog
class LeaderboardItemWidget(QWidget):
def __init__(self, parent, player): def __init__(self, parent, player):
QtWidgets.QWidget.__init__(self, parent) QWidget.__init__(self, parent)
self.ui = PlayerListWidget() self.ui = PlayerListWidget()
self.ui.setupUi(self) self.ui.setupUi(self)
...@@ -35,6 +36,23 @@ class LeaderboardItemWidget(QtWidgets.QWidget): ...@@ -35,6 +36,23 @@ class LeaderboardItemWidget(QtWidgets.QWidget):
self.ui.pushButton.clicked.connect(lambda: logging.debug('clicked')) self.ui.pushButton.clicked.connect(lambda: logging.debug('clicked'))
class DeleteDialog(QDialog):
def __init__(self, parent, player):
print('DeleteDialog {}'.format(player.name))
QDialog.__init__(self, parent)
self.ui = PlayerDeleteDialog()
self.ui.setupUi(self)
self.player = player
self.ui.lblTitle.setText(self.ui.lblTitle.text().format(player.name))
def check(self, rfid):
return rfid == -self.player.id
# Debug
def keyPressEvent(self, e):
if e.key() == Qt.Key_Return:
self.parent().send(modules.LeaderboardModule, rfid=self.player.rfid, source=Side.Right)
class LeaderboardModule(Module): class LeaderboardModule(Module):
def __init__(self, parent): def __init__(self, parent):
super().__init__(parent, LeaderboardWidget()) super().__init__(parent, LeaderboardWidget())
...@@ -51,6 +69,7 @@ class LeaderboardModule(Module): ...@@ -51,6 +69,7 @@ class LeaderboardModule(Module):
self.sortMethodAttr = ['lname', 'stats_property.victories', 'stats_property.goals_scored', 'stats_property.games_played', 'stats_property.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)
self.deleteDialog = None
def load(self): def load(self):
logging.debug('Loading LeaderboardModule') logging.debug('Loading LeaderboardModule')
...@@ -63,6 +82,18 @@ class LeaderboardModule(Module): ...@@ -63,6 +82,18 @@ class LeaderboardModule(Module):
def other(self, **kwargs): def other(self, **kwargs):
logging.debug('Other LeaderboardModule') logging.debug('Other LeaderboardModule')
for key, val in kwargs.items():
if key=='rfid' and self.deleteDialog and self.deleteDialog.check(val):
Database.instance().delete_player(self.deleteDialog.player.id)
# Reset the dialog and the player list
self.deleteDialog.close()
del self.deleteDialog
self.deleteDialog = None
self.players = []
self.ui.listWidget.clear()
self.loadList()
def changeSort(self, rbSort): def changeSort(self, rbSort):
self.selectedSort = self.sortMethodRB.index(rbSort) self.selectedSort = self.sortMethodRB.index(rbSort)
...@@ -72,12 +103,12 @@ class LeaderboardModule(Module): ...@@ -72,12 +103,12 @@ class LeaderboardModule(Module):
if self.players: if self.players:
self.ui.listWidget.clear() self.ui.listWidget.clear()
else: else:
self.players = [Player.fromRFID(id) for id in range(-2 , -7, -1)] self.players = Player.allPlayers()
self.players.sort(key=attrgetter(self.sortMethodAttr[self.selectedSort]), reverse=True) self.players.sort(key=attrgetter(self.sortMethodAttr[self.selectedSort]), reverse=True)
for player in self.players: for player in self.players:
item = QtWidgets.QListWidgetItem() item = QListWidgetItem()
playerWidget = LeaderboardItemWidget(self.ui.listWidget, player) playerWidget = LeaderboardItemWidget(self.ui.listWidget, player)
item.setSizeHint(playerWidget.size()) item.setSizeHint(playerWidget.size())
self.ui.listWidget.addItem(item) self.ui.listWidget.addItem(item)
...@@ -107,6 +138,10 @@ class LeaderboardModule(Module): ...@@ -107,6 +138,10 @@ class LeaderboardModule(Module):
elif e.key() == Qt.Key_Right: elif e.key() == Qt.Key_Right:
newSort = curSort+1 if curSort!=len(self.sortMethodRB)-1 else 0 newSort = curSort+1 if curSort!=len(self.sortMethodRB)-1 else 0
self.sortMethodRB[newSort].animateClick() self.sortMethodRB[newSort].animateClick()
elif e.key() == Qt.Key_Delete:
self.deleteDialog = DeleteDialog(self, self.players[curRow])
self.deleteDialog.open()
def handleExit(self): def handleExit(self):
self.switchModule(modules.MenuModule) self.switchModule(modules.MenuModule)
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
...@@ -10,7 +8,6 @@ import logging ...@@ -10,7 +8,6 @@ import logging
from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
import modules import modules
from module import Module from module import Module
......
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
......
...@@ -7,10 +7,8 @@ Created on Wed Apr 18 18:34:40 2018 ...@@ -7,10 +7,8 @@ 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):
''' '''
...@@ -24,6 +22,8 @@ class Side(Enum): ...@@ -24,6 +22,8 @@ class Side(Enum):
def opposite(self): def opposite(self):
return Side.Right if self==Side.Left else Side.Left return Side.Right if self==Side.Left else Side.Left
from database import Database, DatabaseError
class Player(): class Player():
__query_infos = 'SELECT id, fname, lname, pic FROM Players WHERE rfid==?' __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_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==?)'
...@@ -31,8 +31,9 @@ class Player(): ...@@ -31,8 +31,9 @@ class Player():
_placeholder_pic_path = ':ui/img/placeholder_head.jpg' _placeholder_pic_path = ':ui/img/placeholder_head.jpg'
def __init__(self, id, fname, lname, pic_path, stats): def __init__(self, id, rfid, fname, lname, pic_path, stats):
self.id = id self.id = id
self.rfid = rfid
self.fname = fname self.fname = fname
self.lname = lname self.lname = lname
self.pic_path = pic_path if pic_path else Player._placeholder_pic_path # Default pic if None self.pic_path = pic_path if pic_path else Player._placeholder_pic_path # Default pic if None
...@@ -46,12 +47,16 @@ class Player(): ...@@ -46,12 +47,16 @@ class Player():
# Retrieve generic informations # Retrieve generic informations
id, fname, lname, pic = db.select_one(Player.__query_infos, rfid) id, fname, lname, pic = db.select_one(Player.__query_infos, rfid)
# Retrieve stats # Retrieve stats
stats = {} stats = {}
stats['time_played'], stats['goals_scored'], stats['games_played'] = db.select_one(Player.__query_time_goals_games, id, id) stats['time_played'], stats['goals_scored'], stats['games_played'] = db.select_one(Player.__query_time_goals_games, id, id)
stats['victories'], = db.select_one(Player.__query_victories, id) stats['victories'], = db.select_one(Player.__query_victories, id)
return Player(id, fname, lname, pic, stats) for key, val in stats.items():
if val==None:
stats[key] = 0
return Player(id, rfid, fname, lname, pic, stats)
except DatabaseError as e: except DatabaseError as e:
logging.warn('DB Error: {}'.format(e)) logging.warn('DB Error: {}'.format(e))
...@@ -71,6 +76,11 @@ class Player(): ...@@ -71,6 +76,11 @@ class Player():
@property @property
def stats_property(self): def stats_property(self):
'''
Compatibility property allowing to access stats as a object member and not dict
ex: player.stats['victories'] can be accessed with player.stats_property.victories'
This is mostly used for sorting players in leaderboard.py
'''
class Stat: class Stat:
def __init__(self, stats): def __init__(self, stats):
self.victories = stats['victories'] self.victories = stats['victories']
...@@ -79,6 +89,10 @@ class Player(): ...@@ -79,6 +89,10 @@ class Player():
self.games_played = stats['games_played'] self.games_played = stats['games_played']
return Stat(self.stats) return Stat(self.stats)
@staticmethod
def allPlayers():
return [Player.fromRFID(rfid) for rfid, in Database.instance().select_all_rfid()]
PlayerGuest = Player.fromRFID(-1) PlayerGuest = Player.fromRFID(-1)
PlayerEmpty = Player(-1, '', '', Player._placeholder_pic_path, {'time_played':'', 'goals_scored':'', 'games_played':'', 'victories': ''}) PlayerEmpty = Player(-1, -42, '', '', Player._placeholder_pic_path, {'time_played':'', 'goals_scored':'', 'games_played':'', 'victories': ''})
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
from threading import Thread, Event from threading import Thread, Event
from main import MainWin, OnRasp from main import getContent, OnRasp
from settings import Settings from settings import Settings
if OnRasp: if OnRasp:
...@@ -17,7 +15,7 @@ if OnRasp: ...@@ -17,7 +15,7 @@ if OnRasp:
class Replay(Thread): class Replay(Thread):
def __init__(self, side): def __init__(self, side):
Thread.__init__(self) Thread.__init__(self)
self.replayPath = MainWin.getContent('Replay {}.mp4'.format(side.name)) self.replayPath = getContent('Replay {}.mp4'.format(side.name))
self.shutdown = False self.shutdown = False
self.start_flag = Event() self.start_flag = Event()
...@@ -68,12 +66,16 @@ class Replay(Thread): ...@@ -68,12 +66,16 @@ 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 # 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()
self.stream.close() self.stream.close()
@classmethod
def Dummy(cls):
return getContent('Replay Left.mp4')
@staticmethod @staticmethod
def isCamAvailable(): def isCamAvailable():
return OnRasp # and other checks (ToDo) return OnRasp # and other checks (ToDo)
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Wed Apr 18 18:34:40 2018
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne @author: Antoine Lima, Leo Reynaert, Domitille Jehenne
""" """
import json import json
import main
class MyEncoder(json.JSONEncoder): class MyEncoder(json.JSONEncoder):
def default(self, obj): def default(self, obj):
...@@ -116,8 +113,9 @@ class SettingsHolder(object): ...@@ -116,8 +113,9 @@ class SettingsHolder(object):
del self.settingsPath del self.settingsPath
with open(settingsPath, 'w') as f: with open(settingsPath, 'w') as f:
content = json.dump(self.__dict__, f, cls=MyEncoder, indent=4) json.dump(self.__dict__, f, cls=MyEncoder, indent=4)
self.settingsPath = settingsPath self.settingsPath = settingsPath
Settings = SettingsHolder(main.MainWin.getContent('settings.json')) from main import getContent
Settings = SettingsHolder(getContent('settings.json'))
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>720</width>
<height>405</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="lblTitle">
<property name="font">
<font>
<pointsize>25</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Deleting {}'s profile</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="lblMsg">
<property name="font">
<font>
<pointsize>20</pointsize>
</font>
</property>
<property name="text">
<string>Pass your badge to validate this...</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
...@@ -194,26 +194,8 @@ ...@@ -194,26 +194,8 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QVideoWidget" name="videoWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>QVideoWidget</class>
<extends>QWidget</extends>
<header>PyQt5.QtMultimediaWidgets</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne
"""
import os
OnRasp = os.uname()[1] == 'raspberrypi'
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QGraphicsBlurEffect, QApplication
from PyQt5.QtCore import QTime, Qt
from ui.main_ui import Ui_MainWindow
import modules
class MainWin(QtWidgets.QMainWindow):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
#Background blur
bgBlur = QGraphicsBlurEffect()
bgBlur.setBlurHints(QGraphicsBlurEffect.QualityHint)
#bgBlur.setBlurRadius(5)
#self.ui.panels.setGraphicsEffect(bgBlur)
# Module loading
self.modules = [
modules.MenuModule(self),
modules.AuthQuickModule(self),
modules.AuthLeagueModule(self),
modules.GameModule(self),
modules.EndGameModule(self),
modules.LeaderboardModule(self),
modules.OptionsModule(self)
]
for mod in self.modules:
self.ui.panels.addWidget(mod)
self.ui.panels.setCurrentIndex(0)
self.ui.panels.currentWidget().setFocus()
self.ui.panels.setFocusProxy(self.ui.panels.currentWidget())
self.ui.panels.currentWidget().load()
self.displaySystemTime()
self.startTimer(1000)
self._loadSettings()
#def eventFilter(target, event):
# return event.type()==QEvent.KeyPress and event.key() not in acceptedKeys
def timerEvent(self, e):
self.displaySystemTime()
def displaySystemTime(self):
self.ui.lcdTime.display(QTime.currentTime().toString("hh:mm:ss"))
def findMod(self, type):
mod_idx = [i for i, x in enumerate(self.modules) if isinstance(x, type)]
return -1 if len(mod_idx)==0 else mod_idx[0]
def dispatchMessage(self, msg, toType=None, toAll=False):
if toType!=None:
modulesIdx = [self.findMod(toType)]
else:
modulesIdx = self.modules if toAll else [self.findMod(type(self.ui.panels.currentWidget()))]
for modIdx in modulesIdx:
self.modules[modIdx].other(**msg)
def _loadSettings(self):
from settings import Settings
if Settings['ui.fullscreen']:
self.showFullScreen()
QApplication.setOverrideCursor(Qt.BlankCursor);
else:
self.showNormal()
QApplication.setOverrideCursor(Qt.ArrowCursor);
\ No newline at end of file
...@@ -299,7 +299,7 @@ QRadioButton::indicator { ...@@ -299,7 +299,7 @@ QRadioButton::indicator {
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="title"> <property name="title">
<string>Number of players per side</string> <string>League - Number of players per side</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignCenter</set> <set>Qt::AlignCenter</set>
......
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QLabel" name="lblFName"> <widget class="QLabel" name="lblLName">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>200</width> <width>200</width>
...@@ -94,7 +94,7 @@ ...@@ -94,7 +94,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="lblLName"> <widget class="QLabel" name="lblFName">
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>200</width> <width>200</width>
......