My question is in the context of a GUI. I'm trying to create a program that sends simply emails with a GUI where a user can enter their email, pass and recipient email. I've coded both the GUI and the logic in a single file, separated by functions and both of them are in the same class. Is this a good idea?
Anyway, moving on. I want to set the "Send" command to the "send_email" method I created in the class, but it says it has not been declared. My code (with all the irrelevant stuff removed for this purpose) is below:
class Application(Frame):
init, other widget creation\packing stuffs
def create_widgets(self):
widget creation methods...
#For the send button
self.send = Button(self.framepack, text="Send", width=10, command=send_email)
self.send.pack(padx=5, pady=5)
def send_email(self):
email sending code...
Unfortunately a setup like this raises a NameError where the send_email(self) function has not been defined. The ONLY other way I can get my code to work is when I use a config method for the send button to add in the command as follow:
root = Tk()
app = Application(root)
app.send.config(command=app.send_email)
Is my program badly structured? How would you do it? Would you simply write this procedurally and skip the whole OOP approach? (Saves a LOT of repititive "self" mentions when creating\referring to variables this way).
This is probably what you want:
self.send = Button(self.framepack, text="Send", width=10, command=self.send_email)
Related
I want to make a GUI command line using the Text widget. For debugging purposes, I am trying to print whatever the user types into the separate GUI window to the system terminal. I know that it is frowned upon to mix GUI and Text Based commands into the same script, but I am just debugging, so forgive me 😉
Here is my code:
from Tkinter import *
main = Tk()
console = Text(main)
console.pack()
main.mainloop()
while True:
text = console.get("1.0", "end-1c")
print(text)
My current issue is that when the mainloop starts, (of course) the while loop doesn't. If I were to move the while loop in front of the mainloop call, it would never call mainloop. I really want it to continuously check for new text.
Is there a way to like "pause" the mainloop, or just carry out the command, maybe on a new thread or something?
I want to avoid using main.after(), but if that is the only way, then so be it. ¯\(°_o)/¯
I recommend using main.after(), as it's the canonical way to do things like this in Tkinter. The following will also ensure that it only tries to print every second, instead of as fast as the console can handle it (as the while loop in your code would do if it worked).
def print_console():
print(console.get("1.0", "end-1c"))
main.after(1000, print_console)
print_console()
main.mainloop()
You can also bind widgets to "Modified"
from Tkinter import *
class TextModified():
def __init__(self):
root = Tk()
self.txt = Text(root)
self.txt.pack()
self.txt.focus_set()
self.txt.bind('<<Modified>>', self.changed)
Button(text='Exit', command=root.quit).pack()
root.mainloop()
def changed(self, value=None):
flag = self.txt.edit_modified()
if flag: # prevent from getting called twice
print "changed called", self.txt.get("1.0", "end-1c")
## reset so this will be called on the next change
self.txt.edit_modified(False)
TM=TextModified()
Hopefully this doesn't fall under "general discussion topic", since I'd like it to be more about resolving these issues in an efficient manner than a giant debate about which general approach to GUI programming is the absolute best.
So I've started some GUI programming with tkinter and long story short my code is getting pretty ugly pretty quickly. I'm trying to create a tile-based map editor for a video game. My main issues seem to be:
the inability of callbacks to return values.
the inability to transfer data between windows easily.
I assume that the reason I see these as issues is because I'm using functions a lot more than I'm using classes. For instance, my "load tileset" window is handled entirely functionally: Clicking the menu option in the main window calls the function that loads the new window. From within that window, I create an open file dialog when looking for the image, and modify the canvas displaying the image when I press the enter key (so that it draws the appropriate grid over the image). function function function.
What looks like really bad practice to me is the inclusion of extra arguments to compensate. For example, when I create a tileset, the instance of the TileSet class created should be sent back to the main window where the appropriate information can be displayed. I have a list of loaded tilesets as a global variable (even more bad practice: Everything dealing with my root window is in the global scope! yay!), and because callback functions don't return values, I pass that list as an argument to my "load tileset window" function, which then passes the argument to the create tileset function (called when you click the appropriate button in the window), where it's actually needed so that I can add my newly created tileset to the list. Passing arguments through a function 'hierarchy' like that seems like a horrible idea. It gets confusing, it's horrible for writing modular code, and just generally seems unnecessary.
My attempt at fixing the problem would be to write a class representing the whole GUI, and custom made window classes (that the GUI class can create and reference) that can actually store relevant data. That should take care of issues with transferring data between windows. Hopefully it would cut down on my gratuitous use of lambda functions in callbacks as well.
But I'm wondering: is this the best way? Or at least close? I'd rather not start rewriting and then end up with another system that's just sloppy and confusing in a different way. I know my methods are bad, but I don't really know what the best approach would be. I'm getting a lot of advice on how to do specific things, but none on how to structure the program as a whole. Any help would be greatly appreciated.
It sounds like you're trying to create a GUI that acts procedurally, which won't work. GUIs aren't procedural, their code doesn't run linearly where functions call callbacks which return values. What you're asking isn't unique to tkinter. This is the nature of event based GUI programming -- callbacks can't return anything because the caller is an event rather than a function.
Roughly speaking, you must use a global object of some sort to store your data. Typically this is called the "Model". It can be a global variable, or it might be a database, or it can be an object of some sort. In any case, it must exist "globally"; that is, it must be accessible to the whole GUI.
Often, this access is provided by a third component called a "Controller". It is the interface between the GUI (the "View") and the data (the "Model"). These three components make up what is called the model-view-controller pattern, or MVC.
The model, view and controller don't have to be three different objects. Often, the GUI and the controller are the same object. For small programs this works quite well -- the GUI components talk directly to your data model.
For example, you could have a class that represents a window which inherits from Tkinter.Toplevel. It can have an attribute that represents the data being edited. When the user selects "New" from a main window, it does something like self.tileset = TileSet(filename). That is, it sets the attribute named tileset of the GUI object named self to be an instance of the TileSet class specific to the given filename. Later functions that manipulate the data use self.tileset to access the object. For functions that live outside the main window object (for example, a "save all" function from the main window) you can either pass this object as an argument, or use the window object as the controller, asking it to do something to its tileset.
Here's a brief example:
import Tkinter as tk
import tkFileDialog
import datetime
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.windows = []
menubar = tk.Menu(self)
self.configure(menu=menubar)
fileMenu = tk.Menu(self)
fileMenu.add_command(label="New...", command=self.new_window)
fileMenu.add_command(label="Save All", command=self.save_all)
menubar.add_cascade(label="Window", menu=fileMenu)
label = tk.Label(self, text="Select 'New' from the window menu")
label.pack(padx=20, pady=40)
def save_all(self):
# ask each window object, which is acting both as
# the view and controller, to save it's data
for window in self.windows:
window.save()
def new_window(self):
filename = tkFileDialog.askopenfilename()
if filename is not None:
self.windows.append(TileWindow(self, filename))
class TileWindow(tk.Toplevel):
def __init__(self, master, filename):
tk.Toplevel.__init__(self, master)
self.title("%s - Tile Editor" % filename)
self.filename = filename
# create an instance of a TileSet; all other
# methods in this class can reference this
# tile set
self.tileset = TileSet(filename)
label = tk.Label(self, text="My filename is %s" % filename)
label.pack(padx=20, pady=40)
self.status = tk.Label(self, text="", anchor="w")
self.status.pack(side="bottom", fill="x")
def save(self):
# this method acts as a controller for the data,
# allowing other objects to request that the
# data be saved
now = datetime.datetime.now()
self.status.configure(text="saved %s" % str(now))
class TileSet(object):
def __init__(self, filename):
self.data = "..."
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
I am currently making a chat application in python. I am having 2 separate codes: one for the server and one for the client. The server script is taking the login data of new clients that connects and in another thread manages the messages that he has to receive and send.
The client application is made into a class and works well, excepts that when the script requests the UI to show, the only new window is a empty one:
def __init__(self, master):
self.nr=0
self.frameul=self.tbox=self.txt=self.scrollbar=self.button=self.roottk=[0]*20
self.OameniSiIduri={}
self.LoginUI(master)
self.framestate=""
def ChatUI(self, peer_id):
no=self.no
self.no+=1
self.PeoplesAndId[peer_id]=no
self.base[no]=Toplevel()
self.theframe[no] = Frame(self.base[no])
self.theframe[no].pack()
self.entry[no] = Entry(self.theframe[no], width=95)
self.tbox[no] = Text(self.theframe[no], state=DISABLED, wrap=WORD)
self.button[no] = Button(self.theframe[no], text="Send", fg="green", command=lambda x=self.entry[no].get(), y=peer_id, z=self.tbox[nr]: self.Sendmsg(x,y,z), width=10)
self.tbox[no].pack(side=TOP, fill=X)
self.button[no].pack(side=RIGHT)
self.entry[no].pack(side=LEFT)
.....
All the vars and the functions are declared. Can anyone give me a hint about what can the cause of this problem is?
Found myself the mistake after searching all the night through the code. Apparently if i use this line:
self.theframe=self.tbox=self.entry=self.scrollbar=self.button=self.base=[0]*20
All the objects point to the same value.
My guess is, the code that is creating the UI is throwing an error that you are not seeing. For example, are you importing DISABLED and WORD properly? If not, the code would fail after creating the frame but before creating the other widgets, leaving you with an empty widget.
One way to debug this is to give each toplevel and frame a distinct color. This lets you see which are visible and which are not -- maybe you're looking at a window or frame and thinking it's one thing when it's something else.
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?
I'm writing a client-server program in Python with Tkinter. I need the server to keep track of the connected clients. For this, I would like to have the client send an automated message to the server after the exit button(the standard "X" in the corner) is clicked. How can I know when the user is exiting the program?
You want to use the wm_protocol method of the toplevel window. Specifically, you are interested in the WM_DELETE_WINDOW protocol. If you use that method, it allows you to register a callback which is called when the window is being destroyed.
Usage:
root.protocol("WM_DELETE_WINDOW", app.on_delete)
You can use python atexit module.
For example:
import atexit
def doSomethingOnExit():
pass
atexit.register(doSomethingOnExit)
In my case, the following code didn't work:
root.protocol("WM_DELETE_WINDOW", app.on_delete) # doesn't work
However, it worked using this form:
root.wm_protocol ("WM_DELETE_WINDOW", app.on_delete) # does work
FWIW: It is also possible to assign a widget-specific behavior.
If you want an action to occur when a specific widget is destroyed, you may consider overriding the destroy() method. See the following example:
class MyButton(Tkinter.Button):
def destroy(self):
print "Yo!"
Tkinter.Button.destroy(self)
root = Tkinter.Tk()
f = Tkinter.Frame(root)
b1 = MyButton(f, text="Do nothing")
b1.pack()
f.pack()
b2 = Tkinter.Button(root, text="f.destroy", command=f.destroy)
b2.pack()
root.mainloop()
When the button 'b2' is pressed, the frame 'f' is destroyed, with the child 'b1' and "Yo!" is printed.
I posted the same answer on this topic.
Don't forget to use a lambda like this:
class App:
def run(self):
self.root.protocol("WM_DELETE_WINDOW", lambda: self.quit())
def quit(self):
self.root.destroy()