Updating a variable using a tkinter button - python

I tried writing a program that tests writing skills. (using tkinter)
For use type in y, press enter, write the sentence you were given press enter again and it outputs the time it took you. This is the part that works.
To add more flexibility I wanted to add a button that shuffles the sentence and gives you a new one. I've tried random.choice from a list, defining the variable as parameter and am currently stuck at random.randint. None of them have worked so far. They output a random sentence after launching the code, but the button simply does nothing.
heres the code of the function and further down where the button its used in. if the complete code is necessary to you please just ask for it :D
def change_sentence():
var = random.randint(0,5)
if var == 1:
sentence_var = "thats gonna work"
return sentence_var
if var == 2:
sentence_var = "probably"
return sentence_var
if var == 3:
sentence_var = "i guess"
return sentence_var
if var == 4:
sentence_var = "maybe?"
return sentence_var
if var == 5:
sentence_var = "please??"
return sentence_var
sentence_var = change_sentence()
shuffle_button = Button(root, text="Shuffle", command=change_sentence)
to conclude, i simply look for a way of making my button working.

Having your callback function return a variable doesn't really do anything. Specifically, it won't update the text in your GUI.
You should look into StringVar and using textvariable parameter for you text object. Here's a small example. I also cleaned up your if statement:
root = tk.Tk()
class tkApp:
def __init__(self, master):
self.sentence_var = tk.StringVar()
self.change_sentence()
sentence_text = tk.Label(master, textvariable=self.sentence_var)
shuffle_button = tk.Button(master, text="Shuffle", command=self.change_sentence)
sentence_text.pack()
shuffle_button.pack()
def change_sentence(self):
var = np.random.randint(1, 6)
if var == 1:
self.sentence_var.set("thats gonna work")
elif var == 2:
self.sentence_var.set("probably")
elif var == 3:
self.sentence_var.set("i guess")
elif var == 4:
self.sentence_var.set("maybe?")
elif var == 5:
self.sentence_var.set("please??")
myApp = tkApp(root)
root.mainloop()

You need to put a print statement inside the function if you just want to print the sentence. Your variable sentence_var will be assigned a string when calling the function but after that the Button will have no effect on it as it is not inside the function.

Related

The global isn't working and didn't recognizing variable "text"?

The global isn't working properly and it says that
it does not know what text is, even though it is declared global.
def nguess():
answer = random.randint ( 1, 50 )
def check():
global attempts
attempts = 10
global text
attempts -= 1
guess = int(e.get())
if answer == guess:
text.set("yay you gat it right")
btnc.pack_forget()
elif attempts == 0:
text.set("you are out of attempts")
btnc.pack_forget ()
elif guess > answer:
text.set("incorrect! you have "+ str(attempts) + "attempts remaining. Go higher")
elif guess < answer:
text.set("incorrect! you have "+ str(attempts) + "attempts remaining. Go lower")
return
nw = tk.Toplevel(app)
nw.title("guess the number")
nw.geometry("500x150")
lable = Label(nw, text="guess the number between 1 - 50")
lable.pack()
e = Entry(nw, width = 40, borderwidth = 10)
e.pack()
btnc = Button(nw,text = "Check", command = check)
btnc.pack()
btnq = Button ( nw, text="Quit", command=nw.destroy )
btnq.pack()
text = StringVar()
text.set("you have ten attempts remaining ")
guess_attempts = Label (nw,textvariable = text)
guess_attempts.pack()
Well, what's going on is you're trying to get a variable before initialize it i.e. in check function you're calling a global text variable so what it means is you're bringing whatever text variable stores in global namespace, but the problem is text variable isn't exist in global namespace yet because you've created after calling the check function. Below I show an example:
def test():
global variable
print(variable)
test()
variable = 'Hello'
This will raise an error because of what I just explained, so what you have to do is something like this(based on the example):
def test():
global variable
print(variable)
variable = 'Hello'
test()
In short, initialize the text variable before calling the check function which uses global text

Python creating and traversing a simple menu

I am creating my first Python project. What I am working on is a simple text-based metric to standard (and vice versa) conversion program. I am not seeking the code as I want to figure this out myself. However, I am running into a problem traversing the menu. For example, there are 3 menus: main menu, metric menu, and standard menu. The program opens to the main menu. On the main menu the user can choose to navigate to the metric menu, standard menu, or to exit the program. If the user wants to be able to return to the main menu from the metric or standard menus, what would be the best approach? I have tried while loops and if/elif. However, it seems that my code gets really bloated and convoluted. Could someone please give me some advice on creating and traversing text menus in Python?
You can try something like this:
import sys
import os
def switch():
clear = lambda: os.system('cls')
clear()
s = 0
while s != 5:
s = int(input('1)Metric 2)Standard 3)Exit\n'))
if s == 1:
clear = lambda: os.system('cls')
clear()
metric()
elif s == 2:
clear = lambda: os.system('cls')
clear()
standard()
elif s == 3:
clear = lambda: os.system('cls')
clear()
exit()
else:
print('Out of range.')
def metric():
'''Your code here'''
s = int(input('0)Back\n'))
if s == 0:
switch()
else:
print('Out of range.')
def standard():
'''Your code here'''
s = int(input('0)Back\n'))
if s == 0:
switch()
else:
print('Out of range.')
switch()
Since you said you were new to Python, the __init__() method of a class is called when the object is first constructed. You can read the docs on classes here (skip to 9.3).
I don't know how efficient this would be, but you could use a class that stores other objects of the same class:
class Menu:
def __init__(self, content, short_view, submenus = None):
self.content = content
self.short_view = short_view
if submenus != None:
self.choices = dict(enumerate(submenus, 1))
for sub in submenus:
sub.parent = self
else:
self.choices = {}
subsub1 = Menu("this is subsub1 content", "this goes to subsub1")
subsub2 = Menu("this is subsub2 content", "this goes to subsub2")
subsub3 = Menu("this is subsub3 content", "this goes to subsub3")
sub1 = Menu("this is the first sub menu content", "this goes to sub1", [subsub1, subsub2, subsub3])
sub2 = Menu("this is the second sub menu content", "this goes to sub2")
main = Menu("this is the main menu content", "this goes to main, but will never be used", [sub1, sub2])
main.parent = main
current_menu = main
while True:
print(current_menu.content)
print("\n".join([f"[{num}] {current_menu.choices[num].short_view}" for num in current_menu.choices]))
inpt = input("Choice: ")
if inpt == "exit":
break
elif inpt == "back":
current_menu = current_menu.parent
else:
current_menu = current_menu.choices[int(inpt)]
Usage (in shell):
this is the main menu content
[1] this goes to sub1
[2] this goes to sub2
Choice: 1
this is the first sub menu content
[1] this goes to subsub1
[2] this goes to subsub2
[3] this goes to subsub3
Choice: back
this is the main menu content
[1] this goes to sub1
[2] this goes to sub2
Choice: exit
>>>
Try using the following code :
def main():
a=int(input("1) metric\n2) standard\n3) exit\n"))
if a==1:
metric()
elif a==2:
standard()
elif a==3:
main() #return main()
def metric():
# your code
pass
def standard():
# your code
pass
main()

I want to apply unit tests to simple numeric playback code

I recently started programming with Python for the first time. I have been told that I can not access the essence of unit testing for this code. I tried to practice by listening to the importance of unit testing and Python unit testing.
The code is shown below.
# UpDown.py
import random
import unittest
servicenumber = 0
exp = 0
## Game play screen
def start_screen():
global servicenumber
exe = """
==============================
1. Up/Down Game Start
2. Exp check
3. exit
==============================
Please enter your desired service number."""
print(exe)
servicenumber = input()
if servicenumber == 1:
start_game()
elif servicenumber == 2:
check_exp()
elif servicenumber == 3:
game_exit()
else:
print "Please enter one of 1, 2, or 3."
## Up/Down This is the part of the game.
def start_game():
re = 1
global servicenumber
global exp
if servicenumber == 1:
while re != 0:
notice = """
==============================
Randomly generated values ​​are from 1 to 100.
Players should enter a number between 1 and 100.
If you win, you gain 2 exp, and if you lose, it decreases by 1.
Please enter 0 to finish.
==============================\n"""
print(notice)
var = input('input : ')
if var > 100:
print 'Please enter a value between 1 and 100.'
continue
elif var < 0:
print 'Please enter a value between 1 and 100.'
continue
elif var == 0:
re = 0
else:
print ''
randvalue = random.randrange(1,101)
if var > randvalue:
print 'Up'
exp = exp + 2
print "exp+2"
print "Your experience ",exp
print "Randomly generated values ",randvalue
continue
elif var < randvalue:
print 'Down'
exp = exp-1
print 'Decreasing story ~~'
continue
elif var == randvalue:
print 'The story of being tapped ~~'
continue
else:
print "Randomly generated values ",randvalue
continue
start_screen()
def check_exp():
global servicenumber
if servicenumber == 2:
print "Experience: ",exp
start_screen()
## (exit)
def game_exit():
global servicenumber
if servicenumber == 3:
print 'Exit'
exit()
if __name__ == "__main__":
start_screen()
else:
print "Imported. Start unit testing.\n"
And the code I tried to practice
I think it is pointless.
import unittest
import UpDownGame
class testing(unittest.TestCase):
def test_start(self):
self.assertTrue(UpDownGame.start_screen)
def test_game(self):
self.assertTrue(UpDownGame.start_game)
def test_chkexp(self):
self.assertTrue(UpDownGame.check_exp)
def test_exit(self):
self.assertTrue(UpDownGame.game_exit)
def initialize():
return testing
if __name__ == "__main__":
testsuite = (unittest.makeSuite(testing))
unittest.TextTestRunner(verbosity=2).run(testsuite)
So I want advice on unit testing.
I would really appreciate it if you let me know how to apply unit tests to this code.
Also, let me know if you think I have the basic knowledge I need.
Thank you.
The problem is that you're trying to write the tests when you've already written the code, and you've started with inherently hard to test code. If you wrote the tests before each part of code they are intended to test then you would write the code quite differently.
So, ignoring your code entirely, you want a 'start_screen' function that will print a prompt, read some input, and call a function based on that input. You don't want to hard-wire the functions that get called (because that makes it harder to test), so you might change the function so it takes a dictionary of actions and then you can pass in test actions in place of the ones the real code will run. You also don't want it reading from input() so again you might make that customisable. Writing output with print is also a bad idea for testing, as you'll want to catch the output if you need to make any assertions about it but we'll ignore that for now.
So, change def start_screen() to:
DEFAULT_ACTIONS = { 1: start_game, 2: check_exp, 3: game_exit }
def start_screen(actions=DEFAULT_ACTIONS, input=input):
pass
and write a test:
def test_start_input_1_calls_action_1(self):
calls = []
dummy_actions = { 1: lambda: calls.append('action1') }
def dummy_input():
calls.append('input')
return 1
start_screen(actions=dummy_actions, input=dummy_input)
assert calls == ['input', 'action1']
next you verify that the test compiles and runs and fails with an assertion error. If it fails for any other reason you fix the test.
Only then, you change start_screen so that the test now passes.
Then you can add other tests. A good next test would be one where the 'input' function returns 4 the first time it is called and then a 1:
def test_start_input_4_prompts_again(self):
calls = []
inputs = [1, 4]
dummy_actions = { 1: lambda: calls.append('action1') }
def dummy_input():
calls.append('input')
return inputs.pop(0)
start_screen(actions=dummy_actions, input=dummy_input)
assert calls == ['input', 'input', 'action1']
Now you verify that you get an assertion error, then you modify the code so that the test passes. Now the most important step: you look at the two tests and see that they are almost identical so you remove the duplication to another function and modify the tests so they look something like this:
def test_start_input_1_calls_action_1(self):
self.start_helper([1], ['input', 'action1'])
def test_start_input_4_prompts_again(self):
self.start_helper([4, 1], ['input', 'input', 'action1'])
def start_helper(self, inputs, expected_calls):
... add code here ...
then go on to write tests for the other functions. You will want to lose the global variable: tested functions need to be clean and not depend on variables set outside. Also that recursive call it probably the wrong thing to do, you might want to consider making start_screen have a loop and in that case the tests we just wrote will have to be updated for that.
At every step the fact that you have already written the test is going to influence how you think about the code.

Separating Tkinter UI concerns from Logic in Python app

This is my first app ever. It is working well but I would like to separate the UI concerns like getting input and creating labels, from the translation logic. I would then like to remove the output from the previous translation, i.e., only showing one translation on the screen at a time.
How can I separate the translation logic from my Tkinter GUI?
from Tkinter import *
import tkMessageBox
def start():
inputg = input.get()
if len(inputg) >= 2 and inputg.isalpha():
new_word_out = Label(text=(inputg[1:] + (inputg[0] + "ay")).lower().title()).pack()
out_message = Label(text="Cool! Try another!").pack()
# restart()
elif len(inputg) <= 1 and inputg.isalpha():
show_error(message="Whoops! I need 2 or more characters to translate! Try again!")
return
elif len(inputg) >= 1 and not inputg.isalpha():
show_error(message="Whoops! No numbers or symbols please! Try again!")
return
elif len(inputg) == 0:
show_error(message="It seems you haven't given me anything to translate!")
return
def show_error(message):
tkMessageBox.showerror(title="Error", message=message)
return
def quit():
ask_exit = tkMessageBox.askyesno(title="Quit", message="Are you sure you want to quit?")
if ask_exit > 0:
root.destroy()
return
root = Tk()
input = StringVar() # stores user input into this variable as a string.
root.title("The Pig Translator")
root.protocol("WM_DELETE_WINDOW", quit)
labeltitle1 = Label(text="Hello there! This is my Pig Latin Translator!").pack()
labeltitle2 = Label(text="Please enter a word to continue!", fg='darkgreen', bg='grey').pack()
original_entry = Entry(textvariable=input, bd=5, fg='darkgreen').pack()
translate_button = Button(text="Translate", command=start).pack()
root.bind('<Return>', lambda event: start()) # essentially binds 'Return' keyboard event to translate_button
root.mainloop()
There are many ways you can separate logic from GUI. generally I would recommend using classes and callback functions. Thus, I made a class that generates the gui. However, the translation is performed by external function called do_translation.
MyFrame does not know much about how do_translation. It only knows it returns translated_str, message and takes string as argument. do_translation does not relay on any gui as well. The do_translation takes only an input string, does what it wants, and returns translated string and message. The MyFrame take this function as a callback. You can make any other translation function, and as long as the input and output are same, it will work.
I rely here on a "Cool" in a massage which indicates that translation was ok. Its poor idea to make it relay on 'Cool' word, but did not want to change your code too much. Probably better to raise some error, or use message codes, etc.
from Tkinter import *
import tkMessageBox
class MyFrame(Frame):
def __init__(self, master, input_callback=None, **kwargs):
Frame.__init__(self, master)
self.set_input_callback(input_callback)
self.create_widgets()
self.pack()
def create_widgets(self):
self.input = StringVar() # stores user input into this variable as a string.
self.labeltitle1 = Label(text="Hello there! This is my Pig Latin Translator!")
self.labeltitle1.pack()
self.labeltitle2 = Label(text="Please enter a word to continue!", fg='darkgreen', bg='grey')
self.labeltitle2.pack()
self.original_entry = Entry(textvariable=self.input, bd=5, fg='darkgreen')
self.original_entry.pack()
self.translate_button = Button(text="Translate", command=self.start)
self.translate_button.pack()
self.new_word_out = Label(text='')
self.out_message = Label(text='')
def set_input_callback(self, some_fun):
self.input_callback = some_fun
def show_error(self, message):
tkMessageBox.showerror(title="Error", message=message)
return
def start(self):
inputg = self.input.get()
if self.input_callback:
translated_str, message = self.input_callback(inputg)
if 'Cool' in message:
self.new_word_out['text'] = translated_str
self.new_word_out.pack()
self.out_message['text'] = message
self.out_message.pack()
else:
self.show_error(message)
def do_translation(inputg):
translated_str = message = ''
if len(inputg) >= 2 and inputg.isalpha():
translated_str = (inputg[1:] + (inputg[0] + "ay")).lower()
message = "Cool! Try another!"
elif len(inputg) <= 1 and inputg.isalpha():
message = "Whoops! I need 2 or more characters to translate! Try again!"
elif len(inputg) >= 1 and not inputg.isalpha():
message = "Whoops! No numbers or symbols please! Try again!"
elif len(inputg) == 0:
message = "It seems you haven't given me anything to translate!"
return translated_str, message
def quit():
ask_exit = tkMessageBox.askyesno(title="Quit", message="Are you sure you want to quit?")
if ask_exit > 0:
root.destroy()
return
root = Tk()
root.title("The Pig Translator")
root.protocol("WM_DELETE_WINDOW", quit)
mf = MyFrame(root)
mf.set_input_callback(do_translation)
root.bind('<Return>', lambda event: start()) # essentially binds 'Return' keyboard event to translate_button
root.mainloop()
Hopefully this will be useful. I know, that there is not too much explanation what is happening here, but, don't have much time to write it. Your problem is very general.

tkinter's IntVar() and .get()'ing it

Im in a need of a little help here, i feel like i have searched endlessly and been unable to corret my problem.
The case:
I have a checkbutton which have on value as 1 and off as 0, now i want to do an act if it is on and another if it is off.
My code goes:
#==Checkbox==#
check = IntVar()
checkbox = Checkbutton(labelframe, text="Tillad mere end én linje", variable = check, onvalue=1, offvalue=0)
checkbox.pack(side = RIGHT)
...
def go():
check.get()
print(check)
if(check == 0):
print("off")
w.delete(ALL)
tegnefladen()
update()
else:
print("on")
update()
You aren't actually setting the value. check is an object, and it won't ever be identical to 0. Basically, you want to compare check.get(). Try this:
def go():
print(check.get())
if(check.get() == 0):
print("off")
w.delete(ALL)
tegnefladen()
update()
else:
print("on")
update()

Categories

Resources