previous button in tkinter python code implementation problem - python

I am making a quiz application and I have implemented some buttons like next button in my code but I am unable to implement the prevoius button in my code, I have tried but this is not being that I want, how can I do this ? Please help. The code is below
def next_btn(self):
# Check if the answer is correct
if self.check_ans(self.q_no):
# if the answer is correct it increments the correct by 1
self.correct += 1
# Moves to next Question by incrementing the q_no counter
self.q_no += 1
if self.q_no==self.data_size:
# if it is correct then it displays the score
self.display_result()
# destroys the GUI
root.destroy()
# destroys the GUI
#elif self.q_no-1==self.data_size:
#self.next_button.text="apple"
else:
# shows the next question
self.display_question()
self.display_options()

Related

Problem with simple count variable in Python

I made this short code for a school project using a Microbit computer, however, when I press the a button when the count is 1, the count jumps to 16 and I can't figure out why.
count = (1)
while True:
gesture = accelerometer.current_gesture()
display.show(count)
if button_a.is_pressed():
count *= (2)
if button_b.is_pressed():
count += (1)
if gesture == "shake":
count = (1)
any help would be greatly appreciated!
If count does not always increase by 16 on each click, you are likely dealing with button de-bouncing issue. Like others have suggested, you could use sleep(1) to check whether that is case or not, but for more practical solution you could do the following:
# detect click of button
if button.is_pressed():
count += 1
# do nothing while button remains clicked for the duration
while button.is_pressed():
pass
As other contributors commented, more than one button push is being detected for a single button push.
With Micropython, to avoid repeated button presses being detected for a single button press use the code:
if button_a.was_pressed():
instead of:
if button_a.is_pressed():

Kivy eventloop misses changes of StringProperty

I have an app which asks a series of 8 math questions to the user. The user has a keypad with which he can manipulate a label. This label answer_text holds the answer, the user wants to submit. Since I had issues, there are two "mechanisms" which allow the checking of this answer (if it is true, the next qestion should pop up, if it is false, nothing should happen). Consider the following code, where I suppress everything unrelated to this problem.
from functools import partial
class KeyPad():
on_key_pad_pressed(self, btn, text):
cs = App.get_running_app().root.ids.calculation_screen
if text.isdigit():
answer_text.text += text
cs.current_answer = answer_text.text
elif text == 'clear':
answer_text.text = ''
elif text == 'submit':
cs.on_current_answer(self, btn, text)
elif text == 'joker'
Clock.schedule_once(partial(cs.on_current_answer(btn, MathBackEnd().get_true_answer()), 1)
class CalculationScreen(Screen):
current_answer = StringProperty('')
def __init__(self):
pass
def on_current_answer(self, btn, text):
true_answer = MathBackEnd().get_true_answer()
if text == true_answer:
Logger.info('CS: Cool! You got it')
else:
Logger.info('CS: Please try again.')
So there are at least 3 different ways in Kivy to achieve the wiring between the KeyPad (holds the information about the given answer) and the CalculationScreen (assess given answers). Note that the approach in the first if-case (cs.current_answer = answer_text.text) does work, because current_answer is a StringProperty. Therefore, when it changes, its callback on_current_answer will get called.
The submit button and the joker button both work fine. If you click 'submit' your answer gets checked. If you click 'joker' the correct answer gets checked with a one second penalty for pressing 'joker'. However answers should get checked automatically (first if-case in "on_key_pad_pressed") and this works except for one case: if the current true answer is the same as the former true answer. Suppose the first question is "3+4", you enter 7 and the game automatically checks your answer. It is true, so you get the next question "1+6". You enter 7... nothing happens. You click 'clear' to clear the answer_text label and press 7 again... nothing happens. You click 'submit'. The answer gets checked and you get the next question.
So, which of these 3 approaches is superior/inferior? Should I always use schedule_once (seems to be very robust)? Do you see why I get this buggy behavior in the first if case, if two successive (true) answers are equal? During calculation, the eventloop gets congested by a timer (timing the response of the user), which updates every tenth of a second. May this congestion lead to this weird behavior?
Any help is much appreciated!

Change label text tkinter

I got this code from usingpython.com which is a "type the colour not the word" game.
I am using this code to build an improved version of this game and something is wrong and I can't figure why.
So, I want to change the label where the words are (named "label"), to something like "Game Over! Your score is bla bla bla" when the countdown hits 0. So, i did this (what I added is just the 2 last lines):
def nextColour():
#use the globally declared 'score' and 'play' variables above.
global score
global timeleft
#if a game is currently in play...
if timeleft > 0:
#...make the text entry box active.
e.focus_set()
#if the colour typed is equal to the colour of the text...
if e.get().lower() == colours[1].lower():
#...add one to the score.
score += 1
#clear the text entry box.
e.delete(0, tkinter.END)
#shuffle the list of colours.
random.shuffle(colours)
#change the colour to type, by changing the text _and_ the colour to a random colour value
label.config(fg=str(colours[1]), text=str(colours[0]))
#update the score.
scoreLabel.config(text="Score: " + str(score))
elif timeleft == 0:
ĺabel.config(text="Game Over! Your score is: " + score)
This is not working. When the countdown hits 0 the game just does nothing and stops.
I was thinking if I can do this with a while loop...
Updating a widgets value
See this answer for more details.
You can change the text value of a Label widget 'dynamically' using its textvariable option with a StringVar object, or with the .configure() method of the Label object. As mentioned in the answer above, the .configure() method has the benefit of one less object to track
With textvariable and StringVar:
# Use tkinter for Python 3.x
import Tkinter as tk
from Tkinter import Label
root = tk.Tk()
# ...
my_string_var = tk.StringVar(value="Default Value")
my_label = Label(root, textvariable=my_string_var)
my_label.pack()
#Now to update the Label text, simply `.set()` the `StringVar`
my_string_var.set("New text value")
With .configure()
# ...
my_label = Label(root, text="Default string")
my_label.pack()
#NB: .config() can also be used
my_label.configure(text="New String")
See effbot.org for more details.
Debugging Checks
Without looking at all your code, I would also recommend checking various other issues listed below for possible cause.
To extend on your comments (on this post), there may be various reasons why the program doesn't 'work' as expected:
The program never enters the final if block (if timeleft == 0) and so the .config method does not get the chance to update the variable
The global variable timeleft does reach 0, but after that iteration, it increments above 0 and re-enters the first if block (if timeleft>0), overriding the .config() you desire.
Another part of the code may be calling a .config() on your widget and overriding your desired changes
Planning your GUI
To prevent these things from happening, I highly recommend taking a step back, getting some pen and paper and thinking about the overall design of your application. Specifically ask yourself:
How can the user interact with this widget? What actions/events will cause changes to this widget?
Think of all the combinations of these events and ask yourself if these events conflict with one-another.
Also consider drawing a flow-chart for the application, from when the user launches the application to the possible paths they can take before closing, making sure blocks in the flow do not contradict each other.
Finally, also have a look into the Model-View-Controller architecture (and its variants) for good application design
initial label-
lbl_selection_value1=Label(root, text="Search Option 1")
lbl_selection_value1.grid(row=0,column=0,padx=1)
updated label-
lbl_selection_value1.destroy()
lbl_selection_value1_updated = Label(root, text='New Text')
lbl_selection_value1_updated.grid(row=0, column=0, padx=1)

How do I make Tkinter entry focusout validation happen more than just the first time?

I have an application that, for a part of it, will take a user's input and format it into a standardized time format. To do this, i have a time input that has a focusout event tied to it that calls the time parser/replacer method. However, while testing it and just setting a message label to output some stuff, i noticed that it only triggers once...
Below is some sample code to show the problem.
from Tkinter import *
root=Tk()
message_var = StringVar()
message = Label(root, textvariable=message_var, height=2, width=35, bg="light grey")
time_var = StringVar()
time = Entry(root, textvariable=time_var, validate="focusout", validatecommand=time_update)
lose_focus_var = StringVar()
lose_focus_textbox = Entry(root, textvariable=lose_focus_var)
message_var.set("Enter a time below.")
lose_focus_var.set("Click here to lose focus.")
def time_update():
"""
Updates the time field to show standardized times.
"""
cur_entry = time_var.get()
if len(cur_entry) == 0:
message_var.set("0!")
elif len(cur_entry) == 1:
message_var.set("1!")
elif len(cur_entry) == 2:
message_var.set("2!")
elif len(cur_entry) == 3:
message_var.set("3!")
elif len(cur_entry) == 4:
message_var.set("4!")
elif len(cur_entry) == 5:
message_var.set("5!")
else:
message_var.set("TOO MANY!")
time_var.set("")
message.pack()
time.pack()
lose_focus_textbox.pack()
To reproduce my issue, run the code above. In the window that appears, click into the blank textbox, enter any number of characters, then click into the textbox that says "Click here to lose focus." You'll see that the message widget updates correctly! Yay!
However, if you click into the first textbox again, change the number of characters, then click the Lose Focus box again, the message will not update again. You will need to kill the window and re-run the code for the messages widget to update again.
If i add the time_update call to the other textbox (and refactor time_update to figure out which textbox called it), the message update will happen once for each text box. But only once.
Is there a way for me to re-trigger the <FocusOut> event, other than destroying the Entry widget and recreating it? Why doesn't it trigger every time?
One problem, as you mention in a comment to your own question, is that you're not returning True or False. The whole point of the validation is to let tkinter know if it should allow the edit or not, and it does this based on the return value.
Beyond that, however, the main problem is that you're changing the value of the widget within the validation function. When you do that, tkinter will automatically disable the validation. There are workarounds, but you really shouldn't be changing the value in the validation function. If I were a user, I would find this behavior to be extremely frustrating.
If you want to change the value when it loses focus, consider adding a binding to <FocusOut> rather than using the validation functions.

How to ignore user input during sleep in Python 3?

I'm new to programming and am stuck with this problem in a small RPG game I'm making. It's mostly text based but uses tkinter for a simple GUI.
During battles, I have a for loop that goes through all creatures that are participating in the battle, and executes their action one by one with small sleeps between each action. My problem is that when the user presses keys or clicks buttons, the commands are buffered and executed after the for loop completes. Is there an easy way to ignore the user commands while this loop is going? I've tried unbinding keys and disabling buttons, then re-enabling them when the loop is complete, but it still doesn't work. I've also read about flushing the user input but I can't figure it out. Since I'm a rookie, I think I'm missing some basic concept here.
Here's my code:
def battle_round(self, command):
''' (Battle, command) -> NoneType
A round of battle where every creature gets an action.
'''
# Pause player input with mode variable.
self.player.mode = 'wait'
# Keep track of number of rounds.
self.round_number += 1
# Get initiative order for all mobs, party members, and player.
ordered_creatures = self.initiative()
# Get commands from each mob.
for mob in self.mobs:
mob.command = self.determine_mob_command()
# Check battle speed option.
delay = 1
# Begin actions for all creatures.
for creature in ordered_creatures:
# Delay between actions. Write a space between lines.
self.text_window.update_idletasks()
self.write(self.text_window, '\n')
time.sleep(delay)
# Player action.
if type(creature) == Player:
if command == 'standard_attack':
self.standard_player_attack()
if command == 'retreat':
self.retreat()
if type(creature) == PartyMember:
pass
# MOB action.
if type(creature) == MOB:
if creature.command == 'standard_attack':
self.standard_mob_attack(creature)
self.start_next_round()
I'm using Python 3.2.3 with Tk 8.5 and IDLE 3.2.3. My OS is Windows 7.
Thanks in advance!
Edit: Thanks for the replies so far guys. I may be in over my head here since I didn't even know what threading was until just now, and I'm not sure how I would go about reading and ignoring user input. As far as the code for the user input goes, I have a lot of it. I'll copy and paste some here:
def attack():
if player.in_battle == True:
if player.mode != 'wait':
player.current_battle.battle_round('standard_attack')
def retreat():
if player.in_battle == True:
if player.mode != 'wait':
player.current_battle.battle_round('retreat')
# Set up battle buttons.
attack_button = Button(battle_button_frame, text='(A)ttack', command=attack,
width=10,)
attack_button.grid(column=1, columnspan=1, row=2, padx=5, pady=5)
retreat_button = Button(battle_button_frame, text='(R)etreat', command=retreat,
width=10,)
retreat_button.grid(column=2, columnspan=1, row=2, padx=5, pady=5)
battle_button_list = [attack_button, retreat_button]
These buttons, for example are to have the user either attack the selected monster, or attempt to run away from the battle.
I also have several key bindings:
# Bind Keys
root.bind('<Escape>', func=keyboard_cancel)
root.bind('<Control-Key-m>', func=keyboard_move_mode)
root.bind('<Control-Key-M>', func=keyboard_move_mode)
root.bind('<Control-Key-l>', func=keyboard_look_mode)
root.bind('<Control-Key-L>', func=keyboard_look_mode)
root.bind('<Control-Key-t>', func=keyboard_talk_mode)
root.bind('<Control-Key-T>', func=keyboard_talk_mode)
root.bind('<space>', func=keyboard_begin_battle)
root.bind('<Left>', func=arrow_key_select_left)
root.bind('<Right>', func=arrow_key_select_right)
My problem remains that when the for loop with the sleeps is going, if the user presses a button or uses a key binding, it will get executed as soon as the battle round is over. (About 7 seconds if there are 6 monsters and the player.) I am a complete beginner at this so I apologize for not being clear with my post and for my code being a horrible mess.

Categories

Resources