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
Related
I am trying to use a thread to run a function with multiple arguments. Whenever I tried to execute the code, it would say I was providing 1 too many arguments for the function. In my last attempt, I used 1 less argument than the function needed, and voila it works by using the class itself as an argument. Here is my code.
import threading
import sys
import tkinter
class Window():
'''Class to hold a tkinter window'''
def __init__(self, root):
'''Makes a button'''
button1 = tkinter.Button(root,
text = ' Print ',
command = self.Printer
)
button1.pack()
def Function(x,y,z):
'''function called by the thread'''
print(x)
print(y)
print(z)
def Printer(self):
'''function called by the button to start the thread'''
print('thread started')
x = threading.Thread(target=self.Function, args=('spam', 'eggs'))
x.daemon = True
x.start()
root = tkinter.Tk()
Screen = Window(root)
root.mainloop()
Here is the resulting output. Normally I would expect some kind of error from this; note that I only specified 2 arguments when the function calls for three!
thread started
<__main__.Window object at 0x000001A488CFF848>
spam
eggs
What is causing this to happen? Using python 3.7.5 in IDLE, if that is making a difference.
Function is a method, so call self.function implicitly provides self as a first argument. If that is not your intended behavior either consider switch to a static method or use a function.
The Problem
I'm working on a Python GUI, using Tkinter. I'm also trying to add "toaster" messages, using Ptoaster. Here's an example of what I'm trying to achieve:
from tkinter import *
import ptoaster
PADDING = 20
class MyInterface:
def __init__(self):
self.root = Tk()
self.label = self.make_label()
print_welcome()
def make_label(self):
result = Label(self.root, text="Hello, world!")
result.pack(padx=PADDING, pady=PADDING)
return result
def run_me(self):
self.root.mainloop()
def print_welcome():
message = "Hello again!"
ptoaster.notify("Hello!", message)
interface = MyInterface()
interface.run_me()
If I try to run the above code, one of two things will happen:
The command line will spit out the following error:
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
python3: ../../src/xcb_io.c:260: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.
XIO: fatal IO error 25 (Inappropriate ioctl for device) on X server ":0"
after 207 requests (207 known processed) with 2 events remaining.
Aborted (core dumped)
My whole laptop will freeze, necessitating a hard reset.
However, if I move the call print_welcome() from outside of MyInterface, so that it's called before this class is initialised, then none of the above errors crop up.
What I'd Like to Know
How to call a function, from within a Tkinter GUI class, which causes a "toaster" message to be displayed, without causing the whole platform to crash.
Why the above errors are cropping up.
Documentation states it needs to be verified that the ptoaster.notify is called from the main program.
IMPORTANT - you need to make sure you call notify from the main program
Working code for me:
from tkinter import *
import ptoaster
PADDING = 20
class MyInterface:
def __init__(self):
self.root = Tk()
self.label = self.make_label()
print_welcome()
def make_label(self):
result = Label(self.root, text="Hello, world!")
result.pack(padx=PADDING, pady=PADDING)
return result
def run_me(self):
self.root.mainloop()
def print_welcome():
message = "Hello again!"
ptoaster.notify("Hello!", message)
if __name__ == '__main__':
interface = MyInterface()
interface.run_me()
Documentation (See: Sample Program)
Hello I'm trying to make a program where it asks for a python module, get its functions, append them into a list, and put the list values as options for a OptionMenu. I have the following code:
def fetch_module():
global get_module
global functionmodulename
get_module = askopenfilename(title="get module", filetypes=[("Python source","*.py")])
functionmodulename = inspect.getmodulename(get_function)
getfunctions(sys.modules[get_module])
def fetch_function(module)
global l
l = []
for key, value in module.__dict__.items():
if type(value) is FunctionType:
l.append(value)
if __name__ == "__main__":
function_name = StringVar
OptionMenu(function_name, l).pack()
fetch_module()
mainloop()
However, if I run this, a "[]"show up as the only choice. can I fix this?
There are several things to fix in your code. First of all, you need to create the Tkinter app, let's call it master.
Right now you are assigning the class StringVar to the variable function_name. That does not work. You need to create a new StringVar instance.
Putting a list of things in an OptionMenu is also tricky. see: http://effbot.org/tkinterbook/optionmenu.htm
import Tkinter
# or depending on your python version: import tkinter
if __name__ == "__main__":
master = Tkinter.Tk()
sv = Tkinter.StringVar(master)
l = ['a','b','b','c']
apply( Tkinter.OptionMenu, (master, sv) + tuple(l)).pack()
Tkinter.mainloop()
I am having problem with the callback function of the tkinter trace method. I would like to have 2 entries and the value of each entry depends on the value of another. So if I change the value of one, the value of the other is changed. This is some simple code doing this:
from tkinter import *
class main():
def __init__(self, master):
self.a = DoubleVar(value=2.0)
self.b = DoubleVar()
self.b.trace("w",self.calc_c)
self.c = DoubleVar()
self.c.trace("w",self.calc_b)
Entry(master,textvariable=self.b).grid(row=0,column=0)
Entry(master,textvariable=self.c).grid(row=0,column=1)
def calc_b(self,name,index,mode):
self.b.set(self.c.get()/self.a.get())
def calc_c(self,name,index,mode):
self.c.set(self.b.get()*self.a.get())
root = Tk()
prog = main(root)
root.mainloop()
The program is actually working, returning the right values, but also an error is produced:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib64/python3.3/tkinter/__init__.py", line 1475, in __call__
return self.func(*args)
File "/home/anze/foo.py", line 22, in calc_c
self.c.set(self.b.get()*self.a.get())
File "/usr/lib64/python3.3/tkinter/__init__.py", line 332, in get
return getdouble(self._tk.globalgetvar(self._name))
ValueError: could not convert string to float:
Can someone please explain the meaning of this error?
Thank you!
I think the problem is that the widget is an Entry, which takes a string as input, and your value is a DoubleVar, but you have no validation in place to prevent the user entering an invalid value.
Your code works as long as the input value is a float. When you call get() in calc_c and calc_b after an invalid value was entered, tkinter tries to convert the string in the Entry into a float, fails and throws a ValueError exception.
You can catch the exception with a try-except block. In the try block you put the code that might raise an exception. The except block defines what happens when an exception is caught. You can also have an else block that defines what happens when no exception is caught.
Here's a simple example (with no else block) for calc_c that restores the previous value (calc_b would need to be modified in a similar way):
def calc_c(self,name,index,mode):
try:
self.c.set(self.b.get()*self.a.get())
except ValueError:
# here you can either complain to the user (message-box...)
# or just restore the last value using the other entry like so:
self.b.set(self.c.get()/self.a.get())
I believe your issue is that you are binding self.c to an Entry widget:
Entry(master,textvariable=self.c).grid(row=0,column=1)
But, the textvariable should be set to a Tkinter.StringVar. How about creating a new variable to hold the Entry's string value and then convert in your calc method:
from tkinter import *
class main():
def __init__(self, master):
self.a = DoubleVar(value=2.0)
self.b = DoubleVar()
self.b.trace("w",self.calc_c)
self.c = DoubleVar()
self.c_str = StringVar()
self.c.trace("w",self.calc_b)
Entry(master,textvariable=self.b).grid(row=0,column=0)
Entry(master,textvariable=self.c_str).grid(row=0,column=1)
def calc_b(self,name,index,mode):
self.c.set(float(self.c_str.get()))
self.b.set(self.c.get()/self.a.get())
def calc_c(self,name,index,mode):
self.c.set(self.b.get()*self.a.get())
root = Tk()
prog = main(root)
root.mainloop()
Note you'll probably want to do the same for the other Entry.
I had a similar error using Tk.scale(... resolution=0.1, ...).
When there was matplotlib used in the same script, the decimal number from scale was written with , instead of . and caused an error in getdouble().
I found a workaround by changing the regional settings of the os(kubuntu14.04) from German to US with . as default decimal seperator.
It not satisfying solution but may help to find the problem.
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.