RonRunner/ronrunner_1.0~beta1-1/usr/bin/ronrunner
2026-02-09 16:15:32 +01:00

329 lines
No EOL
12 KiB
Python
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
from tkinter import *
from tkinter import ttk
from player import Player
from table import Table
PLAYERS: list[Player] = []
TABLES: list[Table] = []
INITIAL_SCORE = 30000
UMA1 = 15000
UMA2 = 5000
UMA3 = -5000
UMA4 = -15000
# init tkinter
root = Tk()
root.title("🀄 RonRunner - Mahjong Tournament Scoring 🀄")
root.geometry("800x500")
# configure style
style = ttk.Style()
style.theme_use('clam')
# configure scrollbar
canvas = Canvas(root, highlightthickness=0)
scrollbar = ttk.Scrollbar(root, orient='vertical', command=canvas.yview)
scrollbarFrame = ttk.Frame(canvas)
scrollbarFrame.bind(
"<Configure>",
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
)
canvas.create_window((0, 0), window=scrollbarFrame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
canvas.grid(column=0, row=0, sticky="nsew")
scrollbar.grid(column=1, row=0, sticky="ns")
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
frm = scrollbarFrame
# activate scrollbar with pad/mouse
def _on_mousewheel(event):
canvas.yview_scroll(int(-1*(event.delta/120)), "units")
def _on_scroll_up(event):
canvas.yview_scroll(-1, "units")
def _on_scroll_down(event):
canvas.yview_scroll(1, "units")
def _bind_mousewheel(widget):
widget.bind("<MouseWheel>", _on_mousewheel)
widget.bind("<Button-4>", _on_scroll_up)
widget.bind("<Button-5>", _on_scroll_down)
for child in widget.winfo_children():
_bind_mousewheel(child)
_bind_mousewheel(frm)
# init ui
# players
ttk.Label(frm, text="Rank", font=("Helvetica", 11, "bold")).grid(column=0, row=0, padx=10, pady=10)
ttk.Label(frm, text="Player", font=("Helvetica", 11, "bold")).grid(column=1, row=0, padx=10, pady=10)
ttk.Label(frm, text="Score", font=("Helvetica", 11, "bold")).grid(column=2, row=0, padx=10, pady=10)
entry_player_name = ttk.Entry(frm, width=30, font=("Helvetica", 10))
entry_player_name.grid(column=1, row=len(PLAYERS) + 1, padx=10, pady=10)
# adding player
def addPlayer(entry: Entry):
txt = entry.get()
if txt and not txt in [p.name for p in PLAYERS]:
PLAYERS.append(Player(txt))
entry.delete(0, END)
refreshUi()
btn_player_name = ttk.Button(frm, text=" Add", command=lambda : addPlayer(entry_player_name))
btn_player_name.grid(column=2, row=len(PLAYERS) + 1, padx=10, pady=10)
# tables
# show tables
# create table
def addTable():
popup = Toplevel(root)
popup.title("Create a new table")
popup.geometry("300x250")
p1_var = StringVar(value="Select player 1")
p2_var = StringVar(value="Select player 2")
p3_var = StringVar(value="Select player 3")
p4_var = StringVar(value="Select player 4")
# names list
names = [p.name for p in PLAYERS]
ttk.Label(popup, text="Player 1", font=("Helvetica", 10)).grid(column=0, row=0, padx=10, pady=10)
combo1 = ttk.Combobox(popup, textvariable=p1_var, values=names, state="readonly", width=25)
combo1.grid(column=1, row=0, padx=10, pady=5)
ttk.Label(popup, text="Player 2", font=("Helvetica", 10)).grid(column=0, row=1, padx=10, pady=10)
combo2 = ttk.Combobox(popup, textvariable=p2_var, values=names, state="readonly", width=25)
combo2.grid(column=1, row=1, padx=10, pady=5)
ttk.Label(popup, text="Player 3", font=("Helvetica", 10)).grid(column=0, row=2, padx=10, pady=10)
combo3 = ttk.Combobox(popup, textvariable=p3_var, values=names, state="readonly", width=25)
combo3.grid(column=1, row=2, padx=10, pady=5)
ttk.Label(popup, text="Player 4", font=("Helvetica", 10)).grid(column=0, row=3, padx=10, pady=10)
combo4 = ttk.Combobox(popup, textvariable=p4_var, values=names, state="readonly", width=25)
combo4.grid(column=1, row=3, padx=10, pady=5)
def createTable():
p1_name = p1_var.get()
p2_name = p2_var.get()
p3_name = p3_var.get()
p4_name = p4_var.get()
if p1_name != "Select player 1" and p2_name != "Select player 2" and p3_name != "Select player 3" and p4_name != "Select player 4":
p1 = next((p for p in PLAYERS if p.name == p1_name), None)
p2 = next((p for p in PLAYERS if p.name == p2_name), None)
p3 = next((p for p in PLAYERS if p.name == p3_name), None)
p4 = next((p for p in PLAYERS if p.name == p4_name), None)
if len(set([p1, p2, p3, p4])) == 4:
TABLES.append(Table(p1, p2, p3, p4, INITIAL_SCORE))
cancelTopLevel(popup)
ttk.Button(popup, text="✓ Create", command=createTable).grid(column=1, row=4, padx=10, pady=20)
ttk.Button(popup, text="✕ Cancel", command=lambda : cancelTopLevel(popup)).grid(column=2, row=4, padx=10, pady=20)
btn_create_table = ttk.Button(frm, text=" Create table", command=addTable)
btn_create_table.grid(column=1, row=len(PLAYERS) + 2, padx=10, pady=20)
# table history
# TODO
# refresh ui
def refreshUi():
# update scores
updateScore()
# remove old labels
for widget in frm.winfo_children():
if widget not in [
entry_player_name,
btn_player_name,
btn_create_table
]:
info = widget.grid_info()
if info.get('row', 0) > 0 and info.get('row', 0) < 100:
widget.destroy()
# show players
PLAYERS.sort(key=lambda p : p.score, reverse=True)
for i in range(len(PLAYERS)):
ttk.Label(frm, text=f"{i+1}", font=("Helvetica", 10, "bold"), foreground="#FF6B6B").grid(column=0, row=i+1, padx=10, pady=8)
playerLabel = ttk.Label(frm, text=PLAYERS[i].name, font=("Helvetica", 10), foreground="#0066CC")
playerLabel.grid(column=1, row=i+1, padx=10, pady=8)
ttk.Label(frm, text=f"{PLAYERS[i].score}", font=("Helvetica", 10, "bold"), foreground="#2ECC71").grid(column=2, row=i+1, padx=10, pady=8)
# change player name
def onClick(event, player=PLAYERS[i]):
popup = Toplevel(root)
popup.title(f"Edit - {player.name}")
popup.geometry("300x150")
entry = ttk.Entry(popup, width=30, font=("Helvetica", 10))
entry.insert(0, player.name)
entry.grid(column=0, row=0, padx=10, pady=10)
def changeName():
name = entry.get()
if name and not name in [p.name for p in PLAYERS]:
player.name = name
entry.delete(0, END)
cancelTopLevel(popup)
ttk.Button(popup, text="✓ Change", command=changeName).grid(column=1, row=0, padx=5, pady=10)
ttk.Button(popup, text="✕ Cancel", command=lambda : cancelTopLevel(popup)).grid(column=2, row=0, padx=5, pady=10)
playerLabel.bind("<Button-1>", onClick)
# adding player
entry_player_name.grid(column=1, row=len(PLAYERS) + 1)
btn_player_name.grid(column=2, row=len(PLAYERS) + 1)
# show tables
ttk.Label(frm, text="Tables", font=("Helvetica", 12, "bold")).grid(column=0, row=len(PLAYERS) + 3, columnspan=3, padx=10, pady=20)
for i, table in enumerate(TABLES):
nbCol = 4
col = i % nbCol
row = len(PLAYERS) + 4 + (i // nbCol)
style = ttk.Style()
style.theme_use('clam')
if (table.s1 + table.s2 + table.s3 + table.s4 != 4 * INITIAL_SCORE):
colour = "#F13C3C"
elif ([table.s1, table.s2, table.s3, table.s4] == [INITIAL_SCORE] * 4):
colour = "#F1933C"
else:
colour = "#3CEB2C"
style.configure("Table.TLabelframe", borderwidth=2, relief="solid", bordercolor=colour)
tableFrame = ttk.LabelFrame(frm, text=f"Table {i+1}", padding=10, style="Table.TLabelframe")
tableFrame.grid(column=col, row=row, padx=10, pady=10, sticky="ew")
ttk.Label(tableFrame, text=f"{table.p1.name} : {table.s1}", font=("Helvetica", 9)).pack()
ttk.Label(tableFrame, text=f"{table.p2.name} : {table.s2}", font=("Helvetica", 9)).pack()
ttk.Label(tableFrame, text=f"{table.p3.name} : {table.s3}", font=("Helvetica", 9)).pack()
ttk.Label(tableFrame, text=f"{table.p4.name} : {table.s4}", font=("Helvetica", 9)).pack()
def onClick(event, table=table):
popup = Toplevel(root)
popup.title(f"Edit table")
popup.geometry("300x250")
p1_var = StringVar(value=table.p1.name)
p2_var = StringVar(value=table.p2.name)
p3_var = StringVar(value=table.p3.name)
p4_var = StringVar(value=table.p4.name)
# names list
names = [p.name for p in PLAYERS]
ttk.Label(popup, text="Player 1", font=("Helvetica", 10)).grid(column=0, row=0, padx=10, pady=10)
combo1 = ttk.Combobox(popup, textvariable=p1_var, values=names, state="readonly", width=25)
combo1.grid(column=1, row=0, padx=10, pady=5)
entry1 = ttk.Entry(popup, width=30, font=("Helvetica", 10))
entry1.insert(0, table.s1)
entry1.grid(column=2, row=0, padx=10, pady=10)
ttk.Label(popup, text="Player 2", font=("Helvetica", 10)).grid(column=0, row=1, padx=10, pady=10)
combo2 = ttk.Combobox(popup, textvariable=p2_var, values=names, state="readonly", width=25)
combo2.grid(column=1, row=1, padx=10, pady=5)
entry2 = ttk.Entry(popup, width=30, font=("Helvetica", 10))
entry2.insert(0, table.s2)
entry2.grid(column=2, row=1, padx=10, pady=10)
ttk.Label(popup, text="Player 3", font=("Helvetica", 10)).grid(column=0, row=2, padx=10, pady=10)
combo3 = ttk.Combobox(popup, textvariable=p3_var, values=names, state="readonly", width=25)
combo3.grid(column=1, row=2, padx=10, pady=5)
entry3 = ttk.Entry(popup, width=30, font=("Helvetica", 10))
entry3.insert(0, table.s3)
entry3.grid(column=2, row=2, padx=10, pady=10)
ttk.Label(popup, text="Player 4", font=("Helvetica", 10)).grid(column=0, row=3, padx=10, pady=10)
combo4 = ttk.Combobox(popup, textvariable=p4_var, values=names, state="readonly", width=25)
combo4.grid(column=1, row=3, padx=10, pady=5)
entry4 = ttk.Entry(popup, width=30, font=("Helvetica", 10))
entry4.insert(0, table.s4)
entry4.grid(column=2, row=3, padx=10, pady=10)
def changeTable():
p1_name = p1_var.get()
p2_name = p2_var.get()
p3_name = p3_var.get()
p4_name = p4_var.get()
try:
s1 = int(entry1.get())
s2 = int(entry2.get())
s3 = int(entry3.get())
s4 = int(entry4.get())
except:
return
if p1_name != "Select player 1" and p2_name != "Select player 2" and p3_name != "Select player 3" and p4_name != "Select player 4":
p1 = next((p for p in PLAYERS if p.name == p1_name), None)
p2 = next((p for p in PLAYERS if p.name == p2_name), None)
p3 = next((p for p in PLAYERS if p.name == p3_name), None)
p4 = next((p for p in PLAYERS if p.name == p4_name), None)
if len(set([p1, p2, p3, p4])) == 4:
table.p1 = p1
table.p2 = p2
table.p3 = p3
table.p4 = p4
table.s1 = s1
table.s2 = s2
table.s3 = s3
table.s4 = s4
cancelTopLevel(popup)
ttk.Button(popup, text="✓ Ok", command=changeTable).grid(column=1, row=4, padx=10, pady=20)
ttk.Button(popup, text="✕ Cancel", command=lambda : cancelTopLevel(popup)).grid(column=2, row=4, padx=10, pady=20)
tableFrame.bind("<Button-1>", onClick)
for child in tableFrame.winfo_children():
child.bind("<Button-1>", onClick)
# adding table
btn_create_table.grid(column=1, row=len(PLAYERS) + 2, padx=10, pady=20)
def cancelTopLevel(tl: Toplevel):
tl.destroy()
refreshUi()
def updateScore():
for p in PLAYERS:
p.score = 0
for t in TABLES:
scores = [
(t.p1, t.s1),
(t.p2, t.s2),
(t.p3, t.s3),
(t.p4, t.s4)
]
scores.sort(key=lambda c : c[1], reverse=True)
scores[0][0].score += scores[0][1] + UMA1 - INITIAL_SCORE
scores[1][0].score += scores[1][1] + UMA2 - INITIAL_SCORE
scores[2][0].score += scores[2][1] + UMA3 - INITIAL_SCORE
scores[3][0].score += scores[3][1] + UMA4 - INITIAL_SCORE
refreshUi()
root.mainloop()