Triggering tkinter callbacks via GUI user interaction and line command - python

I am modifying a nice Chess game that I found here.
This game is initialized this way:
root = tk.Tk()
app = App(root)
app.canvas.bind("<Button-1>", app.callback)
root.mainloop()
This initialization provide a GUI with a chessboard. Every time that I move a piece, a
def callback(self,event)
def displayBoard(self):
is triggered and make all the magic (move the piece, check if is a valid move, and etc)
This is working fine. However, I would like to change this so I can also trigger this function by, for example, the command line.
Explaining in better detail, I wanna be able to move a piece of the chessboard not only by the user input via the GUI but also typing commands (e.g. move to position x,y) instead of only being able to play via GUI.
Is this a possible change or would I have to change the whole core?

Related

Restart button for a tkinter tic-tac-toe [duplicate]

I just want the equivalent of closing and reopening my main program. I want to invoke it when a "new"-like option from a drop-down menu is clicked on. Something like calling root.destroy() and then re-initiating the mainloop.
How can I get this done?
There are at least three ways you can solve this.
Method one: the head fake. When you create your app, don't put all the widgets in the root window. Instead, hide the root window and create a new toplevel that represents your application. When you restart it's just a matter of destroying that new toplevel and re-running all your start-up logic.
Method two: nuke and pave. Similar in concept but slightly different in execution. In this model, when you want to restart you simply delete all the widgets in the main window, reset the geometry to null (so the window will once again resize itself based on its contents) and then run the logic that draws all the other widgets.
Method three: if it worked the first time... As suggested by Martin v. Löwis, simply have your program exec a new instance of the program, then exit.
The first two methods are potentially faster and have the (dis?)advantage of preserving the current environment. For example you could save the copy of the clipboard, column widths, etc. The third method absolutely guarantees a blank slate.
If you are on Unix, restart the entire application with os.execv. Make sure you pass all command line arguments etc.
You could take all your GUI building logic and initial state code out of the mainloop and put it into functions. Call these functions from the mainloop (something like: buildgui() & initstate()) and then, when the user clicks your menu icon, just call initstate() to set it back like it was when the application first started.

Tkinter code runs without mainloop, update, or after

I have been trying to use tkinter to make a gui to select some excel files and sheets from those files.
I have a lot of experience in python, but I'd probably say a novice at tkinter.
The code I have written to select the files is shown below (typos are likely because I cannot access the internet on the machine these files are on, so I am typing it up here).
My question is about mainloop(), the update functions, and after(). I had mainloop() at the end of my code, but my program wouldn't terminate (i.e. the terminal would remain) after it did what it does. So I removed the mainloop() and now it functions perfectly without any mainloop(), update(), or after() calls.
I don't really understand this and would really like to. I get that mainloop() stops the code from progressing until the root closes, but I thought nothing would show up without mainloop(), and this code does wait for the user to close the windows before continuing.
I was also wondering if I do not have mainloop (or the like), the code still closes fine whether or not I have root.destroy() at the end of the App class (commented in the code). I don't get that either.
This information would help me make better and correct code in the future and hopefully improve this one. FYI, I have searched for the answer to this everywhere and could not find one, at least one I could understand. Thanks a bunch.
This is as minimal as I can think of for this code.
Code (Edited from original post):
import tkinter as tk
from tkinter import ttk
class App:
def __init__(self, parent):
self.root = parent
self.root.withdraw()
tl = tk.Toplevel(parent)
b = ttk.Button(tl, text="Test widget")
b.grid()
tl.wait_window()
# This line does not change how the code functions at all
#self.root.destroy()
def run(self):
# Whether or not this is a function in the class or called globally doesn't matter.
self.root.mainloop()
if __name__ == "__main__":
root = tk.Tk()
app = App(root)
# This is the mainloop() call. If I include it, the program does not close after
# running the code in App.__init__. Without it, it runs perfectly.
# app.run()
mainloop enters an event listening loop for your tk.Tk() application.
You should create only one object with Tk() in every tkinter application.
Now this loop will "globally" listen for GUI events and will call your registered callbacks (event handlers). Code before the mainloop call will run but nothing will be shown or updated in the screen until you call it. No event (user input, mouse movement, keyboard and others) will be queued and responded to before that. But the widgets are created and configured, just not shown yet. If you call root.update() without ever entering the mainloop you will eventually see the window flash and disappear because everything is running, and you force window refresh, but then suddenly the program ends.
So configure your GUI and in the end always run root.mainloop()
Now when you call wait_window, you are creating a local event loop (and blocking the mainloop if it was running). For a perfect explanation of wait_window by Brian see here
What happened to you is that your app is happily running with your local event loop, but without mainloop. That would be a strange thing to do. Even if you are making as Brian explains, a modal window, you want to "return" to the mainloop after the window closes.
As for the after method it just registers a callback function to run after your choosen time, or after the mainloop becames idle, with after_idle (nothing more to refresh, no events in the queue..., also think as soon as possible). And if you want to re-run that function again in the same way it should re-register with after or after_idle before returning.
So always use your mainloop() ending call - or should I say event loop starting call :)
Edit:
Also, don't do things in your code that are slow, or that may block the loops, such as calling sleep or any other blocking function. That will freeze the GUI, until the block ends.

Kivy equivalent to tkinter's messagebox

I'm attempting to transition a program from tkinter to Kivy. When I got to a section where I used messagebox.askyesno, I figured that I could just create a popup with a couple of buttons, and I'd be done. The issue I've encountered is that, while Kivy's popup and tkinter's messagebox are both modal, when I call messagebox.askyesno in a function, messagebox will halt all execution of the current function until the messagebox is destroyed, while the popup will allow the function to finish. My original program had
flag = messagebox.askyesno(message='...',parent=self)
if flag:
#Stuff if flag is true
else:
#Stuff if flag is false
However, this will not work with a Kivy popup since the popup will open, and the program will continue to execute. Is there a way to halt execution until the popup has been destroyed, or another way to solve the problem?
The basic idea is a quasi-dialog for a two player game. Here, the program asks one player if he wants to perform an action, such as move a piece. If the player says "yes," then the second player is given a messagebox.askyesno for a counter-move. A simple analogy is advancing a runner from first base to third base on a single in baseball. You would have to ask the offensive team if he wants to advance the runner, or have the runner remain at second. If the answer is yes, then the program would have to ask the defensive team if he wants to throw to third. It would definitely be possible to create a function to handle each instance of askyesno, with appropriate bindings, but it seems excessive.
I'm not very familiar with how tkinter does things, but kivy requires a slightly different mental model here. You don't want to stop and start the eventloop in between bits of python code, but instead probably want to start the popup, pass any state you need into it or store it somewhere else, then bind the result of the popup (e.g. when the user presses a 'done' button) to some new function that does the rest of your calculation.
I can provide an example if you like, especially if you give more information about what you're trying to do.

When do I need to call mainloop in a Tkinter application?

Every tkinter tutorial I have seen claims that tkinter.mainloop must be called for windows to be drawn and events to be processed, and they always call this function, even in hello world programs. However, when I try these out in the interactive shell, windows are drawn correctly without having to call mainloop. This example of embedding matplotlib graphics in tkinter produces a relatively complex application, with buttons for panning, zooming and resizing a plot within a tkinter window, and again, this all works if you remove the call to mainloop and run the code in the interactive shell. Of course, if I run the script (with mainloop removed) outside the interactive shell, the program ends too quickly to see what happens, but if I add a call to input to hold the program open everything works correctly (I'm running python 3.2.2 on linux).
So what exactly does mainloop do, and when is it necessary to call it?
EDIT:
To clarify, if I open up the GNOME terminal and type
$python3
>>> import tkinter
>>> root = tkinter.Tk()
a window immediately appears without having to call mainloop, and more complex tkinter functionality seems to work as well (for example, adding buttons to the window). In IDLE, a call to mainloop is necessary. It was my understanding that nothing should be drawn, and no events should be processed, until mainloop is called.
The answer to your main question is, you must call mainloop once and only once, when you are ready for your application to run.
mainloop is not much more than an infinite loop that looks roughly like this (those aren't the actual names of the methods, the names merely serve to illustrate the point):
while True:
event=wait_for_event()
event.process()
if main_window_has_been_destroyed():
break
In this context, "event" means both the user interactions (mouse clicks, key presses, etc) and requests from the toolkit or the OS/window manager to draw or redraw a widget. If that loop isn't running, the events don't get processed. If the events don't get processed, nothing will appear on the screen and your program will likely exit unless you have your own infinite loop running.
So, why don't you need to call this interactively? That's just a convenience, because otherwise it would be impossible to enter any commands once you call mainloop since mainloop runs until the main window is destroyed.
Compare a program with an interactive GUI to a program that calculates the hundredth Fibonacci number. All the latter program has to go through a series of steps in order, top to bottom. The set of steps and their sequencing can be known in advance, and it'll remain constant no matter how many times you run the program.
But the GUI program is different: at any given moment, it has to be able to handle all sorts of different kinds of events and interactions. This requirement is often implemented using a programming construct called an event loop. An event loop is the central control structure of a program. It waits for an event to happen, and then dispatches the appropriate handler.
You didn't mention which interactive shell you're using, but I'm guessing it's IDLE. IDLE itself is a Tkinter program, and it already has an event loop going. So possibly the Tkinter code you are typing into the shell is getting bound to IDLE's event loop.
When you execute your code, the tkinter window will refuse to open without there being a mainloop function.
For example this will not work:
from tkinter import*
root=Tk()
This, however, will work:
from tkinter import*
root=Tk()
root.mainloop()
If you’re using python interective shell, you don’t need to call the root.mainloop() function. But if you are coding in a file, suppose one in IDLE, you need to call out the mainloop() function in order for your program to work as follows:
from tkinter import *
root = Tk()
# Elements in the GUI must go here eg:
lbl = Label(root, text=“Text”)
lbl.pack()
# At the end of the program, do this:
root.mainloop()
As follows:
from tkinter import *
tk = Tk()
canvas = Canvas(tk, width=500, height=500)
canvas.pack()
canvas.create_line(0, 0, 500, 500)
mainloop()
I've decided that, instead of sticking a call directly to mainloop anywhere in my script, I'll just add it as part of atexit - that is, when the Python interpreter decides it's time to start closing down, it's going to enter Tk's mainloop. This then prevents it from finishing the shut down sequence until the user actually tells Tk to quit (IE, with command-Q on a Mac, or by clicking on the red X in Windows.)
from Tkinter import Tk
root = Tk()
import atexit
atexit.register(root.mainloop)
There seems to be no need to call mainloop from a system command line. The Python interpreter will continue running without it, because it's waiting for further input from you (until you run exit()).

Highlight Select Box in Python

I am trying to rebuild the functionality of the desktop's "highlight to select" feature so that I can use it in my own app. When I say "highlight to select" I mean the selection box that shows up if you click and drag on your desktop (native to all main-stream OS).
I've been working for hours trying to recreate it, and simply can't find a way. I've tried PyGTK, Xlib for python, and a couple other weird hacks. All of which have their own problems that won't allow me to move forward.
I generally don't ask for straight up example code without providing some sort of starting point, but in this project I don't even know where to start. How would you do this?
Here's the requirements:
Must draw on the root window (or a transparent layer that "appears" to be the root)
Must return the coordinates of the selection (x, y, height width)
Update: Forgot some details.
I am using Ubuntu 10.10
I have dual monitors (though, I don't think that should matter)
I don't mind downloading any extra libraries that are necessary
I don't know if this is what you're looking for, but what if you created another window in your module, and have your code show it when you release drag? You could fetch the cursor's current position, and have it draw the window there.
This should help you get the mouse position on the root window.
So, your code may look a little like this (this is untested code!) I'm only showing the relevant portions of what goes inside __ init __.
def __init__(self):
...
#Some of your code here.
...
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
#Note that I am creating a popup window separately.
popwin = gtk.Window(gtk.WINDOW_POPUP)
#I am setting "decorated" to False, so it will have no titlebar or window controls.
#Be sure to compensate for this by having another means of closing it.
popwin.set_decorated(False)
def ShowPopup():
#You may need to put additional arguments in above if this is to be an event.
#For sake of example, I'm leaving this open ended.
#Get the cursor position.
rootwin = widget.get_screen().get_root_window()
curx, cury, mods = rootwin.get_pointer()
#Set the popup window position.
popwin.move(curx, cury)
popwin.show()
def HidePopup():
#This is just an example for how to hide the popup when you're done with it.
popwin.hide()
...
#More of your code here.
...
#Of course, here is the code showing your program's main window automatically.
win.show()
A very simplistic approach, but it should give the appearance of what you're wanting.

Categories

Resources