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('', 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('', 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()