Files
Quadraludi/lobby.py
2019-05-11 21:34:18 +02:00

316 lines
10 KiB
Python

import tkinter as tk
import color
import main as quadraLudi
import importlib
import sys
from socket import socket, AF_INET, SOCK_STREAM
from datetime import datetime
# Server
SERVER_ADDRESS = 'eveldee.ddns.net'
SERVER_PORT = 1409
# Tags
TEXT_TIME = 'Time'
TEXT_PSEUDO = 'Pseudo'
TEXT_SEPARATION = 'Separation'
TEXT_MESSAGE = 'Message'
TEXT_RANK = 'Rank'
TEXT_SHIFT_SEPARATOR = 'Shift_Separation'
TEXT_SCORE = 'Score'
class Lobby:
def __init__(self):
# Win
win = tk.Tk()
win.title('QuadraLudi - Lobby')
win.geometry('800x640')
win['bg'] = color.LEVEL_1
# win.resizable(False, False)
self.win = win
# Frame
self.frame = None
# Variables
self.pseudo = tk.StringVar(win, "")
self.message = tk.StringVar(win, "")
self.scoreLen = 0
self.chatLen = 0
# Logo
if sys.platform.startswith('win'):
win.iconbitmap('res/logo/icon.ico')
else:
logo = tk.PhotoImage('res/logo/icon.png')
win.tk.call('wm', 'iconphoto', win._w, logo)
# Network
self.networkManager = NetworkManager()
self.isUpdating = True
# First
self.nameDisplay()
def resetFrame(self):
if self.frame != None:
self.frame.destroy()
frame = tk.Frame(self.win, bg = color.LEVEL_1)
frame.pack(fill = tk.BOTH, expand = True, padx = 5, pady = 5)
self.frame = frame
def nameDisplay(self):
self.resetFrame()
# Place
pseudoFrame = tk.LabelFrame(self.frame, text = 'Pseudo', bg = color.LEVEL_1, fg = color.PURPLE)
pseudoFrame.place(relx = 0.5, rely = 0.5, width = 200, height = 50, anchor = 'center')
entry = tk.Entry(pseudoFrame, textvariable = self.pseudo, bg = color.LEVEL_2, fg = color.GHOST)
entry.bind('<Return>', lambda e: self.nameConfirm())
entry.pack(side = tk.LEFT, expand = True)
entry.focus()
tk.Button(
pseudoFrame, text = 'Valider', bg = color.PURPLE, fg = color.LEVEL_1,
command = lambda: self.nameConfirm()
).pack(side = tk.LEFT, expand = True)
def lobbyDisplay(self):
self.resetFrame()
# Configure layout
mainFrame = self.frame
mainFrame.columnconfigure(0, weight = 4)
mainFrame.columnconfigure(1, weight = 1)
mainFrame.rowconfigure(0, weight = 1)
# Score
scoreFrame = tk.LabelFrame(mainFrame, text = 'Score', bg = color.LEVEL_1, fg = color.PURPLE, font = (None, 12))
scoreFrame.grid(column = 0, row = 0, sticky = 'NSEW', padx = 5)
scoreFrameDisplay = tk.Text(scoreFrame, bg = color.LEVEL_1, bd = 0, wrap = tk.WORD, font = (None, 11), spacing1 = 7)
scoreFrameDisplay.tag_config(TEXT_RANK, foreground = color.PURPLE)
scoreFrameDisplay.tag_config(TEXT_SHIFT_SEPARATOR, foreground = color.BLUE)
scoreFrameDisplay.tag_config(TEXT_PSEUDO, foreground = color.GREEN)
scoreFrameDisplay.tag_config(TEXT_SEPARATION, foreground = color.YELLOW)
scoreFrameDisplay.tag_config(TEXT_SCORE, foreground = color.PINK)
scoreFrameScroll = tk.Scrollbar(scoreFrame, command = scoreFrameDisplay.yview)
scoreFrameDisplay.config(yscrollcommand = scoreFrameScroll.set)
scoreFrameScroll.pack(side = tk.RIGHT, fill = tk.Y)
scoreFrameDisplay.pack(side = tk.LEFT, fill = tk.BOTH)
self.scoreFrameDisplay = scoreFrameDisplay
# Chat Frame
chatFrame = tk.LabelFrame(mainFrame, text = 'Chat', bg = color.LEVEL_1, fg = color.PURPLE, font = (None, 12))
chatFrame.grid(column = 1, row = 0, sticky = 'NSEW', padx = 5)
# Chat input
bottomFrame = tk.Frame(chatFrame, bg = color.LEVEL_5, name = 'bottomFrame')
bottomFrame.pack(side = tk.BOTTOM, fill = tk.X, expand = True)
entry = tk.Entry(bottomFrame, textvariable = self.message, bg = color.LEVEL_1, fg = color.GHOST, font = (None, 11))
entry.bind('<Return>', lambda e: self.sendMessage())
entry.pack(side = tk.LEFT, fill = tk.BOTH, expand = True, padx = 5, pady = 5)
entry.focus()
self.chatEntry = entry
tk.Button(
bottomFrame, text = 'Envoyer', bg = color.PURPLE, fg = color.LEVEL_1,
command = lambda: self.sendMessage()
).pack(side = tk.LEFT, padx = 5, pady = 5)
# Chat display
chatTextFrame = tk.Frame(chatFrame, bg = color.LEVEL_1)
chatTextFrame.pack(side = tk.TOP, fill = tk.BOTH, pady = 2)
chatTextDisplay = tk.Text(chatTextFrame, bg = color.LEVEL_1, bd = 0, state = tk.DISABLED, wrap = tk.WORD, font = (None, 11), spacing1 = 7)
chatTextDisplayScroll = tk.Scrollbar(chatTextFrame, command = chatTextDisplay.yview)
chatTextDisplay.config(yscrollcommand = chatTextDisplayScroll.set)
chatTextDisplayScroll.pack(side = tk.RIGHT, fill = tk.Y)
chatTextDisplay.pack(side = tk.LEFT, fill = tk.BOTH, padx = 3)
self.chatTextDisplay = chatTextDisplay
chatTextDisplay.tag_config(TEXT_TIME, foreground = color.PURPLE)
chatTextDisplay.tag_config(TEXT_PSEUDO, foreground = color.GREEN)
chatTextDisplay.tag_config(TEXT_SEPARATION, foreground = color.YELLOW)
chatTextDisplay.tag_config(TEXT_MESSAGE, foreground = color.PINK)
# Play
tk.Button(
mainFrame, text = 'Jouer', bg = color.PURPLE, fg = color.LEVEL_1,
command = lambda: self.play()
).grid(column = 0, row = 1, columnspan = 2, sticky = 'NSEW', padx = 5, pady = 5)
self.update()
def update(self):
if self.isUpdating:
messages = self.networkManager.requestMessages()
scores = self.networkManager.requestScores()
# Score
if len(scores) > self.scoreLen:
self.scoreLen = len(scores)
rank = 1
for score in scores:
pseudo, score = score.split(';')
self.addScoreEntry(rank, pseudo, score)
rank += 1
# Chat
if len(messages) > self.chatLen:
for messsage in messages[self.chatLen:]:
split = messsage.split(';')
time, pseudo, content = split[0], split[1], ';'.join(split[2:])
self.addChatEntry(time, pseudo, content)
self.chatLen = len(messages)
self.win.after(1000, lambda: self.update())
def nameConfirm(self):
pseudo = self.pseudo.get().replace('@', ' ').replace(';', ' ')
if pseudo == '『 』':
return
elif pseudo == ' ':
self.pseudo.set('『 』')
elif pseudo == '' or pseudo.isspace():
return
self.lobbyDisplay()
def sendMessage(self):
# Check empty
message = self.message.get()
pseudo = self.pseudo.get()
if message == '':
return
# Send
message = message.replace('@', ' ')
self.networkManager.sendMessage(pseudo, message)
# Clear
now = datetime.now().time()
time = f"{now.hour}:{str(now.minute).zfill(2)}"
self.addChatEntry(time, pseudo, message)
self.chatLen += 1
self.message.set('')
def sendScore(self, score):
self.networkManager.sendScore(self.pseudo.get(), score)
def play(self):
# Reload
importlib.reload(quadraLudi)
# Minimize
self.win.iconify()
# Display
self.isUpdating = False
quadraLudi.main(self.win, lambda score: self.playEnd(score))
def playEnd(self, score):
# Display window, focus
self.win.deiconify()
self.win.focus_force()
self.chatEntry.focus()
self.isUpdating = True
# Send score
self.sendScore(score)
def addScoreEntry(self, rank, pseudo, score):
self.scoreFrameDisplay['state'] = tk.NORMAL
self.scoreFrameDisplay.insert(tk.END, f'{rank}', TEXT_RANK)
self.scoreFrameDisplay.insert(tk.END, f'> ', TEXT_SHIFT_SEPARATOR)
self.scoreFrameDisplay.insert(tk.END, f'{pseudo}', TEXT_PSEUDO)
self.scoreFrameDisplay.insert(tk.END, f': ', TEXT_SEPARATION)
self.scoreFrameDisplay.insert(tk.END, f'{score}\n', TEXT_SCORE)
self.scoreFrameDisplay['state'] = tk.DISABLED
def addChatEntry(self, time, pseudo, message):
self.chatTextDisplay['state'] = tk.NORMAL
self.chatTextDisplay.insert(tk.END, f'[{time}] ', TEXT_TIME)
self.chatTextDisplay.insert(tk.END, f'({pseudo})', TEXT_PSEUDO)
self.chatTextDisplay.insert(tk.END, ': ', TEXT_SEPARATION)
self.chatTextDisplay.insert(tk.END, f'{message}\n', TEXT_MESSAGE)
self.chatTextDisplay.see(tk.END)
self.chatTextDisplay['state'] = tk.DISABLED
def show(self):
self.win.mainloop()
class NetworkManager:
def __init__(self):
self.address = (SERVER_ADDRESS, SERVER_PORT)
def _createSocket(self):
# Create TCP socket
sock = socket(AF_INET, SOCK_STREAM)
sock.connect(self.address)
return sock
def _sendPacket(self, command, content):
packet = f"{command}\n{content}".encode('utf-8')
length = len(packet).to_bytes(4, 'little')
sock = self._createSocket()
sock.sendall(length)
sock.sendall(packet)
sock.close()
def _receivePacket(self, command):
sock = self._createSocket()
# Send command
packet = f"{command}\n".encode('utf-8')
length = len(packet).to_bytes(4, 'little')
sock.sendall(length)
sock.sendall(packet)
# Receive
# Length
length = sock.recv(4)
length = int.from_bytes(length, 'little')
# Data
response = sock.recv(length)
response = response.decode('utf-8')
sock.close()
split = response.split('\n')
return (split[0], '\n'.join(split[1:]))
def sendMessage(self, pseudo, message):
now = datetime.now().time()
time = f"{now.hour}:{str(now.minute).zfill(2)}"
self._sendPacket('ChatAdd', f"{time};{pseudo};{message}")
def sendScore(self, pseudo, score):
self._sendPacket('ScoreAdd', f"{pseudo};{score}")
def requestMessages(self):
_, response = self._receivePacket('ChatRequest')
return response.split('@')
def requestScores(self):
_, response = self._receivePacket('ScoreRequest')
return response.split('@')
def main():
lobby = Lobby()
lobby.show()
if __name__ == "__main__":
main()