Tkinter mouse event initially triggered - python

I'm currently learning Tkinter and I cannot find a solution for my problem here nor outside Stackoverflow. In a nutshell, all events that I bind to my widgets are triggered initialy and don't respond to my actions.
In this example, the red rectangle appears on the canvas when I run the code, and
color=random.choice(['red', 'blue'])
revealed that the event binding doesn't work after that:
import Tkinter as tk
class application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.can = tk.Canvas(master, width=200, height=200)
self.can.bind('<Button-2>', self.draw())
self.can.grid()
def draw(self):
self.can.create_rectangle(50, 50, 100, 100, fill='red')
app = application()
app.mainloop()
I use a Mac platform, but I haven't got a clue about its role in the problem. Could anyone please point me at the mistake i did here?

There are two things here:
You should not be calling self.draw when you bind it to <Button-2>.
When you click <Button-2>, an event object will be sent to self.draw. Thus, you need to make your function accept this argument, even if it does not use it.
In all, your script should look like this (the lines I changed are in comment boxes):
import Tkinter as tk
class application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.can = tk.Canvas(master, width=200, height=200)
#######################################
self.can.bind('<Button-2>', self.draw)
#######################################
self.can.grid()
#######################
def draw(self, event):
#######################
self.can.create_rectangle(50, 50, 100, 100, fill='red')
app = application()
app.mainloop()

There are two mistakes in your code.
1.self.can.bind('<Button-2>', self.draw())
You should not make a method call. You should assign a method which should be called, when Button-2 is clicked
self.can.bind('<Button-2>', self.draw)
2.def draw(self)
Most of the times, an event object will be passed. You should change that line to
def draw(self, event).
In the future, once you are familiar with the basics, you will be using the event object and also learn about why it is getting passed.

Related

How to make pop-up window with force attention in Tkinter

I want to create a window which doesn't allow the user to access other windows until you give an input.
I tried win.attribute("ontop", True) but it allows the user to access other windows.
or is there any function like a force_focus_lock() in Tkinter python 3.8 which doesn't allow other window to
get focus until you give a input or the close present window.
I believe the below is what you are trying to do. Explanation is given in comments.
method #1: (PopOut1)
you can still move the main window
the new window assumes focus if there is a mouse release on main window
method #2: (PopOut2)
the main window is locked in place
the new window will assume focus, blink and "ding" if there is a mouse release on main window
import tkinter as tk
#first method
class PopOut1(tk.Toplevel):
def __init__(self, master, **kwargs):
tk.Toplevel.__init__(self, master, **kwargs)
self.geometry('400x300')
#set focus to this window
self.focus_set()
#releasing on any other tkinter window, within this process, forces focus back to this window
self.grab_set()
#second method
class PopOut2(tk.Toplevel):
def __init__(self, master, **kwargs):
tk.Toplevel.__init__(self, master, **kwargs)
self.geometry('400x300')
#set focus to this window
self.focus_set()
#disable the main window
master.attributes('-disabled', True)
#so this window can't end up behind the disabled window
#only necessary if this window is not transient
#self.attributes('-topmost', True)
#capture close event
self.protocol("WM_DELETE_WINDOW", self.close)
#event=None ~ in case you also want to bind this to something
def close(self, event=None):
#re-enable the main window
self.master.attributes('-disabled', False)
#destroy this window
self.destroy()
class App(tk.Tk):
TITLE = 'Application'
WIDTH, HEIGHT, X, Y = 800, 600, 50, 50
def __init__(self):
tk.Tk.__init__(self)
tk.Button(self, text="open popout 1", command=self.open1).grid()
tk.Button(self, text="open popout 2", command=self.open2).grid()
def open1(self):
PopOut1(self)
def open2(self):
#.transient(self) ~
# flash PopOut if focus is attempted on main
# automatically drawn above parent
# will not appear in taskbar
PopOut2(self).transient(self)
if __name__ == '__main__':
app = App()
app.title(App.TITLE)
app.geometry(f'{App.WIDTH}x{App.HEIGHT}+{App.X}+{App.Y}')
#app.resizable(width=False, height=False)
app.mainloop()

Simple frame with tk in python

I'm trying to write a code that prints a Frame to the screen with a Button and
Canvas in it
import tkinter as tk
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.entry = tk.Entry(self)
self.button = tk.Button(self, text="Get", command=self.on_button)
self.button.pack()
self.entry.pack()
self.text =tk.Text(height=20,width=10)
self.text.pack()
self.canvas=tk.Canvas(fill='Black')
self.canvas.pack()
def on_button(self):
print(self.entry.get())
app = SampleApp()
app.mainloop()
As soon as I run it, I get an error:
_tkinter.TclError: unknown option "-fill"
I have no idea why.
Fill is a create_rectangle argument, not a constructor argument:
self.canvas.create_rectangle(0, 0, width, height, fill = "black")
The canvas doesn't use a fill option to define what the background is; the error is coming out of the lower levels of the Tkinter code where it flips to the underlying Tcl/Tk runtime; option names get a hyphen put in front of them, and the error otherwise means what it says, “don't know what fill is in this context” (paraphrased).
However, the canvas does use a background option that takes a colour. Try:
self.canvas=tk.Canvas(background='Black')
You can also create rectangles on the canvas; those are fillable. The overall canvas isn't a rectangle, it's a widget.

adding image to tkinter gui

I am new to Tkinter. I want to create a GUI that can support embedding audio files and that also has a background image. I have tried endlessly to install pygame to no avail. I cannot seem to figure out why it is not installing correctly so at this point I am just trying to find the easiest way possible to have these two options. Below is my attempt at displaying a background image using a canvas widget. However, I always get an error that my variables are not defined. I would really appreciate some feedback on what I am doing wrong, as well as any helpful tkinter tutorials that involve more than just the basics. Thanks in advance
from Tkinter import *
root = Tk()
root.geometry("500x500")
class Application(Frame):
def __init__(self, master):
#initialize the frame
Frame.__init__(self, master)
self.grid()
self.createWidgets()
def createWidgets(self):
self.can = Canvas(root, width=160, height=160, bg='white')
self.pic = PhotoImage(file='speaker.gif')
self.item = can.create_image(80, 80, image=pic)
app = Application(root)
#kick off event loop
root.mainloop()
Every time you want to use an attribute of a class inside one of its methods, you need to prefix it with self.:
self.item = self.can.create_image(80, 80, image=self.pic)
# ^^^^^ ^^^^^
Otherwise, Python will treat the names as being local to the function and will raise an exception when it fails to find them.
Also, you forgot to call grid on your canvas widget:
self.can = Canvas(root, width=160, height=160, bg='white')
self.can.grid(...)
As for resources on Tkinter, you can check out these:
http://www.tkdocs.com/tutorial/onepage.html
http://effbot.org/tkinterbook/

How do you validate a window in Tkinter?

def createWidgets(self):
self.INSTRUCTIONS = Button(self) #creating button linked to instructions_window
self.INSTRUCTIONS["text"] = "Instructions"
self.INSTRUCTIONS["fg"] = "green"
self.INSTRUCTIONS["command"] = self.instruction_window #command which opens instructions_window
self.INSTRUCTIONS.pack({"side": "left"})
Currently, if I press the button multiple times then the instructions window will open multiple times. How do I ensure that when the button is pressed, if the window is already open then it will flash to show that the same window can't be opened. Is there a command? Or do I need to use a validation of some sort?
Here's a great article dealing with more complicated examples of dialog boxes.
Essentially what you are looking for is almost like a modal dialog window except it seems with the additional ability to still interact with the parent window to a degree. At this point it may be worth considering making it totally modal, but I do not know your use case. If not, you can definitely adapt the scripts given on the tutorial website to fit your needs.
The way I do this is to create a function that will create the window if it doesn't exist, and then display the window. Personally I don't think there's a need to flash the window, but you could do that if you want. Tkinter doesn't know how to flash a window, but you can do something simple like changing the colors briefly.
Here's an example:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.instruction_window = None
self.instructions = tk.Button(self, text="Instructions", foreground="green",
command=self.show_instructions)
self.instructions.pack(side="left")
def show_instructions(self):
'''show the instruction window; create it if it doesn't exist'''
if self.instruction_window is None or not self.instruction_window.winfo_exists():
self.instruction_window = InstructionWindow(self)
else:
self.instruction_window.flash()
class InstructionWindow(tk.Toplevel):
'''A simple instruction window'''
def __init__(self, parent):
tk.Toplevel.__init__(self, parent)
self.text = tk.Text(self, width=40, height=8)
self.text.pack(side="top", fill="both", expand=True)
self.text.insert("end", "these are the instructions")
def flash(self):
'''make the window visible, and make it flash temporarily'''
# make sure the window is visible, in case it got hidden
self.lift()
self.deiconify()
# blink the colors
self.after(100, lambda: self.text.configure(bg="black", fg="white"))
self.after(500, lambda: self.text.configure(bg="white", fg="black"))
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()

How to use TkInter fill or expand functions with self?

I have a very simple program with TkInter in Python.
How to I use the "fill" or "expand" options with the following code?
from Tkinter import *
class Application(Frame):
def sayhello(self):
print "Hello!"
def createWidgets(self):
self.var = Button(self, text="Hello", command = self.sayhello)
self.var.pack(side=LEFT)
self.QUIT = Button(self, text="QUIT", fg="red", command = self.quit)
self.QUIT.pack(side=LEFT)
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.createWidgets()
root = Tk()
app = Application(master=root)
app.mainloop()
root.destroy()
Note: I have to use the self.var.pack() format so it can do a "command" when it's pressed...unless anyone has a better way?
To make an object fill it's container in the y axis, you would use the parameter fill="y" (or fill=Y if you import Y from Tkinter).
Note that this only controls how the widget fills its container. In your code, this makes the button fill the inner frame, but because your inner frame doesn't fill the main window in the y axis, you might not get the visual effect you expect.
Also, specifically in the case of buttons on the Macintosh, the button won't grow to fill the space. On OSX, buttons are native widgets which can't grow in height.

Categories

Resources