how to restart a program in tkinter - python

I am programming a simple game using tkinter and want an option at the end to be able to replay the game, but I am not sure how I would go about doing it. I would like to be able to go back to the beginning where i define init. Also is there a way I could do this and not have to quit it so I could save scores as the player keeps playing.
from tkinter import *
import random
class App:
def __init__(self, master):
player_dice = []
for i in range(1,6):
x = random.randint(1,6)
player_dice.append(x)
self.label = Label(master, text = x , fg = "red").grid(row =0, column =i+1)
self.label = Label(master, text = "Dice:" , fg = "red").grid(row =0, column =1)
self.hi_one = Button(master, text="one", command= lambda: say_one(player_dice)).grid(row = 1, column = 1)
self.hi_two = Button(master, text="two", command= lambda: say_two(player_dice)).grid(row = 1, column = 2)
Here is where I would like to add a button to loop back to init.
def num(var, die, dlist):
new_window = Toplevel(root)
if die == 1:
if guess == total:
result = Message(new_window, text = "You Win").grid(row = 1, column = 1)
else:
result = Message(new_window, text = "You Lose").grid(row = 1, column = 1)
restart = Button(new_window, text = "Play Again", command = ?????).grid(row=1, column = 2)

Easiest way:
Just add a function resetBoard that resets your game.
Obviously there are various parts of the UI that don't need to be re-set, so those can go into __init__, the rest can go into resetBoard() and you can (possibly) call resetBoard() from within __init__.
Correct way:
Implement an MVC or MVP pattern: Separate your data and logic from your UI. Your view (UI) should reflect whatever is in your model, then reseting the game is just a question of reseting the model and firing the correct events so the view is updated (highly simplistic, but the very useful model-view-XXX patterns cannot be properly explained in just a few words.)

Related

Why can't I specific the default selected radiobutton?

I am making a simple GUI using Python's tkinter module, and I'm having a lot of trouble with radiobuttons. I wish to have the first selected by default, but the other two are selected at the start. Additionally, when I just pass the cursor over the window, the first one becomes checked (I do not click) so all 3 show as selected. My code:
import tkinter as tk
class openGUI(object):
def __init__(self):
# title of window
window.title("DUNE LArTPC Simulator")
# label for choices
self.question = tk.Label(window, text = "Do you want to create new or analyse existing data?")
self.question.grid(row = 0, column = 0, columnspan = 3)
# buttons corresponding to choices
self.createBtn = tk.Button(window, text = "Create", command = self.createData)
self.analyseBtn = tk.Button(window, text = "Analyse", command = self.analyseData)
self.createBtn.grid(row = 1, column = 0)
self.analyseBtn.grid(row = 1, column = 2)
def analyseData(self):
"""
Not implemented yet.
"""
pass
def createData(self):
# edit window to display new widgets (irritating to have lots of windows open!)
window.title("Select variable")
self.question.destroy()
self.createBtn.destroy()
self.analyseBtn.destroy()
# text in window
variableQ = tk.Label(window, text = "Please select Independent variable for dataset:")
variableQ.grid(row = 0, column = 0, columnspan = 3)
# radioselect variable
selection = tk.StringVar()
selection.set("lifetime")
# radioselect buttons
lifetimeBtn = tk.Radiobutton(window, variable = selection, value = "lifetime", text = "Lifetime")
elecNoiseBtn = tk.Radiobutton(window, variable = selection, value = "electronic", text = "Electronic Noise")
radioactivityBtn = tk.Radiobutton(window, variable = selection, value = "radioactive", text = "Radioactivity")
lifetimeBtn.grid(row = 1, column = 0)
elecNoiseBtn.grid(row = 1, column = 1)
radioactivityBtn.grid(row = 1, column = 2)
# create window
window = tk.Tk()
# create class object with methods to populate
# window with widgets
initWin = openGUI()
# enter mainloop
window.mainloop()
Running the above gives me:
I have tried using lifetimeBtn.select() method instead of setting the StringVar(), but this does not seem to work either. What have I missed?
EDIT: added rest of code to show how I am using class and functions to manipulate the window.
It is because selection is a local variable inside createData() function and it will be garbage collected after function completes.
Change selection to instance variable self.selection.

Updating the Tkinter button error with code structure Explanation required

So I'm currently teaching myself python, and I am developing a simple GUI restaurant menu. But I came across an error I'm hoping someone could explain, I've solved it by doing self.total_button by option, rather than putting the options in parentheses (like I did for the button above it), but the only thing I have changed is that and the program runs with no errors.
The line in the total method self.total_button["text"] = "Total £"+ str(self.cash_total) only works when I declare self.total_button the way I do, if I declare each option in parentheses it states a Nonetype' object does not support item assignment.
Does layout matter with buttons in tkinter when updating them later in a program?
#Order Up!
#A Simple GUI program, that presents a simple restaurant menu.
#It lists items, and prices.
#Let the user select different items and then show the user the total bill.
from tkinter import *
class Application(Frame):
"""Create a GUI application."""
def __init__(self, master):
"""Initialises the frame"""
super(Application, self).__init__(master)
self.grid()
self.cash_total = 0
self.total_list = []
self.create_widgets()
def create_widgets(self):
"""Creates the widgets that creates the menu ordering systems"""
#Create a instruction label
Label(self,
text = "Click the desired buttons to order something"
).grid(row = 0, column = 0, columnspan = 4, sticky = N)
#Create Burger button
Button(self,
text = "Hamburger no bun",
command = self.hamburger_no_bun
).grid(row = 1, column = 0, sticky = W)
#Creates a total button
#Super weird bug have to set it out like this so i can use the total method later.
self.total_button = Button(self)
self.total_button["text"] = "Total: £"
self.total_button["command"] = self.total
self.total_button.grid(row = 5, column = 5, sticky = W)
#Create Text Box to show current order
self.order_txt = Text (self, width = 100, height = 8, wrap = WORD)
self.order_txt.grid(row = 1, column = 5, sticky = W)
def hamburger_no_bun(self):
"""Creates a hamburger tuple to be added to the list."""
self.hamburger_no_bun = ("Hamburger no bun, £", 2.95)
self.total_list.append(self.hamburger_no_bun)
self.order_txt.insert(0.2, str(self.hamburger_no_bun))
def total(self):
#The affected method
"""Adds the total amount due taken from the total_list"""
for i in self.total_list:
self.cash_total += i[1]
print(self.cash_total)
self.total_button["text"] = "Total £"+ str(self.cash_total)
self.cash_total = 0
#main
root = Tk()
root.title("Order Up! - A Restaurant Menu GUI")
app = Application(root)
root.mainloop()
You probably wrote code like this
self.total_button = Button(self, self, text="Total: £", command=self.total).grid(row = 5, column = 5, sticky = W)
You may be suprised to learn that self.total_button now holds the value None. This is because it is holding the return value of the grid method of the Button not the button reference itself.
Later when you try to use self.total_button it will throw an exception, because the value is None and None has no attribute "Text".
To resolve the issue you must correctly capture the reference to the button and to do this split the line creating and setting up the button into two lines.
self.total_button = Button(self, text="Total: £", command=self.total)
self.total_button.grid(row = 5, column = 5, sticky = W)
Now you have a correct reference to the button, that will be usable later in the total method.

Tkinter entry getting text entered by user

I am very new to Tkinter ( I find it very difficult to learn). I have a python script working based on user input. I would like to wrap a GUI around it and eventually put it on web. In any case for user input I would like to get this from the GUI with a combination of Entry widgets and some buttons. First thing is I was reading and some people mentioned to use a class so I have the following. I have a few questions
I would like to check to see if indeed the users entered a value before he hits the GO button. How do I do this?
I would like the value entered to be made accessible by the rest of the program in the main body. How do I do this?
Thanks,
from Tkinter import *
class MainWindow():
def get_fc(self):
a = self.fc_gui.get()
return a
def __init__(self, master):
self.master = master
self.master.title('TEST')
self.fc_gui = DoubleVar(self.master, value = 500.00)
self.fclabel1 = Label(self.master, text = 'Please Enter a value', fg = 'black', bg = 'yellow')
self.fclabel1.grid(row = 0, column = 0)
self.fcedit1 = Entry(self.master, textvariable = self.fc_gui, bd = 5 )
self.fcedit1.grid(row = 1, column = 0)
fcbutton1 = Button(self.master, text='GO', command = self.get_fc)
fcbutton1.grid(row = 1, column = 1)
master = Tk()
MainWindow(master)
master.mainloop()
It doesn't make sense to return to a Button. The Button can't do anything with the value. Instead, save the value as an instance variable.
You don't have a mainloop().
You can't really check if the user entered a value before they hit "Go" - at the start of the program, of course they haven't entered anything yet. If you needed to track the contents of this field, there are ways to do that, but it's not necessary for a simple validation. Just check the value when they hit the button.
from Tkinter import *
class MainWindow():
def get_fc(self):
a = self.fc_gui.get()
if a: # this block will execute if a has content
self.a = a # save it for future use
def __init__(self, master):
self.master = master
self.master.title('TEST')
self.fc_gui = DoubleVar(self.master, value = 500.00)
self.fclabel1 = Label(self.master, text='Please Enter a value',
fg = 'black', bg = 'yellow')
self.fclabel1.grid(row = 0, column = 0)
self.fcedit1 = Entry(self.master, textvariable = self.fc_gui, bd = 5 )
self.fcedit1.grid(row = 1, column = 0)
fcbutton1 = Button(self.master, text='GO', command = self.get_fc)
fcbutton1.grid(row = 1, column = 1)
master = Tk()
MainWindow(master)
master.mainloop() # don't forget mainloop()

Tkinter Radiobutton spawns frames

I'm new to this GUI business with python2.7 and Tkinter. I'm trying to create a new frame depending on which Radiobutton the user choose, like a menu. When I click on a radiobutton it creats a new frame just like I want, but if I continue to click on the same radiobutton, it will create another frame, and another frame, etc. Can't seem to figure out on how to check if the Radiobutton is already marked (clicked on just once).
Hope I made myself clear, thankful for help!
class Books:
""" Books() is the main class for creating the whole interface """
def __init__(self):
""" Initialize the first function in class Books() """
self.library = "library.txt"
self.filepath = os.getcwd() + "/" + self.library
self.window = Tk()
self.window.title("Personal library")
self.window.wm_iconbitmap(default="myicon.ico")
userChoice = Frame(self.window, height = 1, bd = 1, relief = RIDGE)
userChoice.pack(side = TOP, pady = 10, padx = 5)
self.menuChoice = IntVar()
btAddBooks = Radiobutton(userChoice, text = "Add a new book to the library", value = 1, variable = self.menuChoice, command = self.processChoice)
btAddBooks.grid(row = 1, sticky = W)
btFindBooks = Radiobutton(userChoice, text = "Print info about a book", value = 2, variable = self.menuChoice, command = self.processChoice)
btFindBooks.grid(row = 2, sticky = W)
btPrintBooks = Radiobutton(userChoice, text = "Print all book titles in library", value = 3, variable = self.menuChoice, command = self.processChoice)
btPrintBooks.grid(row = 3, sticky = W
def processChoice(self):
""" Used to handle user choice of Radiobuttons """
if self.menuChoice.get() == 1:
self.processAddBooks()
elif self.menuChoice.get() == 2:
self.processFindBook()
elif self.menuChoice.get() == 3:
self.processShowBooks(self.filepath)
def processAddBooks(self):
""" Add a new book to the library. """
# Create a new frame
questions = Frame(self.window, height = 1, bd = 1, relief = SUNKEN)
questions.pack(fill = X, pady = 10, padx = 5)
# Do stuff with frame here...
Well, if you only need one frame to be open at a time, you can call frame.destroy() on the previous frame before you instantiate the new frame. However, this approach will require that there be something initialized for Tkinter to destroy the first time one of the buttons is selected, otherwise you'll get an error. For my purpose, I just created a throwaway class with a destroy method that did nothing, then used an instance of that class as a placeholder bound to that variable until my Toplevel widget was created for the first time. If you want multiple frames open at the same time, just not duplicates of the same option, try using a different variable name for each frame and only creating the frame if not frame.winfo_exists()--though I'm not 100% sure this wouldn't be susceptible to the same issue of needing a placeholder assigned to that variable until the frame is created the first time. If such is needed, the placeholder class would need a winfo_exists() method that would return False.

Thread designed to delete Tkinter Text Widget Content does not operate.

Sorry if this is a rather specific problem, but let me try and explain it well.
I've got a text box that a user can add text to by typing in a box and hitting 'Send'.
The problem is that I need to clean out the textbox after fifteen lines have been used.
Here is what I tried to do:
Whenever the user hits send, to add the text to the textbox, it add's a number to a Variable called textlimiter. Because every send button creates a new line as well. I can use a variable to record the number of times something is 'sent' to the textbox.
When the variable exceeds 15, a thread that is watching the variable will wipe the textbox clean.
Thing is, It never seems to work. I've tried asking for help and I got a little from an awesome guy who suggested I rewrite everything into 'frames', so that I can class all my functions, widgets, and variables into one class.
It appears I've got a conflict ion with variable class's. So I tried to set the variable to 'global' inside my class's, and functions using the variable. But that didn't seem to work either.
I can go and rewrite it all into frames I suppose, but I was just going to ask if anyone has a quick fix they can think of here. (I also don't know when the guy can help me once I'm done and if issues arise)
If my question is inappropriate, or somehow not allowed, Tell me and I'll take it down tomorrow when I get up. Sorry if it's bad.
I've got the code here:
# [Importing Modules]
from tkinter import *
import os
import threading
#_________________| Setting Variables
global textlimiter
textlimiter = 0
#____________| Defining Functions
def CONVERT():
chatbox.insert(INSERT,"You: "+USER_ENTRY.get()+ "\n")
INPUT_BOX.set("")
global textlimiter
textlimiter += 1
#_______| Creating Window |
chat = Tk()
chat.title("Net Send Client [0.4]")
chat.geometry('550x500+200+200')
#________| Title |
title = StringVar()
title.set("Net Send Chat\n Type in box to send.")
title_widget = Label(chat,textvariable = title, height = 4)
title_widget.pack()
#_______________________| User Input Box|
INPUT_BOX = StringVar()
USER_ENTRY = Entry(chat,textvariable = INPUT_BOX)
USER_ENTRY.pack(side=TOP, padx = 10, pady = 20)
#___________________________________________________________________| Send Button|
send_button = Button(chat, text="Send",width = 20, command = CONVERT)
send_button.pack(padx = 10, pady = 10)
#_______________________________________________________| Text Box Widget |
chatbox = Text(width = 60, height = 15, relief = SUNKEN)
left = Frame(chatbox)
right = Frame(chatbox)
s_start = Scrollbar(right)
s_start.pack(side=RIGHT)
chatbox.grid(row = 0, column = 0, columnspan = 3)
chatbox.pack()
#__________________________________________| Chat Wizard Checks Text Limit|
class Chatwizard(threading.Thread):
def Chatmonitor():
global textlimiter
if textlimiter >= 15:
chatbox.set(None)
chatbox.insert(INSERT,"Console: Limit Reached, Chat Wiped" + "\n")
Chatwizard.start
chat.mainloop()
As you can see I've tried setting my variables to Global, but nothing seems to change.
If you try running the program, remember you need to enter and send something 15 times for it to supposedly wipe the chatbox.
Thanks for taking a look if you do. I'm really a little stumped right now.
You definitely do not need a thread to monitor the Text widget
(chatbox), because the chatbox itself can tell you how many lines
of text it contains:
numlines = len(chatbox.get("1.0",END).splitlines())
Moreover, Tkinter is not threadsafe. All GUI widgets should be
touched by one and only one thread.
from Tkinter import *
import os
import threading
def CONVERT():
numlines = len(chatbox.get("1.0",END).splitlines())
if numlines > 2:
chatbox.delete("1.0",END)
chatbox.insert(INSERT,"Console: Limit Reached, Chat Wiped" + "\n")
chatbox.insert(INSERT,"You: "+USER_ENTRY.get()+ "\n")
INPUT_BOX.set("")
chat = Tk()
chat.title("Net Send Client [0.4]")
chat.geometry('550x500+200+200')
title = StringVar()
title.set("Net Send Chat\n Type in box to send.")
title_widget = Label(chat,textvariable = title, height = 4)
title_widget.pack()
INPUT_BOX = StringVar()
USER_ENTRY = Entry(chat,textvariable = INPUT_BOX)
USER_ENTRY.pack(side=TOP, padx = 10, pady = 20)
send_button = Button(chat, text="Send",width = 20, command = CONVERT)
send_button.pack(padx = 10, pady = 10)
chatbox = Text(width = 60, height = 15, relief = SUNKEN)
left = Frame(chatbox)
right = Frame(chatbox)
s_start = Scrollbar(right)
s_start.pack(side=RIGHT)
chatbox.grid(row = 0, column = 0, columnspan = 3)
chatbox.pack()
chat.mainloop()

Categories

Resources