Error message in bind key event - python

I am struggling to bind a left arrow press with a printed message. Any thoughts? I've searched this site and seem to be following the same instructions others have used, but it's still not working! I am very new to Python (and coding in general) and would really appreciate the help!
The error message:
NameError: name 'onArrowKey' is not defined
Relevant code:
from Tkinter import*
class FullScreenApp(object):
def __init__(self, master):
self.master=master
pad=3
self._geom='200x200+0+0'
master.geometry("{0}x{1}+0+0".format(
master.winfo_screenwidth()-pad, master.winfo_screenheight()-pad))
master.bind('<Escape>',self.toggle_geom)
def toggle_geom(self,event):
geom=self.master.winfo_geometry()
print(geom,self._geom)
self.master.geometry(self._geom)
self._geom=geom
def onArrowKey(self,event):
print 'Got arrow key press'
root=Tk()
root.bind('<Left>', onArrowKey)
root.config(background="black")
root.title("Who buzzed in first?")
app=FullScreenApp(root)
root.mainloop()

In the context you're writing in, onArrowKey has no definition. Move the def onArrowKey out of the FullScreenApp class, so that the context is right, or call app.onArrowKey instead of onArrowKey (after assigning app).

I guess you should move the problematic statement (root.bind('<Left>', onArrowKey)) in the __init__ of your class :
like this :
master.bind('<Left>', self.onArrowKey)

Related

How to execute a class method from another class with the method passed a parameter in Python

I'm a beginner in learning python..
I'm looking for help in solving an OOP problem
My main program has something simplified like below:
class abc(Frame):
def _init_(self, master)
Frame.__init__(self)
self.B1 = Mybutton(self.master, self.cmd)
def cmd(self):
print("hello world")
In the main program, I import Mybutton class in another file, which is simplified as below:
class Mybutton():
def _init_(self, parent, command):
self.command = command
def A_ramdom_fcn(self):
...
self.command() ------------------>> here I want to execute the command
in class abc, not in class Mybutton.
How to execute a method from another class that is passed as an instance method, you may ask why not just execute it in class abc, but I have event attached to button press, it needs to do a roundabout to achieve this..
First, fix the typos: missing : in abc's init method, and it should be __init__ (with two underscores) for both classes.
It seems like you've gotten yourself turned around. You've set things up correctly using composition: an abc has a Mybutton, and it looks like you correctly pass the function to Mybutton so that it can execute it. In fact, your code will work as written if you do, for example
a = abc(master) # no context for what master is, but I assume you have it
a.B1.A_ramdom_fcn()
With the way you've set things up, you don't want to import and make instances of Mybutton in your main program (what abc would they belong to?). You want to import and make instances of abc. You then access their internal Mybutton like I've shown in the example above. This works because when you pass self.cmd to the Mybutton constructor while inside the abc constructor, it's already a bound method of the abc you're constructing.
As an addendum, it looks like you might be having an XY problem with regards to why you need such a roundabout method. Is there any reason why you can't simply pass abc.cmd to the button press handler?
Theoretically, what you are trying is possible, you can capture the object method into variable and call it later (python 3):
class Window:
def __init__(self):
self.my_button = Mybutton(self.cmd)
def cmd(self):
print("hello world")
class Mybutton:
def __init__(self, command):
self.command = command
def a_ramdom_fcn(self):
self.command.__call__()
win = Window()
win.my_button.a_ramdom_fcn()
I assume you are trying to make the generic Button class which doesn't know what to do when it's clicked and you want to put the actual logic into your Window class.
That makes sense, but it would be even better to extract the logic into the third, Command class. This allows us to limit the Window responsibility and also avoid the trick with method-as-variable (the command we pass to the button object is just another object):
class HelloWorldCommand:
def execute(self):
print("Hello world")
class Window:
def __init__(self):
self.my_button = Mybutton(
HelloWorldCommand()
)
class Mybutton:
def __init__(self, command):
self.command = command
def a_ramdom_fcn(self):
self.command.execute()
win = Window()
win.my_button.a_ramdom_fcn()

Tkinter - StringVar() trace not executing command

I wrote this code to be a version manager, but it doesn't execute the command changeDir(). Why?
https://pastebin.com/VSnhzRzF
You forgot to pass a 'name' argument to changeDir function. And there's no exception because your statement has no effect!
Snippet to represent the problem:
import sys
def exec_smth():
# execution without effect
exec('write_smth')
try:
# execution with exception because of missing argument
exec('write_smth()')
except TypeError as error:
# now we pass an argument
exec('write_smth("I failed because of %s" % error )')
def write_smth(smth):
sys.stdout.write(smth)
exec_smth()
Anyway, outside your __init__ function there're no StringVars at all thanks to garbage collector, so your code would fail anyway!
There're even more problems, because you never bind any of your sv{} to a widget and expect something in return! But ok, let's try to do things with exec:
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.entries = []
for _ in range(5):
exec('self.sv{} = tk.StringVar()'.format(_))
exec('self.sv{}.trace("w", self.change_sv)'.format(_))
exec('self.entries.append(tk.Entry(self, text="", textvariable=self.sv{}))'.format(_))
for entry in self.entries:
entry.pack()
def change_sv(*args):
# get id of a variable (you can't rely on that (0-9)!)
idx = args[1][-1:]
# get new value
value = getattr(args[0], 'sv{}'.format(idx)).get()
# result
print('Value changed in self.sv%s to %s!' % (idx, value))
app = App()
app.mainloop()
Output:
As you see - we always need a reference to StringVars and I think that option with a list of them is far better!
Note: If you need to pass something to callback function - use a lambda function! All code tested with Python 3.
Links:
The Variable Classes
Tkinter Callbacks
Behavior of exec function in Python 2 and Python 3

How do I update Kivy elements from a thread?

I have a socket client that calls a View() class every time it receives a message. I've split my code in such a way such that this class can simply use print() or any other display method, as I like. However, it seems that Kivy is not fond of this method. I've extended Kivy's BoxLayout class for my view and can call the message() function. The class looks something like this:
class View(BoxLayout):
def __init__(self, **kwargs):
super(View, self).__init__(**kwargs)
self.btn = Button(text='Default')
# Bind button press method
self.btn.bind(on_press=self.message)
self.add_widget(self.btn)
def message(self, message):
self.btn.text = 'Meow'
self.add_widget(Button(text='Meow'))
print(str(message))
The message function is indeed called and it prints but the interface does not update. When I press the button however, it does update the interface as well as print.
I've looked into using a StringProperty to modify the button text but have failed that too. Just as a note, in case what I'm doing is completely infeasible, I'm trying to later draw an entire interface consisting of width * height buttons, in the form of a board.
Any input is very much appreciated, it's been driving me insane.
EDIT 1*
I've followed on a few comments and tried a few things out. I've added a Clock class and have it schedule an update() method from View. The update method simply changes the text of a few elements. I've noticed it works when I schedule it to, as shown below:
def update(self, *args, **kwargs):
self.btn.text = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase) for i in range(32))
def message(self, message):
try:
print(message)
self.text = 'sending'
except Exception as e:
print(e)
The thread now simply assigns the text property as seen in message(). The periodically triggered update() methods works too, assigning random text. Now the issue however that it cannot set the text. This does not work:
def update(self, *args, **kwargs):
self.btn.text = self.text
I must be doing wrong elsewhere, any suggestions?
EDIT 2*
The error I'm trying to debug is here.
Since you don't post a full working example, I can only guess at what your doing. It seems you have an event (incoming message) on a thread, and you want to display some text when this occurs. You need to 'push' UI updates to the main thread, but you don't need to do periodic updates with Clock, you can just schedule one-time calls with Clock.schedule_once.
from functools import partial
def update(self, text, *a):
self.btn.text = text
def message(self, message):
Clock.schedule_once(partial(self.update, message), 0)
As inclement mentioned, you can do this 'push to main thread' automatically with the #mainthread decorator:
#mainthread
def update(self, text):
self.btn.text = text
def message(self, message):
update(message)
This way, whenever you call update it will be executed on the main thread.

Python - tkinter - How to capture bind function

from tkinter import StringVar, messagebox, Entry, Tk
def accept(event):
acceptInput=messagebox.askquestion("Input Assessment","do you accept this input?")
return acceptInput
window=Tk()
userInput=StringVar()
e=Entry(window,textvariable=userInput)
e.pack()
e.bind('<Return>',accept)
window.mainloop()
My question is: How do I capture the returned value of the accept function?
I've tried:
e.bind('<Return>',a=accept.get())
and
a=e.bind('<Return>',accept).get()
bound functions don't "return". Your callback needs to set a global variable or call some other function. For example:
def accept(event):
global acceptInput
acceptInput=messagebox.askquestion("Input Assessment","do you accept this input?")
... or ...
def accept(event):
acceptInput=messagebox.askquestion("Input Assessment", "do you accept this input?")
do_something(acceptInput)
It is up to you to define what you want to do in do_something (eg: write the data to disk, show an error, play a song, etc), or how you want to use the global variable in some other function.
Generally speaking, these things are easiest to accomplish if pieces of your application are instances of a class -- Then accept can just set an attribute on the class. In this case, you might want to bind that functionality up in the Entry:
class AcceptEntry(Entry):
def __init__(self, *args, **kwargs):
Entry.__init__(self, *args, **kwargs)
self.bind('<Return>', self.accept)
self.acceptInput = None
def accept(self, event):
self.acceptInput = messagebox.askquestion("Input Assessment",
"do you accept this input?")
For the function bind, the <Return> doesn't mean the return of a function. Instead, it does mean the event "Enter key" that is pressed by the user.
So, if you like to get the response from the messagebox, then you have do it with other ways. Possibly using with another StringVar() option with your messagebox, or using any global variable.

Tkinter entry widget get is undefined, not updating to my main function,

i'm following a few different guides to re-learn Tkinter by writing a little application that grabs stock prices. My issue that I am up a wall against is calling the .get() method from my entry widget variable. I've seen a couple other suggestions to put all the widget creating inside a function and then call the function in the init method, however I'm getting the same error, even when using the self argument in front of my entry variables. I know it's an issue with the way i'm passing data from function to function, but I can't wrap my head around it. Here's the code, sorry for the wall of text:
class MyApp:
def __init__(self, parent):
self.myParent = parent
self.myContainer1 = Frame(parent)
self.myContainer1.pack()
self.createWidgets()
button1 = Button(self.myContainer1, command = self.button1Click)
button1.configure(text = "get quote")
button1.pack()
def createWidgets(self):
root.title("Stock App")
self.symbol = Entry(self.myContainer1)
self.symbol.pack()
self.symbol.focus_set()
def button1Click(self):
stock = symbol.get()
print stock
I've taken it down to simplest form even and just had the button1Click call a callback function-
def button1Click(self):
print callback()
def callback():
print symbol.get()
This returns the exact same error:
NameError: global name 'symbol' is not defined
Is it getting destroyed too early? how do I fix this?
I've referenced multiple documents for tkinter and have seen some great fixes but none are extensible, or um unable to see how they relate to me using it inside of an object.
Thanks in advance for the help.
As far as I can tell inside of your button1Click method you need to add self as in:
def callback():
print self.symbol.get()
You're missing self. to make the callback:
def callback():
print self.symbol.get()
instead.

Categories

Resources