Get Tkinter to process Arrow Key Input - python

(I drastically simplified my code now so you should also be able to run it.)
Like I said in the previous version of this question, I am trying to get tkinter to process arrow key strokes. The following code produces a grid of $-signs with one I in the middle (the agent). All it is supposed to do for now is, on pressing the up key, move the agent up and collect a $-sign.
'''
import tkinter as tk
class PlayGame:
def __init__(self):
self.history=[] #Will contain the move history. Each element is a quadruple consisting of the state,action,reinforcement,new state.
self.root=tk.Tk()
for i in range(27):
for j in range(27):
tk.Label(self.root,text='$',height=1,width=3).grid(row=i,column=j)
self.I=[14,14]
tk.Label(self.root,text='I',height=1,width=3).grid(row=14,column=14)
self.root.bind('<KeyPress>',self.onKeyPress)
self.root.mainloop()
def onKeyPress(self,event):
if event.keysym in ['Up','Right','Down','Left']:
if event.keysym=='Up':
tk.Label(self.root,text='',height=1,width=3).grid(row=self.I[0],column=self.I[1])
tk.Label(self.root,text='I',height=1,width=3).grid(row=self.I[0]+1,column=self.I[1])
self.I[0]+=1
'''
It still doesn't work and again, something very weird happened: First, I accidentally had 'row=self.I[0],column=self.I[1]+1' instead of 'row=self.I[0]+1,column=self.I[1]'. The agent would then move to the right when I pressed the up key. After I corrected the mistake, it didn't move at all anymore. But that line can't really be the reason it is not working, can it? Does anyone have an idea why it worked initially, but suddenly not anymore?

Related

How to simulate random key inputs from a pre-defined set of keys, key combinations? tkinter GUI / window not working

i'm a beginner in python, only having finished a course on it and currently working through a book.
A fairly simple project (and one of the few actually useful things i could think of) is an anit-AFK tool. I started googleing and found a few elements that i fried to combine.
So i "frankensteined" a script together by stitching different elements and my first test seem to work kinda fine, generally sort of doing what i want it to.
However i have a few problems:
1: tkinter GUI doesn't really work (i'm running the code directly from PyCharm); a window opens but it completely lacks the buttons or anything really. It's a blank window, that seems to have the dimension i gave it, but lacking everything else.
2: I don't know how to make it press random keys, from a set of pre-defined keys and key combinations.
My idea was assigning each key to a number, then having a random number generator pick a number, assigning it to a variable. All of that in a loop so that each time another loop decides it's time to press a key, the key (combination') is a new random one.
Now that i've explained the general idea, here's the code i have until now:
import pyautogui
from random import randint
from time import sleep
from tkinter import *
running = True
key = ["w", "a", "s", "d"] # this is the "base" keys
key_2 = [key[randint(0,3)], key[randint(0,3)] + "shift", "space"] # this is for combinations
def anti_afk():
while running:
pyautogui.press(key_2[randint(0,2)])
print("Test") # to easily test if the sleep is working working
sleep(randint(5,30))
def start():
running = True
def stop():
running = False
root = Tk()
root.title("Anti AFK Tool")
root.geometry ("500x500")
app = Frame(root)
app.grid
start = Button(app, text="Start anti-AFK", command=start)
stop = Button(app, text="Stop preventing AFK", command=stop)
start.grid()
stop.grid()
root.after(1000, anti_afk)
root.mainloop()
As i said above i'm a beginner and pretty much everything regarding tkinter and pyautogui isn't written by me but taken from THIS and THIS post right here. Also this is the very first time i use tkinter
So, when running it with only one key (W key in my case) it generally seems to work kinda fine. The GUI doesn't work beyond what i described above.
I'm programming on my MacBook and get the "spinning ball of death" in the application window. Generally i want the program do be running mainly on windows.
So the problem is that the random key selections seems to be not working like i want it to. Especially the (in this case) shift+key doesn't. Also the GUI of the program doesn't work.
Another idea i had was defining a function for each action seperately and then randomly calling that function. But the problem of how to call / use random functions / list elements is basically the same and the list solution seems much more flexible with less code.
Can you help me with this?

Using onscreen.click() in turtle but only works at the end of the programme

This is the programme I wrote but I'm not sure what's wrong:-
import turtle
import random
bob = turtle.Turtle()
screen = turtle.Screen()
def coord(x,y):
print(x,y)
turtle.onscreenclick(None)
turtle.onscreenclick(coord)
turtle.listen()
print('hello')
turtle.done()
The programme works fine except that the print('hello') part happens first, followed by the on screen event. How do I make the onscreen event occur first before the rest of my programme?
You can simply make the rest of your code part of the coord function:
def coord():
print(x,y)
turtle.onscreenclick(None)
print("Hello")
# The rest of your program goes here.
However, a few things to note:
This isn't an amazing elegant solution, especially if you intend to set up other events further into your code. It can become quite hard to keep track of.
It's imperative that you remove the event binding (in this case the binding of coord to onscreenclick) as soon as it's been used, otherwise you could end up with multiple instances of the same code running at once if someone double-clicked the screen, for example. In your case you've already done this (with turtle.onscreenclick(None)), but it is something to keep in mind.
If you choose to go this route, don't forget to rename the coord function something more representative of what that section of your code will do.

Some questions about switches in python

This is my first Question here at StackOverflow, so please be patient with me if some info isn't present or I missed something important, but anyways i'll do my best :)
Recently I started to code in Python2.7, so I'm not very good at it. While playing with PyGtk, PyGObject, Glade, etc I found something particular about switches (Haven't tried with any other widget, so I don't know if it happens somewhere else. Most likely it doesn't, I hope...)
I made a very basic GUI with a single "window" plus a "switch" using Glade
My objective was to deactivate switch after user tried to activate it if some exeption raised up before, something like:
Activate it --> * Found error --> * Deactivate it
I made some code, and after a while, I noted that THIS piece of code created a loop-like block, blocking GUI's window afterwards:
builder = Gtk.Builder()
window1 = builder.get_object('window')
switchie = builder.get_object('switchie')
switchie.set_active(False)
def Hi(switch, active):
print switchie.get_active()
switchie.set_active(not switchie.get_active())
switchie.connect("""notify::active""", Hi)
window1.set_position(Gtk.WindowPosition.CENTER)
window1.connect("delete-event", Gtk.main_quit)
window1.show_all()
If i'm right, "switchie.connect" links "switchie" object with "Hi" func whenever "switchie" gets clicked.
But if I execute this and try to turn switch on, GUI hangs up. I did try to execute this via script & command-line and adding the "print switch state", resulting in an endless loop (True & False)
I tried with many other funcs I made, but neither of them could solve this issue. In fact, this is the "essence" of all the other funcs I made.
Why does this happen?
Where's the loop?
Am I wrong in some line?
Help is appreciated!
(If you need to see the rest of my faulty funcs, just ask for 'em, but I don't think they'll help...)
You want to hook up the switch like this:
switchie.connect("""activate""", Hi)
This will only get called once for every time it is clicked. What you were doing is hooking up to the signal after it changed, so it was constantly changing, and never catching up. You will also want to change
def Hi(switch, active):
to
def Hi(switch, active = None):
for keyboard support.

Adding a countdown timer in python

I'll admit, i am a newbie with python, but here is my issue.
the version is 2.6.5 (i know i'ts an old version but there's reasons to this) and livewires is used
Bascially this game has a bunch of colored balloons in which you need to click them to make them disappear. Adjacent balloons of the same color disappear along with the clicked balloon. Once the balloons are cleared it moves on to the next level.
I need to create a timer on the top right of my screen. This timer needs to countdown in seconds (from 30 might be a good start.) However no matter what i try, either the timer does not display or the numbers are overlap eachother. I would like to know how to do this, as it has been driving me up the wall as of late.
...Of course it also needs to end the game if it reaches zero and add more time if the level is complete...
But for now i just want to focus on displaying the timer and having it count down to zero on screen.
class Timer(games.Sprite):
""" countdown timer """
def __init__(self):
timer_message = games.Text(
value = 30,
size = 50,
color = red,
x = 600,
y = 30
)
def start(self):
while self.timer_message.value != 0:
time.sleep(1)
self.timer_message.value -= 1
game.screen.add(timer_message)
Alright. I fixed the "compressing balloons table" (accidentally deleted the self_update lol) problem, but now it is saying that "global name timer_message is not defined"... despite the fact that it says timer_message = games.Text
I would paste the whole code, but i can't get the indentation right (this is my first time using this website.)
So, I understand it's been some time and if you don't need an answer anymore that's alright.
For now it's hard to answer your question in general because I don't understand the structure of the rest of your code or how you're displaying graphics. However, I can tell you while you're getting the
global name timer_message is not defined
error. It's because when you define timer_message within the __init__ function you are defining it within the local scope of the function but not for the class. In order to make it accessible to the class you need to assign to self.timer_message.
This is a consequence of how python imitates object oriented programming, but making this change should address your immediate error.

What is causing this cascade menu failure with tkinter in python?

The following code exhibits a problem I do not understand:
from Tkinter import *
root = Tk()
cheese_var = IntVar()
parrot_var = IntVar(value=1)
check_menu = Menu(tearoff=0)
check_menu.add_checkbutton(label="Cheese", variable=cheese_var)
check_menu.add_checkbutton(label="Parrot", variable=parrot_var)
count = 0
class Top():
def __init__(self):
global count
count += 1
self.tl = Toplevel(root)
Label(self.tl, text="Window " + str(count)).pack()
self.mb = Menubutton(self.tl, text="Push Me", bg='pink')
self.menu = Menu(self.mb, tearoff=0)
self.menu.add_cascade(label="Choices", menu=check_menu)
self.menu.add_command(label="New Window", command=new_top)
self.mb.config(menu=self.menu)
self.mb.pack()
def new_top():
Top()
Top()
root.mainloop()
The menu brought up by the menu button in the created top level window initially behaves as expected. Clicking on the New Window command there creates a new such window, which also behaves as expected. Indeed, as long as you keep creating new top level windows, everything continues to work as expected. However, once you delete (close) any one of those windows, then, in a subsequently created new window, the Choices cascade on the new menu is not functional. (It is still OK in the windows created before the closing of one.)
The situation in which I initially encountered this symptom was much more complex, but I was able to simplify it down to the above example which exhibits the issue. I have discovered that I can avoid the problem by having each instance of Top create its own check_menu as an attribute; but I do not understand why this should be necessary. Please point me the way if there is one to avoid the problem without such replication of a cascade menu used in multiple windows.
Unfortunately, I don't think it is possible to do what you want. I'll try to explain as best as I can.
When you first run the script, check_menu is created and works fine for the first window. As you create more windows, check_menu is simply shared between them. However, when you close one of them, check_menu (and everything under it) is destroyed. So, when you create a new window after that, check_menu no longer exists and it doesn't show.
However, the script doesn't throw an error because, for some reason, Tkinter allows you to assign menus to things that aren't menus. Believe it or not, none of the following code:
self.menu.add_cascade(label="Choices", menu=None)
self.menu.add_cascade(label="Choices", menu=1)
self.menu.add_cascade(label="Choices", menu="")
will break the script. Each line simply does nothing but create an empty cascade "Choices".
That is basically what is happening. After closing one window, check_menu and everything under it is destroyed. Yet, Tkinter doesn't throw an error but instead assigns a menu to something that is no longer a menu (as far as what it is assigning the menu to, I believe it is using the old instance of check_menu, which was destroyed).
To solve this problem, recreate check_menu and everything under it each time you call Top. In other words, put the code for check_menu (and its options) in the __init__ method of Top. That way, each time Top is called, check_menu will exist.
Hope this helps (and that I explained it sufficiently :)

Categories

Resources