Python ttk.Button -command, runs without button being pressed - python

I'm making a small script in python with ttk and I have a problem where a function runs where it shouldn't. The button code looks as follows:
btReload = ttk.Button(treeBottomUI, text="Reload", width=17, command=loadModelTree(treeModel))
btReload.pack(side="left")
and the function is as this:
def loadModelTree(tree):
print ("Loading models...")
allModels = os.listdir(confModPath)
for chunk in allModels:
...
For some reason, the function runs without the button being pressed. Why?

Markus, yes, that's the right solution, but it is not because you can't use multi-argument commands in widget callouts. Consider, in your original code, ...command=loadModelTree(treeModel)... is an invocation of the method. Lambda allows you to abstract the command so you can have an arbitrary number of arguments without confusing the interpreter by invoking it, e.g., ...command=lambda arg1=myarg1, arg2=myarg2, arg3=myarg3: myCallout(arg1, arg2, arg3)....
I hope that makes what is going on a bit clearer.

Well, as I found the answer, I'll answer my own question.
It appers that ttk.button commands does not support sending arguments to functions so the work around is to do as follows:
btReload = ttk.Button(treeBottomUI, text="Reload", width=17, command=lambda i=treeModel: loadModelTree(i))
btReload.pack(side="left")
Simple as pie!

Related

Python TK Notebook tab change check

Newbie programmer here. I am building a tk based desktop app and ran into an issue:
I have a main window with several stuff in it including two tabs:
global nBook
nBook = ttk.Notebook(self, name="book")
nBook.place(x=300,y=400)
frameOne = ttk.Frame(nBook, width=100, height=100)
frameTwo = ttk.Frame(nBook, width=100, height=100)
nBook.add(frameOne, text='T1')
nBook.add(frameTwo, text='T2')
frameOne.bind("<<NotebookTabChanged>>", self.routine())
frameTwo.bind("<<NotebookTabChanged>>", self.routine())
routine() is a function that SHOULD perform a check every time T2 is selected
def routine(self):
if str(nBook.index(nBook.select())) == "2":
# Do stuff
else:
pass
Problem is that it doesn't do anything when the tab is changed except for calling the routine function as soon as I open the app and never again. I just can't figure out what I'm doing wrong.
Could anyone point out the mistake(s) I'm making?
EDIT: Same issue if I try
nBook.bind("<<NotebookTabChanged>>", self.xbRoutine())
The error comes from the event binding statements: when using self.routine() the callback is called when the bind statement is executed, not when the event is triggered. To get the correct behavior, the second argument of bind should be the name of a function not a call to this function, so simply remove the parentheses.
Another error: when using bind, the callback function is expected to have a first argument (traditionnaly called event) storing the event parameters. So you should define your callback as:
def routine(self, event):
...
I had the same problem. The answer given by #sciroccorics is not complete.
What you bind is not the tab itself, but the notebook.
So, it should be
nBook.bind("<<NotebookTabChanged>>", self.xbRoutine)
Alternatively you could use lambda.
In your case this will look something like this:
frameOne.bind("<<NotebookTabChanged>>", lambda _: self.routine())
Don't forget the _, otherwise you will get a TypeError, since the event is passed as an argument.
lamba is really helpful if your function requires one or more arguments.

Replacing input() portions of a Python program in a GUI

I am pretty new to python and have been working with a program that was originally meant for the command line. As such, it uses the input() function quite a bit, especially in the middle of loops. Mostly, the original code would do something like this:
for item in list:
# some logic here
user_input - input("prompt")
# uses user_input
I've decided I want to make a GUI for it, but I also want to make it optional. I have a class called Viewer that decorates the original program, and I am struggling to figure out how to best to handle the calls to input().
My first thought was just to inject a new input function altogether so that way it looks for input in my GUI text box instead the sys.stdout. I found many Stack Overflow answers on how to print sys.stdout to a GUI element (like this), but not how to take input from one. All of my attempts either ended in freezing the GUI by creating a loop, or the program not pausing for input and simply grabbing what was in the box when the prompt was put forward.
Secondly, I tried to break apart my functions to better match the code examples for buttons. So, button 1 would trigger a function_part_1, which would go until it required an input. If the GUI flag was turned off, it would automatically go to an input function, otherwise it would return and a button press would trigger function_part_2:
def function_part_1(list):
item = list[0]
# do some logic on item 1
if GUI:
print("prompt")
return
# After this, the GUI waits for a button press, and then calls function_part_2
else:
function_input(list)
def function_input(list):
user_input = input("prompt")
function_part_2(user_input, list)
def function_part_2(user_input, list):
# uses user_input on item
list.remove(list[0])
if list:
function_part_1(list)
else:
return
However, this turned ugly really quickly. First, it broke apart many loops which would require a lot of refactoring to keep the logic in tact (Especially for the nested loops). It also requires that I pass all my local variables from function-part to function-part, which exploded the function headers. In order to have only a single input box for the user, the GUI has to have logic to know which function is executing and what function-part comes next. It made my functions harder to follow and hurt readability.
Is there a nicer way to do this?
I am using appJar for the GUI, which is based around Tkinter. I am willing to switch to pure Tkinter if that would make things easier.
The easiest way is to overwrite the input function. Here's a working example using tkinter:
import tkinter as tk
def input(prompt=''):
win= tk.Tk()
label= tk.Label(win, text=prompt)
label.pack()
userinput= tk.StringVar(win)
entry= tk.Entry(win, textvariable=userinput)
entry.pack()
# pressing the button should stop the mainloop
button= tk.Button(win, text="ok", command=win.quit)
button.pack()
# block execution until the user presses the OK button
win.mainloop()
# mainloop has ended. Read the value of the Entry, then destroy the GUI.
userinput= userinput.get()
win.destroy()
return userinput
print(input('foobar'))

Python Tkinter Multiple Commands

I'm currently working on a GUI using Tkinter and Python. One of the windows I create has two buttons on it: one to restart a separate python script and the other one to shut down the whole program.
When I hit the "restart" button, I'd like it to run the restart code, and then destroy the window that has the two buttons on it. I saw something else on SO that let you run two commands at once through a button click but I can't seem to get it to work. Right now the code for the button is:
buttonRestart = Button(restartWindow, text = "Restart", width = 8,
height=3, command = lambda: self.restartExternal() and
restartWinow.destroy)
When executed, it seems that the restartExternal code is working, but it doesn't destroy the window as well. Any suggestions would be greatly appreciated!
Just create a method that calls the two methods. Tere's no shame in creating an extra function for this. It's a much more maintainable solution that trying to cram a bunch of code into a lambda.
def on_restart(self):
self.restartExternal()
self.restartWinow.destroy()
buttonRestart = Button(..., command = self.on_restart)
Instead of self.restartExternal() and restartWindow.destroy you could do [self.restartExternal(), restartWindow.destroy()]. This way, it will call restartWindow.destroy() whatever self.restartExternal() returns, whereas how it is, if self.restartExternal() returns False, Python doesn't even check to see if restartWindow.destroy is True or False. Besides that, restartWindow.destroy isn't even called in yours, because you left out the parentheses.
The answer proposed by Bryan looks reasonable, but with minimum changes - you can feed the list of functions to lambda function, like this:
buttonRestart = Button(restartWindow, text = "Restart", width = 8,
height=3, command = lambda: [self.restartExternal(),
restartWinow.destroy()] )
In my opinion, it looks better for two functions, at least.

TypeError: printName1() takes 0 positional arguments but 1 was given

So I have this very simple thing I wrote and it's killing me trying to figure out why it won't work. All it does it print a statement when you click.
So for the first example I had a button and assigned the function printName1 directly to it, which worked perfectly fine.
Then the next thing was to bind it using the .bind() function. So in this case we just have a frame that prints out certain things based on which button you press. But unfortunately whenever I use bind, it throws the error show above. References tkinter\__init__.py for the error, so it's not something directly in my code but maybe it needs to be done differently? Thanks guys.
from tkinter import *
root = Tk()
def printName1():
print('Jack')
def printName2():
print('John')
def printName3():
print('Jill')
frame = Frame(root, width=300, height=250)
frame.bind("<Button-1>", printName1)
frame.bind("<Button-2>", printName2)
frame.bind("<Button-3>", printName3)
frame.pack()
root.mainloop()
EDIT: The error is confusing because it made it seem like there was an extra argument when there should be 0. But actually I needed to add an argument to the functions and that was event. so it should be def printName1(event) and so on. Just figured I would let you guys know what worked for me in case anyone stumbles upon this.
If you refer to the documentation regarding tkinter events and bindings, you will see that when an event is triggered, the associated event object will be passed as the first (and only) argument to the bounded function (being printName1 and friends in your case).
So what you need to do is to modify those printName* functions to accept the event argument.
def printName1(event):
print('Jack')
Then what you desired to achieve should work.
Naturally, you could make the event argument optional as #TigerhawkT3 suggested.
Events, such as from the keyboard/mouse, are all sent to the application with information about the event: which key was it, where was the mouse when you clicked, that sort of thing. This means that any callback bound to such an event needs to take an argument. If you want to also bind it to a Tkinter Button, which doesn't take an event, you can handle that as well. Just define your functions with a default argument:
def printName1(event=None):
...

could anyone give a simple python for explaining the "Hollywood principle"/inversion of control

I'm very interested in the Twisted web framework. As far as I know the framework
uses the Hollywood principle. I just know the term but totally have no
idea about this design pattern.
I have done a lot of Google searching on the implementation of the
Hollywood principle in Python. But there were few results.
Could any one show me some simple python code to describe this design pattern?
I've actually never heard the phrase "Hollywood principle" before, nor am I familiar with Twisted (though I feel I should be). But the concept of inversion of control isn't so difficult. I think GUI programming is a good way to introduce it. Consider the following from here (slightly modified).
import Tkinter
class App(object):
def __init__(self, master):
frame = Tkinter.Frame(master)
frame.pack()
self.button = Tkinter.Button(frame, text="QUIT", fg="red", command=frame.quit)
self.button.pack(side=Tkinter.LEFT)
self.hi_there = Tkinter.Button(frame, text="Hello", command=self.say_hi)
self.hi_there.pack(side=Tkinter.LEFT)
def say_hi(self):
print "hi there, everyone!"
root = Tkinter.Tk()
app = App(root)
root.mainloop()
This is a very simple example of inversion of control. It uses callbacks -- hence the Hollywood principle moniker (thanks Sven for the link). The idea is that you write a function, but you never call it. Instead, you hand it over to another program and tell that program when to call it. Then you give control to that program. Here's a detailed explanation of the code:
import Tkinter
class App(object):
We start with a class definition, which will hold our callbacks and pass them to the appropriate parts of what I'll call the "master program."
def __init__(self, master):
Our class needs a "master program"; the master program is what will call the functions we define. In this case, it's the root window of the GUI. More properly, in the context of GUI programming, we might say that master is the parent of frame.
frame = Tkinter.Frame(master)
frame.pack()
These two lines create a Frame object, which is essentially a box that contains widgets. You'll see what a widget is in a second. As you can see, it also has a parent -- the same one as our App's: master.
self.button = Tkinter.Button(frame, text="QUIT", command=frame.quit)
self.button.pack(side=Tkinter.LEFT)
self.button is a widget. When you create it with Tkinter.Button, you give it some properties, like a label (text="QUIT"). You also tell it what its parent is -- in this case, not master, but frame. So now we have a hierarchy -- master -> frame -> button. But the most important thing we do is this: command=frame.quit. This tells the button what to do when it is activated by a mouse click. This is, in short, a callback. Here, we pass it the frame's quit method, which in this case causes the whole program to quit. Note that the function is not followed by () -- that's because we don't want to call it. We just want to hand it over to button.
self.hi_there = Tkinter.Button(frame, text="Hello", command=self.say_hi)
self.hi_there.pack(side=Tkinter.LEFT)
This is another widget that is almost identical to the first, the only exception being that instead of passing self.quit as a callback, we've passed self.say_hi. Since this is defined below, you could substitute any function you wanted here. (In both the above sets of lines, self.button.pack just tells the Button where it should go in frame.)
def say_hi(self):
print "hi there, everyone!"
say_hi is where you define what the Hello button does.
root = Tkinter.Tk()
app = App(root)
Now we invoke our class, creating an instance. We create the root window, and then create an App instance with root as its parent.
root.mainloop()
And then we're done. We pass control to Tkinter, which does the rest of the work.
Twisted is designed with an asynchronous approach. Your program causes something to happen, but instead of waiting for that thing to happen, it passes control back to the twisted event loop ('reactor' in twisted parlance). When the reactor finds that something needs your attention, it will invoke your program with the 'callback' you supplied when you originally invoked the action. The basic workflow looks like this. First,
something_deferred = make.twisted.do.something()
the something_deferred is usually a twisted.internet.defer.Deferred instance that represents the result of something(). Usually, something() takes some time to complete, and so the deferred object doesn't yet have the results, even though something() returned immediately. What you then have to do is define a function that twisted can call once the result is actually ready.
def something_callback(result):
do_something_else(result)
In most cases, you should also define a function that handles the case that an error occured in something(),
def something_errback(fault):
cleanup_something()
Finally, you have to tell twisted that you want it to use those functions when something() is finally ready.
something_deferred.addCallbacks(something_callback, something_errback)
Note that you don't call the functions yourself, you just pass their names to addCallbacks(). Twisted is itself responsible for calling your functions.
Not every example follows this exact pattern, but in most cases, you put an instance method in a class that implements a twisted defined interface, or pass a callable to twisted functions that produce events.
The "hollywood principle" is so named because, in the movie industry in Hollywood, nervous first-time actors sometimes call the studio over and over again after an audition, to see if they got the part. In software development we call this polling, and it's super inefficient. In the Hollywood metaphor, it wastes even a successful applicant's time (because they may call repeatedly before the selection has been made) and it wastes the studios time (because many applications who were not selected will nevertheless call them).
Here's a Python file which hopefully illuminates the principle in action:
First, we have an Actor, who can perform for an audition, or get a part:
class Actor(object):
def __init__(self, name):
self.name = name
def youGotThePart(self):
print self.name, "got the part."
def perform(self):
print self.name, "performs an audition."
Then, we have the casting process:
applicants = []
def audition(actor):
actor.perform()
applicants.append(actor)
def cast():
import random
selection = random.choice(applicants)
selection.youGotThePart()
An audition asks an actor to perform, and when casting happens, an actor is selected (at random - as a non-participant in Hollywood's process, I suspect this is probably the most realistic simulation). That actor then gets notified (the studio "calls" them).
And finally, here's the whole casting process:
alice = Actor("alice")
bob = Actor("bob")
carol = Actor("carol")
dave = Actor("dave")
audition(alice)
audition(bob)
audition(carol)
audition(dave)
cast()
As you can see, this is very simple and doesn't involve GUIs or networks or any other external sources of input. It's just a way to structure your code to avoid wasteful checking and re-checking of the same state. If you think about structuring this program to not follow the principle in question, you'd have a loop something like this:
while True:
for actor in alice, bob, carol, dave:
if actor.didIGetThePart():
break
maybeGiveSomeoneThePart()
which is of course very wasteful, since who knows how many times the actors might make those calls before the studio chooses?

Categories