Hi I am using Python27 on Window7 OS, i am trying to create a Tk GUI with button, when the button is press a file directory will appear. But the following code won't do anything. Did i miss out something?
import webbrowser
import Tkinter as Tk
def action(self):
webbrowser.open ('C:\AgmPlots')
win = Tk.Toplevel()
frame = Tk.Frame(master=win).grid(row=1, column=1)
button = Tk.Button(master=frame, text='press', command= lambda: action())
You've got three big problems.
First, you never start the GUI. You need something like win.mainloop() at the end to actually do anything.
Second, your button isn't actually laid out within the frame, so you won't see it. You need something like button.pack().
Finally, your command is a function that calls action(), with no arguments. But you've defined it to require a parameter. So, all that will happen when you click it is that Tk will log a traceback that looks like this:
Exception in Tkinter callback
Traceback (most recent call last):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1470, in __call__
return self.func(*args)
File "tkt.py", line 8, in <lambda>
button = Tk.Button(master=frame, text='press', command= lambda: action())
TypeError: action() takes exactly 1 argument (0 given)
To fix that, either don't add the unnecessary self parameter to action (this is a function, not a method), or explicitly pass some dummy to match it in your lambda.
While we're at it, lambda: action() does exactly the same thing as action itself, except more verbose, harder to read, and slower. You should never use unescaped backslashes in non-raw string literals. And we might as well remove the stray spaces and PEP8-ify everything to make it consistent.
So, putting it all together:
import webbrowser
import Tkinter as Tk
def action():
webbrowser.open(r'C:\AgmPlots')
win = Tk.Toplevel()
frame = Tk.Frame(master=win).grid(row=1, column=1)
button = Tk.Button(master=frame, text='press', command=action)
button.pack()
win.mainloop()
Related
I am trying to create a popup menu that opens only when right-clicked inside of certain widgets (Text and Entry, in this case) but nowhere else
inside the root window.
When a user right-clicks inside one of the widgets and selects "copy", the text selection inside that widget
should be copied to the clipboard.
As is, the code below only works when explicitly referring to a certain widget but I want to generalize the copyToClipboard function
to copy the text selection from the widget that the user right-clicked inside.
Instead, running the commented out lines from the code below gives the following error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\...\...\lib\tkinter\__init__.py", line 1702, in __call__
return self.func(*args)
TypeError: <lambda>() missing 1 required positional argument: 'e'
How do I access the appropriate (right-clicked) widget within the copyToClipboard function?
def copyToClipboard():
#def copyToClipboard(event):
#value = event.widget.get(SEL_FIRST,SEL_LAST)
value = inputText.get(SEL_FIRST,SEL_LAST)
pyperclip.copy(value)
print(value)
def showMenu(event):
popup.post(event.x_root, event.y_root)
inputEntry = ttk.Entry(root)
inputText = Text(root)
popup = Menu(root)
popup.add_command(label="Copy", command=copyToClipboard)
#popup.add_command(label="Copy", command=lambda e: copyToClipboard(e))
inputText.bind("<Button-3>", showMenu)
inputEntry.bind("<Button-3>", showMenu)
inputText.pack()
inputEntry.pack()
mainloop()
I've added a solution below. Storing event.widget as a global variable seemed to help as per acw's suggestion. I got rid of pyperclip because it kept giving me chinese chars and other random chars when mixing clicking-to-copy with Ctrl-V.
EDIT: It is worth noting that the Entry widget doesn't seem to handle line breaks well when they are pasted with Ctrl-V into the entry widget. Unfortunately, I haven't found an effective way to override the hotkey's default paste command to remove the line breaks prior to pasting.
from tkinter import *
from tkinter import ttk
root = Tk()
def copyToClipboard():
val = clickedWidget.selection_get()
root.clipboard_clear()
root.clipboard_append(val)
def showMenu(event):
global clickedWidget
clickedWidget = event.widget
popup.post(event.x_root, event.y_root)
inputEntry = ttk.Entry(root)
inputText = Text(root)
popup = Menu(root)
popup.add_command(label="Copy", command=copyToClipboard)
inputText.bind("<Button-3>", showMenu)
inputEntry.bind("<Button-3>", showMenu)
inputText.pack()
inputEntry.pack()
mainloop()
I am trying to write tkinter code that calls a function either when a button is clicked or when enter is clicked when the button is in focus. I have the following code:
import tkinter as tk
m = tk.Tk(className="My window")
def callback():
print("Ice cream weather")
butt = tk.Button(m, text="My button", width=25, command=callback)
butt.grid()
butt.focus_set()
butt.bind('<Return>', callback)
m.mainloop()
But when I run it and press Enter, I get the following error:
Traceback (most recent call last):
File "C:\Users\chiller\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
TypeError: callback() takes 0 positional arguments but 1 was given
I found a solution, which I posted below, but I am wondering if there is a simpler or neater fix.
A way to solve this is to set the function up to take in a dummy parameter, and set a default value for when it isn't passed (i.e. for button click, which passes 0 arguments). This is just changing the line
def callback():
To
def callback(pointless=None):
This gives the extra parameter from pressing Enter a place to go, while not requiring it for a button push.
I would love some help, thanks!
I am trying to use lambdas to assign functionality to window events. It already worked for assigning the "enter" button to a function.
But, for some reason, it doesn't work for the default exit button on the window.
As you can see in the create_entry_window function, I used a lambda twice, and it worked only for the "Return".
The problem occurs with this line:
root.protocol("WM_DELETE_WINDOW", (lambda event: exit_entry(root)))
Here is the code:
from tkinter import Tk, Frame, BOTTOM, Entry, Text, END
def clear_and_get_entry(root, entry):
"""
"""
global entered_text
entry_text = entry.get()
entry.delete(0, END)
entry.insert(0, "")
entered_text = entry_text
root.destroy()
def exit_entry(root):
"""
"""
global entered_text
entered_text = False
print "here at exit"
root.destroy()
def create_entry_window(text):
"""
"""
root = Tk()
root.title("One picture is worth a thousand sounds!")
root.geometry("500x200")
root.resizable(width=False, height=False)
bottom_frame = Frame(root)
bottom_frame.pack(side=BOTTOM)
entry = Entry(root)
entry.pack(side=BOTTOM)
description_text = Text(root, height=50, width=100)
description_text.insert(END, text)
description_text.tag_configure("center", justify='center')
description_text.tag_add("center", "1.0", "end")
description_text.pack()
root.protocol("WM_DELETE_WINDOW", (lambda event: exit_entry(root)))
entry.bind("<Return>", (lambda event: clear_and_get_entry(root, entry)))
return root
if __name__ == '__main__':
root = create_entry_window("Some text")
root.mainloop()
When trying to exit the window, I get this error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Heights\PortableApps\PortablePython2.7.6.1\App\lib\lib-tk\Tkinter.py", line 1470, in __call__
return self.func(*args)
TypeError: <lambda>() takes exactly 1 argument (0 given)
The quick-fix to your problem is quite easy:
Change the line reading root.protocol("WM_DELETE_WINDOW", (lambda event: exit_entry(root))) into
root.protocol("WM_DELETE_WINDOW", lambda: exit_entry(root))
The root.protocol does not provide an argument when triggered. But your lambda expects one. Removing the argument event fixes the problem for me.
Please note that there are some other issues with your code.
You are still using Python 2. Unless there are very compelling reasons to do so, I suggest you move forward to Python 3. I had not problems running your code with Python 3. All I needed to do was to add brackets to the print statement:
print "here at exit" -> print("here at exit")
Secondly: You are using global in order for your functions to communicate with each other. This is considered bad-practice. It leads to confusing code that is very hard to debug. I suggest that you have a closer look at some tkinter examples and how they are using an object oriented approach to deal with this issue. A possible starting point could be Introduction_to_GUI_Programming. The Calculator class looks like a good starting-point to me.
I am working with Tkinter in Python 3.5 and I encounter a weird problem.
I used the tkinterbook about events and bindings to write this simple example:
from tkinter import *
root = Tk()
frame = Frame(root, width=100, height=100)
def callback(event):
print("clicked at", event.x, event.y)
# frame.unbind("<Button-1>", callback)
frame.bind("<Button-1>", callback)
frame.pack()
root.mainloop()
It works fine, but if I try to unbind the callback (just uncomment the line), it fails with the following error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Delgan\AppData\Local\Programs\Python\Python35\lib\tkinter\__init__.py", line 1549, in __call__
return self.func(*args)
File "C:\Users\Delgan\Desktop\Test\test.py", line 9, in callback
frame.unbind("<Button-1>", callback)
File "C:\Users\Delgan\AppData\Local\Programs\Python\Python35\lib\tkinter\__init__.py", line 1105, in unbind
self.deletecommand(funcid)
File "C:\Users\Delgan\AppData\Local\Programs\Python\Python35\lib\tkinter\__init__.py", line 441, in deletecommand
self.tk.deletecommand(name)
TypeError: deletecommand() argument must be str, not function
This is not clear, I am not sure if it is a bug in tkinter or if I am doing something wrong.
frame.unbind("<Button-1>") works fine but I would like to remove this exact callback instead of a global remove.
The second argument to unbind is a 'funcid', not a function. help(root.unbind) returns
unbind(sequence, funcid=None) method of tkinter.Tk instance.
Unbind for this widget for event SEQUENCE the function identified with FUNCID.
Many tk functions return tk object ids that can be arguments for other funtions, and bind is one of them.
>>> i = root.bind('<Button-1>', int)
>>> i
'1733092354312int'
>>> root.unbind('<Button-1>', i) # Specific binding removed.
Buried in the output from help(root.bind) is this: "Bind will return an identifier to allow deletion of the bound function with unbind without memory leak."
from Tkinter import *
import webbrowser
root = Tk()
frame = Frame(root)
frame.pack()
url = 'http://www.sampleurl.com'
def OpenUrl(url):
webbrowser.open_new(url)
button = Button(frame, text="CLICK", command=OpenUrl(url))
button.pack()
root.mainloop()
My goal is to open a URL when I click the button in the GUI widget. However, I am not sure how to do this.
Python opens two new windows when I run the script without clicking
anything. Additionally, nothing happens when I click the button.
You should use
button = Button(root, text="CLCK", command=lambda aurl=url:OpenUrl(aurl))
this is the correct way of sending a callback when arguments are required.
From here:
A common beginner’s mistake is to call the callback function when
constructing the widget. That is, instead of giving just the
function’s name (e.g. “callback”), the programmer adds parentheses and
argument values to the function:
If you do this, Python will call the callback function before creating
the widget, and pass the function’s return value to Tkinter. Tkinter
then attempts to convert the return value to a string, and tells Tk to
call a function with that name when the button is activated. This is
probably not what you wanted.
For simple cases like this, you can use a lambda expression as a link
between Tkinter and the callback function:
Alternatively, you don't have to pass the URL as an argument of the command. Obviously your OpenUrl method would be stuck opening that one URL in this case, but it would work.
from Tkinter import *
import webbrowser
url = 'http://www.sampleurl.com'
root = Tk()
frame = Frame(root)
frame.pack()
def OpenUrl():
webbrowser.open_new(url)
button = Button(frame, text="CLICK", command=OpenUrl)
button.pack()
root.mainloop()