tkinter sorting txt file for leaderboard - python

I've been coding a multiplication game where you can practice multiplications. I'm trying to add in a leaderboard using an external txt file that would sort by the highest percentage but have been stuck for quite a while now. It's able to append the new data and show the results. The txt file is laid out as name, age, score (Jackie Welles, 16, 70%)
from tkinter import *
import random
from tkinter import messagebox
class Timestable:
def __init__(self, parent):
Timestable.f1 = Frame(parent)
Timestable.f1.grid()
Timestable.f2 = Frame(parent)
Timestable.f2.grid()
Timestable.f2.grid_forget()
Timestable.f3 = Frame(parent)
Timestable.f3.grid()
Timestable.f3.grid_forget()
#frame 1 ========================================================
Label(self.f1,text="Multiplication Practice").grid()
Label(self.f1,text="Name:").grid()
Timestable.name = Entry (self.f1)
Timestable.name.grid()
Label(self.f1,text="Age").grid()
Timestable.age = Entry (self.f1)
Timestable.age.grid()
Timestable.incorrect=[]
Timestable.user = []
Timestable.checked1 = IntVar()
Timestable.checked2 = IntVar()
self.c1 = Checkbutton(self.f1, text='1',variable=Timestable.checked1,onvalue=1,offvalue=0,command=lambda:data.save(self))
self.c1.grid()
self.c2 = Checkbutton(self.f1, text='2', variable=Timestable.checked2,onvalue=1,offvalue=0,command=lambda:data.save(self))
self.c2.grid()
Timestable.w = Spinbox(self.f1, from_=1, to=5)
Timestable.w.grid()
Button(self.f1,text="Start", command=lambda: data.start(self)).grid()
#frame 2 ========================================================
Label(self.f2,text="Frame 2 ").grid()
self.x=0
self.correct=0
sub = lambda: data.Submit(self)
Button(self.f2,text="submit", command=sub).grid()
Timestable.entryWidget = Entry (self.f2)
Timestable.entryWidget.grid()
#frame 3 ========================================================
Label(self.f3,text="Frame 3 ").grid()
Timestable.congrats=Label(Timestable.f3,text="")
Timestable.congrats.grid()
Timestable.incdisplay = Label(Timestable.f3,text = Timestable.incorrect)
Timestable.incdisplay.grid()
Timestable.my_text=Text(self.f3,width=40,height=10)
Timestable.my_text.grid()
class data:
def openleader(self):
file=open('Leaderboard.txt',"a")
file.write(Timestable.name.get()+", "+Timestable.age.get()+", "+str(data.percent)+"%""\n")
file.close
file=open("Leaderboard.txt","r")
idk=file.readlines()
Timestable.my_text.insert(END,idk)
def save(self):
if Timestable.checked1.get() == 1:
Timestable.user.append(1)
if Timestable.checked2.get() == 1:
Timestable.user.append(2)
def clear_text(self):
Timestable.entryWidget.delete(0, 'end')
def Questions(self):
number1 = random.choice(Timestable.user)
number2 = random.randrange(1,12)
self.answer = number1 * number2
self.prompt = (str(number1) + " X " + str(number2))
quest = Label(Timestable.f2, text=self.prompt, width=len(self.prompt))
quest.grid()
return self.answer
def start(self):
Timestable.f1.grid_forget()
Timestable.f2.grid()
data.Questions(self)
def results(self):
Timestable.f2.grid_forget()
Timestable.f3.grid()
def Submit(self):
if Timestable.entryWidget.get() == "":
messagebox.showerror("Error", "Please enter a number.")
else:
if self.answer != int(Timestable.entryWidget.get().strip()):
messagebox.showinfo("Answer", "INCORRECT! Answer: " + str(self.answer))
Timestable.incorrect.append(self.prompt)
else:
messagebox.showinfo("Answer", "CORRECT!")
self.correct = self.correct +1
self.x=self.x+1
if self.x < int(Timestable.w.get()):
data.Questions(self)
data.clear_text(self)
else:
data.results(self)
data.percent = round(self.correct/self.x*100)
Timestable.congrats.configure(text="Congrats, you got "+ str(data.percent) +"% of the questions correct")
Timestable.incdisplay.configure(text=Timestable.incorrect)
data.openleader(self)
root = Tk()
root.geometry("300x300")
Timestable(root)
data()
root.mainloop()

That can be added fairly easily. We can also clean up the way it is diplayed as well. Since you are already essentially storing the data in csv format we can stick to that structure when presenting the output data.
def openleader(self):
file=open('Leaderboard.txt',"a")
file.write(Timestable.name.get()+", "+Timestable.age.get()+", "+str(data.percent)+"%""\n")
file.close
file=open("Leaderboard.txt","r").read()
headers = " ".join(["Name", "Age", "Score"])
output = [i.split(",") for i in file.split("\n") if i]
out_sort = sorted(output, key=lambda x: int(x[2][:-1]), reverse=True)
final = '\n'.join([headers, "-"*len(headers),*(" ".join(i) for i in out_sort)])
Timestable.my_text.insert(END, final)

Related

enter special character in between text in python at after certain length

code is running good in main project , took some sample code from that as below
from tkinter import *
root = Tk()
root.geometry('200x200')
root.title('Testing')
num1 = StringVar()
num2 = StringVar()
def callback(integer):
if integer.isdigit():
return True
elif integer == "":
return True
else:
return False
def limit_no1(no):
c = no.get()[0:16]
no.set(c)
num1.trace("w", lambda name, index, mode, sv=num1: limit_no1(sv))
reg = root.register(callback)
ent1 = Entry(root, textvariable=num1)
ent1.pack()
ent1.config(validate="key", validatecommand=(reg, '%P'))
this has some condition as accepting only numbers and up 16 digits
but as like some web apps i need spacing after 4 characters
is there any method for that so that i can apply to this and and for input of currency also as ',' in btw data
I worked on some code but indexing getting issue in indexing
the below code continuation of above only
num2.trace("w", lambda name, index, mode, sv=num2: limit_no2(sv))
ent2 = Entry(root, textvariable=num2)
ent2.pack(pady=20)
ent2.config(validate="key", validatecommand=(reg, '%P'))
def limit_no2(no):
c = no.get()[0:19]
split_string = [c[i:i + 4] for i in range(0, len(c), 4)]
if len(split_string) > 1:
final_string = ''
for i in range(len(split_string)):
final_string += split_string[i]
final_string += " "
no.set(final_string)
else:
no.set(c)
root.mainloop()

How to update tkinter labels that are stored in a list, using a button?

I need to update a list of labels that could be any size. All questions I've found already dealt in single labels instead of lists. I tried to extrapolate those answers to my problem and have not been successful.
I tried associating the list of labels with a list of StringVars and was not successful.
import nation
import game_time
def main():
Ven = nation.Nation("Venice", 1500000, 60, 5)
Mil = nation.Nation("Milan", 1250000, 10, 5)
Flo = nation.Nation("Florence", 7500000, 90, 5)
game_time.pass_time(1)
print ("test")
for n in nation.Nation._registry:
print (n.get_pop_stats())
if __name__ == '__main__':
main()
#NATION
name = ""
population = 0
econ_value = 0
owned_land = 0
GROWTH = .002
MONTH_GROWTH = .002/12
current_growth = 0
expected_growth = 0;
class Nation:
_registry = []
def __init__(self, name = "dummy", population = -1, econ_value = -1, owned_land = -1):
self.name = name
self.population = population
self.econ_value= econ_value
self.owned_land = owned_land
self._registry.append(self)
def get_pop_stats(self):
return "Nation: " + self.name + " Population: " + str(self.population) + " Current Growth: " + str(self.current_growth) + " Expected Growth: " + str(self.expected_growth) + "\n"
#TIME
import nation
GROWTH = .002
MONTH_GROWTH = .002/12
def pass_time(x):
while x > 0:
for p in nation.Nation._registry:
temp = p.population
p.population += p.population*(p.econ_value/100)*MONTH_GROWTH
p.current_growth = p.population - temp
p.expected_growth = p.population*(p.econ_value/100)*MONTH_GROWTH
x -= 1;
#ERROR
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.do_labels()
ran = 0
def do_labels(self):
pop_text = []
pop_stats = []
time = tk.Button(self, text="PASS TIME", fg="red", command= lambda: self.press())
time.pack(side="bottom")
i = 0
for n in nation.Nation._registry:
pop_text.append(tk.StringVar(value = n.get_pop_stats()))
pop_stats.append(tk.Label(self, textvariable = pop_text[i], command = print("initializing label")))
pop_stats[i].pack(side="top")
i+=1
self.ran = 1
self.pack()
root.update()
def press(self):
print("this works")
game_time.pass_time(1)
root = tk.Tk()
app = Application(master=root)
app.mainloop()
My bad, Here is the nation class, required to run the code.
Expected: Numbers in the labels update.
Actual: Nothing happens in the window, but the button successfully prints in the console.

Entry Widget python 3.x ValueError: invalid literal for int() with base 10

i am currently doing my activity in class about user interface in python 3.x. I used tkinter and wanted to get a integer from a Entry Widget. I searched and tried solutions from other questions from this site and non of them actually helped me yet. I made a code with the same function but without interface and it worked just fine but after i tried to convert it into interfaced one, i then got stuck whit this error:
File "C:\Users\Klien\Desktop\w.py", line 40, in deposit
x = int(E2.get())
ValueError: invalid literal for int() with base 10: ''
code:
from tkinter import *
import tkinter as tk
import sys
class User():
def __init__(self, first_name, last_name, gender, age, pin, secretQuestion):
self.first_name = first_name
self.last_name = last_name
self.gender = gender
self.age = age
self.pin = pin
self.secretQuestion = secretQuestion
self.balance = 0
def menu(self):
window = tk.Toplevel(root)
label1 = Label(window, text="Choose Options")
label1.pack()
a = Button(window,text="1. Personal Information",command=person1.personalInfo)
a.pack()
b = Button(window,text="2. Deposit",command=person1.deposit)
b.pack()
c = Button(window,text="3. Withdraw",command=person1.withdraw)
c.pack()
d = Button(window,text="4. Change PIN",command=person1.changePin)
d.pack()
e = Button(window,text="5. Quit",command=person1.logout)
e.pack()
def deposit(self):
window = tk.Toplevel(root)
L2 = Label(window,text='Input money to be deposited:')
L2.pack()
E2= Entry(window,bd=1)
E2.pack()
a = Button(window,text='okay',command=E2.get())
a.pack()
x = int(E2.get())
if x > 0:
tk.messagebox.showinfo("Success","Deposit Success")
self.balance += x
else:
tk.messagebox.showinfo("Error","Please Input again.")
def withdraw(self):
x = int(input("Input money to withdraw: "))
if x > self.balance:
print("Insufficient balance!")
if x < self.balance:
self.balance -= x
print("Withdraw Success!")
def logout(self):
root.destroy()
def personalInfo(self):
tk.messagebox.showinfo("Personal Info","Full Name: "+self.first_name.title()+" "+self.last_name.title()+'\n Gender:'+self.gender.title()+'\n Age:'+(str(self.age))+'\nBalance:'+(str(self.balance)))
def changePin(self):
y = str(input("What is the name of your dog?"))
if y == self.secretQuestion:
z = str(input("Enter new PIN"))
self.pin = z
print("Successful!")
person1 = User('klien menard','luminarias','male','12','123123','k',)
z = person1.pin
def log():
y = E1.get()
if y == z:
person1.menu()
if y != z:
tk.messagebox.showinfo("Error","Incorrect PIN")
root = tk.Tk()
L1 = Label(root,text='PIN')
L1.pack()
E1= Entry(root,bd=1)
E1.pack()
b = Button(root,text='okay',command=log)
b.pack()
root.mainloop()
part of the code with error:
def deposit(self):
window = tk.Toplevel(root)
L2 = Label(window,text='Input money to be deposited:')
L2.pack()
E2= Entry(window,bd=1)
E2.pack()
a = Button(window,text='okay',command=E2.get())
a.pack()
x = int(E2.get())
if x > 0:
tk.messagebox.showinfo("Success","Deposit Success")
self.balance += x
else:
tk.messagebox.showinfo("Error","Please Input again.")
Im new to this python thing and dont really have good programming skills.
I think you are trying to make the conversion to int before E2.get() returns any valid value, so your E2.get() return '' instead of something you can convert to int and Python complains. I think you have to separate the E2 creation and the x assignment and make E2 accessible from module level. BTW, improve naming...

raw_input() equivalent in cocos2d for Python

I'm building a simple game in cocos2d-0.6.0, and can't figure out how to let the player enter text for a username or other preferences.
I've found only a few examples, like the one here, but it isn't quite what I'm looking for. My attempt is below; I used the handling_events.py to try update_text, but it just strings together a list of letters separated by commas.
The ultimate goal is to be able to use a label to pose a question ("What is your name?") and then have the user type a response that will get stored as a variable I can access later (to display on following scenes on a high-score list, for example). Let me know if I can clarify my question.
class Settings(cocos.layer.ColorLayer):
is_event_handler = True
def __init__(self):
super(Settings, self).__init__(0,0,0,255)
label = cocos.text.Label('Pick a name:',
font_name='Courier',
font_size=32,
anchor_x='center', anchor_y='center')
label.position = 320,650
self.add(label )
self.text = cocos.text.Label("", x=100, y=280)
self.keys_pressed = set()
self.update_text()
self.add(self.text)
Here is the problem. I don't want to just collect a list of letters and symbols like A,G,T,SEMICOLON.
def update_text(self):
key_names = [pyglet.window.key.symbol_string(k) for k in self.keys_pressed]
text = 'Keys: ' + ','.join(key_names)
# Update self.text
self.text.element.text = text
This is optional; it transitions the settings page onto the main gameplay page.
def on_key_press(self, k, m):
if k == key.ENTER:
director.replace(FadeTransition(
main_scene, 1))
else:
self.keys_pressed.add(k)
self.update_text()
if __name__ == "__main__":
cocos.director.director.init(height = 690, width = 640)
settings_scene = cocos.scene.Scene(settings)
cocos.director.director.run(settings_scene)
Perhaps you were looking for something like this. The only downside is that it's all caps right now:
import cocos
import pyglet
class Settings(cocos.layer.ColorLayer):
is_event_handler = True
def __init__(self):
super(Settings, self).__init__(0,0,0,255)
label = cocos.text.Label('Pick a name:',
font_name='Courier',
font_size=32,
anchor_x='center', anchor_y='center')
label.position = 320,650
self.add( label )
self.text = cocos.text.Label("", x=100, y=280)
self.keys_pressed = ""
self.update_text()
self.add(self.text)
def update_text(self):
# Update self.text
self.text.element.text = self.keys_pressed
def on_key_press(self, k, m):
if k == pyglet.window.key.ENTER:
print "You Entered: {}".format(self.keys_pressed)
# cocos.director.director.replace(FadeTransition(main_scene, 1)) # disabled for testing
cocos.director.director.scene.end() # added for testing
else:
kk = pyglet.window.key.symbol_string(k)
if kk == "SPACE":
kk = " "
if kk == "BACKSPACE":
self.keys_pressed = self.keys_pressed[:-1]
else:
# ignored_keys can obviously be expanded
ignored_keys = ("LSHIFT", "RSHIFT", "LCTRL", "RCTRL", "LCOMMAND",
"RCOMMAND", "LOPTION", "ROPTION")
if kk not in ignored_keys:
self.keys_pressed = self.keys_pressed + kk
self.update_text()
if __name__ == "__main__":
cocos.director.director.init(height = 690, width = 640)
settings_scene = cocos.scene.Scene(Settings())
cocos.director.director.run(settings_scene)

Keeping a string without over writing it throughout the program in python

I'm trying to make a program that stores a user's recipe, using a tkinter gui to do so. I need to make a way to keep track of what is inputted, and store it in a text file. I have tried using lists to no avail, and think that using a string is the way forward, but have run into a problem - each time I try to add to the string, it over writes and doesn't keep the data from before. I have tried to use
mystring.join(a + b + etc)
but that didnt work, and my new code is as follows:
from tkinter import *
number_people = 1
itemslist = ''
itemslist1 = ''
def script (): # Puts main body of program into a function so that it can be re-run #
global number_people
number_people = 1
global itemslist, itemslist1
itemslist = ''
itemslist1 = ''
#### MAIN ####
fake_window = Tk() # #
new_recipe_window = fake_window # Opens window, allows it be closed #
start_window = fake_window # #
start_window.title("Recipe Book Task") # #
#### MAIN ####
### Functions ###
def close (x):
global start_window
global new_recipe_window
(x).withdraw()
def moreitems ():
a = item_box.get()
b = quantity_units_box.get()
c = len(a)
if a == '':
pass
elif b == '':
pass
else:
item_box.delete(0,c)
quantity_units_box.delete(0,c)
global itemslist
global itemslist1
itemslist1 = itemslist + a + ', ' + b + ', '
print ("Items list =", itemslist1)
def new_recipe ():
new_recipe_window = Tk()
new_recipe_window.title("New Recipe")
close(start_window)
recipe_name_label = Label(new_recipe_window, text="Recipe Name: ")
recipe_name_label.grid(row=0, column=0)
recipe_name_box = Entry(new_recipe_window)
recipe_name_box.grid(row=0, column=1)
def continue_1 ():
global check_box
check_box = recipe_name_box.get()
if check_box == '':
pass
else:
global itemslist
global itemslist1
itemslist1 = itemslist + check_box + ', '
print (itemslist1)
continue_button_1.destroy()
item_label = Label(new_recipe_window, text="Ingredient: ")
item_label.grid(row=1, column=0)
global item_box
item_box = Entry(new_recipe_window)
item_box.grid(row=1, column=1)
quantity_units_label = Label(new_recipe_window, text="Quantity and Units: ")
quantity_units_label.grid(row=2, column=0)
global quantity_units_box
quantity_units_box = Entry(new_recipe_window)
quantity_units_box.grid(row=2, column=1)
def continue_2 ():
check_box_1 = item_box.get()
check_box_2 = quantity_units_box.get()
if check_box_1 == '':
pass
elif check_box_2 == '':
pass
else:
global itemslist
itemslist.join(check_box_1)
itemslist.join(check_box_2)
continue_button_2.destroy()
more_items.destroy()
add_people_label = Label(new_recipe_window, text="Choose amount of people")
add_people_label.grid(row=3, column=0, columnspan=2)
def add ():
global number_people
number_people += 1
num_people_label.config(text="Number of people: " + str(number_people))
def minus ():
global number_people
if number_people > 1:
number_people -= 1
num_people_label.config(text="Number of people: " + str(number_people))
def finish ():
itemslist.join(str(number_people))
print("ItemsList = " + itemslist)
saveFile = open("Recipe_Book.txt", "a")
saveFile.write(itemslist + '\n')
saveFile.close
close(new_recipe_window)
script()
num_people_label = Label(new_recipe_window, text="Number of people: " + str(number_people))
num_people_label.grid(row=4, column=0, columnspan=2)
add_people_button = Button(new_recipe_window, text="+")
add_people_button.grid(row=5, column=1)
add_people_button.config(command=add)
minus_people_button = Button(new_recipe_window, text="-")
minus_people_button.grid(row=5, column=0)
minus_people_button.config(command=minus)
finish_button = Button(new_recipe_window, text="Finish")
finish_button.grid(row=6, column=0, columnspan=2)
finish_button.config(command=finish)
continue_button_2 = Button(new_recipe_window, text="Continue...")
continue_button_2.grid(row=3, column=0)
continue_button_2.config(command=continue_2)
more_items = Button(new_recipe_window, text="Add another item", command=moreitems)
more_items.grid(row=3, column=1)
continue_button_1 = Button(new_recipe_window, text="Continue...")
continue_button_1.grid(row=1, column=0)
continue_button_1.config(command=continue_1)
new_recipe = Button(start_window, text="New Recipe", command=new_recipe)
new_recipe.grid(row=0, column=0)
script()
So to recap, my question is how do I keep the string itemslist and itemslist1 from being overwritten, or is there another way I can do this?
EDIT FOR AAAANTOINE
I was about to clarify for you what I wanted, but I just figured out what I was doing wrong, thanks for your help, you taught me what .join does, thanks.
Your code never actually assigns to itemslist other than at the beginning of script(). The only time it ever appears on the left side of the assignment operator is when it's being initialized.
You can probably change all instances of itemslist1 to itemslist and have a working program.
Edit
On further review, I suspect that you think str.join(v) appends string v to the str. That's not how join works.
>>> s = 'something'
>>> s.join('a')
'a'
join takes a list as an argument and joins its contents together, with the str instance as a separator. Typically, the source string would actually be an empty string or a comma.
>>> s.join(['a', 'b', 'c'])
'asomethingbsomethingc'
>>> ','.join(['a', 'b', 'c']) # comma separation
'a,b,c'
>>> '-'.join(s) # spell it out!
's-o-m-e-t-h-i-n-g'
How do I do it, then?
You append to strings using this syntax:
>>> s = s + 'a'
>>> s
'somethinga'
(Or the shorthand version:)
>>> s += 'a'
>>> s
'somethinga'

Categories

Resources