Entry widget won't clear upon function - python-3.x

I'm trying to get the Entry to widget to clear (clearTextBox()) but also run newQuote(). since I added the second bind it won't clear out. Any ideas?
from random import *
from tkinter import *
def clearTextBox(event):
textBox.delete(0, END)
textBox.insert(0, "")
def newQuote(event):
rightLabel.config(text=quotes[randint(0,10)])
textBox = Entry(leftFrame, width=60)
textBox.pack(ipady=10, side=LEFT)
textBox.bind("<Return>", clearTextBox)
textBox.bind("<Return>", newQuote)
root.mainloop()

Your second call to bind overwrites the first.
To fix this, you can add the keyword argument add="+", to call both functions:
textBox.bind("<Return>", newQuote, add="+")

Related

I am not able to grab the date from the DateEntry using tkinter python

from tkinter import *
from tkinter.ttk import Combobox
from tkinter import messagebox
from tkinter import filedialog
from tkcalendar import *
root = Tk()
root.geometry('500x500')
def Date_func():
print(startdateEntry.get())
print(enddateEntry.get())
middle_frame=Frame(root,bg='#80c1ff',bd=10)
middle_frame.place(relx=0.5,rely=0.25,relwidth=0.75,relheight=0.2,anchor='n')
#Date and Time Entry boxes and buttons
StartHR=StringVar()
EndHR=StringVar()
StartMN=StringVar()
EndMN=StringVar()
StartDate=StringVar()
EndDate=StringVar()
StartHR.set('HH')
EndHR.set('HH')
StartMN.set('MM')
EndMN.set('MM')
starthourEntry=Entry(middle_frame,text=StartHR,width=5).grid(row=0,column=0)
endhourEntry=Entry(middle_frame,text=EndHR,width=5).grid(row=1,column=0)
startminuteEntry=Entry(middle_frame,text=StartMN,width=5).grid(row=0,column=1)
endminuteEntry=Entry(middle_frame,text=EndMN,width=5).grid(row=1,column=1)
startdateEntry = DateEntry(middle_frame,width=30,bg="darkblue",fg="white",year=2020).grid(row=0,column=2)
enddateEntry = DateEntry(middle_frame,width=30,bg="darkblue",fg="white",year=2020).grid(row=1,column=2,padx=30,pady=10)
selectStartDate_button=Button(middle_frame,text='StartDate',command=Date_func).grid(row=0,column=3)
selectEndDate_button=Button(middle_frame,text='EndDate',command=Date_func).grid(row=1,column=3)
root.mainloop()
I want to capture the date selected in the DateEntry but failed to do so,I am getting "
AttributeError: 'NoneType' object has no attribute 'get'"
I tried DateEntry.get_date as well.
The problem is that you are saying DateEntry(...).grid(...) in the same line, which returns None, it is not recommened to not do so and is a bad practice. So to fix it simply say it in two different lines, like:
startdateEntry = DateEntry(middle_frame,width=30,bg="darkblue",fg="white",year=2020)
startdateEntry.grid(row=0,column=2)
This is recommended to be done for all similar widgets on which you want to use attributes like get(), insert(), cget() and so on..
Then to get selection from this, I think you have to say startdateEntry.selection_get() instead of using the get() method.
Also as a side tip :-
Entry widget does not have text argument, unlike Button or Label, perhaps you meant textvariable?
Hope it cleared your doubts, do let me know if any doubts.
Cheers
I removed the .grid() to next line and get the work done.

Coding a general function to: Disable a tkinter widget with a checkbutton

I'm trying to write a function to disable a widget in a tkinter program depending on the value of a checkbutton. I want this function to be general: That is, I can pass it the widget and associated check variable and it will disable the widget (if the variable is checked the right way).
Here is an abstracted version of my code
import tkinter
class App:
def __init__(self,root):
widg = tkinter.Scale(root,from_=0,to=100)
checkvar = tkinter.IntVar()
checker = tkinter.Checkbutton(root,variable=checkvar,command=self.check(var,checkvar))
widg.grid()
checker.grid()
widg.configure(state=tkinter.DISABLED)
widg.configure(state=tkinter.NORMAL)
def check(self,widget,var):
if var.get()==1:
widget.configure(state=tkinter.DISABLED)
elif var.get()==0:
widget.configure(state=tkinter.NORMAL)
m = tkinter.Tk()
f=App(m)
It is intended to function such that clicking the checkbutton triggers the callback - check - with the parameters of the widget and the check variable. Then it will evaluate whether the widget should be on or off and change its state accordingly. There are no errors but the state doesn't change. What am I missing here?
Thanks
The command argument simply takes the uncalled function so passing arguments to it takes some workaround.
So it expects self.check rather than self.check() since the widget will call the function later.
I've found that using partial is a workaround for passing the arguments.
import tkinter
from functools import partial
class App:
def __init__(self,root):
widg = tkinter.Scale(root,from_=0,to=100)
checkvar = tkinter.IntVar()
checker = tkinter.Checkbutton(root,variable=checkvar,command=partial(self.check, widg, checkvar))
widg.grid()
checker.grid()
widg.configure(state=tkinter.DISABLED)
widg.configure(state=tkinter.NORMAL)
def check(self,widget,var):
if var.get()==1:
widget.configure(state=tkinter.DISABLED)
elif var.get()==0:
widget.configure(state=tkinter.NORMAL)
m = tkinter.Tk()
f=App(m)
m.mainloop()
Credit to my friend JB for helping me with this.
As user Axe319 suggested, the problem is just that tkinter doesn't expect to pass any parameters. It is possible to work around this by using a lambda function. The line that defines the checker variable, in my original post line 8, can be rewritten as such:
self.checker = tkinter.Checkbutton(root,variable=checkvar,command=lambda:self.check(self.widg,checkvar))
and it will work as intended.

Tkinter - How to trace expanding list of variables

What I am trying to do track when any values in a list of StringVar change, even when the list is expanding. Any additions to the list before the trace statement will result in the callback. But any additions afterward, such as when pressing a button, will not cause any callback.
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root)
frame.grid(row=0)
L = []
def add_entry(event):
L.append(tk.StringVar())
tk.Entry(frame,textvariable=L[len(L)-1]).grid(row=len(L),padx=(10,10),pady=(5,5))
add = tk.Button(frame,text='add Entry',command='buttonpressed')
add.grid(row=0)
add.bind('<Button-1>',add_entry)
for i in range(2):
L.append(tk.StringVar())
tk.Entry(frame,textvariable=L[len(L)-1]).grid(row=len(L),padx=(10,10),pady=(5,5))
for i in L:
i.trace('w',lambda *arg:print('Modified'))
root.mainloop()
Modifying the first two Entry's prints out Modified, but any Entry's after the trace is run, such as the ones produced when a button is pressed, will not.
How do I make it so that trace method will run the callback for the entire list of variables even if the list is expanded?
Simple suggestion, change your add_entry function to something like this:
def add_entry(event):
L.append(tk.StringVar())
tk.Entry(frame,textvariable=L[len(L)-1]).grid(row=len(L),padx=(10,10),pady=(5,5))
L[len(L)-1].trace('w',lambda *arg:print('Modified'))
Extra suggestions:
This add = tk.Button(frame,text='add Entry',command='buttonpressed') is assigning a string to command option, means it will try to execute that string when button is clicked(which will do nothing). Instead, you can assign your function add_entry to command option and it will call that function when button is clicked and you can avoid binding Mouse Button1 click to your Button(Note: No need to use argument event in function when using like this). Read more here
Python supports negative indexing of List, so you can call L[-1] to retrieve the last element in the list instead of calling L[len(L)-1]).
Once you change your add_entry function as suggested, you can reduce your code to
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root)
frame.grid(row=0)
L = []
def add_entry():
global L
L.append(tk.StringVar())
tk.Entry(frame,textvariable=L[-1]).grid(row=len(L),padx=(10,10),pady=(5,5))
L[-1].trace('w',lambda *arg:print('Modified'))
add = tk.Button(frame,text='add Entry',command=add_entry)
add.grid(row=0)
for i in range(2):
add_entry()
root.mainloop()

tkinter function repeats itself twice when ttk widgets are engaged

The program works as intended when I simply use tkinter's widgets. When I use ttk's widgets the program repeats itself twice. I tried almost everything in my knowledge to fix this, I believe that *args have something to do with it. Is there anyway to prevent my function _up_options from running twice?
from tkinter import *
from tkinter import ttk
root = Tk()
first = StringVar(root)
second = StringVar(root)
Ore = {'Options': [''], 'Yes': ['One'], 'No': ['Two']}
entry1 = ttk.OptionMenu(root, first, *Ore.keys())
entry2 = ttk.OptionMenu(root, second, '')
entry1.pack()
entry2.pack()
def _up_options(*args):
print('update_options')
ores = Ore[first.get()]
second.set(ores[0])
menu = entry2['menu']
menu.delete(0, 'end')
for line in ores:
print('for')
menu.add_command(label=line, command=lambda choice=line: second.set(choice))
first.trace('w', _up_options)
root.mainloop()
PS, I used *args in my function to work. If anyone can explain this, I would be very grateful
I think I figured this out. The problem is that the variable actually is set twice by the ttk OptionMenu.
Take a look at this piece of code from the tkinter OptionMenu:
for v in values:
menu.add_command(label=v, command=_setit(variable, v, callback))
This adds a button to the menu for each value, with a _setit command. When the _setit is called it sets the variable and another callback if provided:
def __call__(self, *args):
self.__var.set(self.__value)
if self.__callback:
self.__callback(self.__value, *args)
Now look at this piece of code from the ttk OptionMenu:
for val in values:
menu.add_radiobutton(label=val,
command=tkinter._setit(self._variable, val, self._callback),
variable=self._variable)
Instead of a command this adds a radiobutton to the menu. All radiobuttons are "grouped" by linking them to the same variable. Because the radiobuttons have a variable, when one of them is clicked, the variable is set to the value of the button. Next to this, the same command is added as in the tkinter OptionMenu. As said, this sets the variable and then fires another command of provided. As you can see, now the variable is updated twice, once because it is linked to the radiobutton and once more because it is set in the _setit function. Because you trace the changing of the variable and the variable is set twice, your code also runs twice.
Because the variable is set twice from within the ttk code, I guess there's not much you can do about that. If you don't change the variable from any other part of your code than from the OptionMenu though, you could choose to not trace the variable, but instead add your function as command to the OptionMenu:
entry1 = ttk.OptionMenu(root, first, *Ore.keys(), command=_up_options)
P.S. this was introduced with this commit after this bugreport.
I guess when adding the variable=self._variable the command should have been changed to just command=self._callback.
You can understand the problem in the error message:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\user\AppData\Local\Programs\Python\Python36\lib\tkinter__init__.py", line 1699, in call
return self.func(*args)
TypeError: _up_options() takes 0 positional arguments but 3 were given
Initially, you don't use _up_options When you change the Options you call _up_options to trace the first StringVar and change it to the value of the next object in the dictionary.
Now when you do that you are running on all the objects in the dictionary, therefore, you need the *args so the lambda function will run on all args given!
As for your problem:
When I use ttk's widgets the program repeats itself twice.
EDIT
See #fhdrsdg's answer!
The solution is just to change command=tkinter._setit(self._variable, val, self._callback) to command=self._callback.
Hope you find this helpful!
Instead of tracing the StringVar, add a callback as command argument for OptionMenu constructor.
I created a subclass of ttk.OptionMenu to solve this (as well as to provide slightly simpler usage of the widget and a more useful callback). I think this is a more stable approach than modifying the original class directly or just overriding the original method because it guarantees compatibility with potential changes to the built-in/original widget in future Tkinter versions.
class Dropdown( ttk.OptionMenu ):
def __init__( self, parent, options, default='', variable=None, command=None, **kwargs ):
self.command = command
if not default:
default = options[0]
if not variable:
variable = Tk.StringVar()
if command:
assert callable( command ), 'The given command is not callable! {}'.format( command )
ttk.OptionMenu.__init__( self, parent, variable, default, *options, command=self.callBack, **kwargs )
else:
ttk.OptionMenu.__init__( self, parent, variable, default, *options, **kwargs )
def callBack( self, newValue ):
self.command( self, newValue )
You can then use it like this:
def callback( widget, newValue ):
print 'callback called with', newValue
print 'called by', widget
options = [ 'One', 'Two', 'Three' ]
dropdown = Dropdown( parent, options, command=callback )
dropdown.pack()
Besides avoiding the double-trace issue, other notable differences from the original ttk.OptionMenu includes not needing to supply a Tkinter variable or default value if you don't need them (the default item will be the first item in the options list if not provided), and being able to get the widget that called the callback function when it fires. The latter is very helpful if you have many dropdown widgets sharing the same callback and you need to know which one is being used within the call.
Soon after writing this, I also found another solution using lambda: Passing OptionMenu into a callback (or retrieving a reference to the used widget)
I thought I might still share this Dropdown widget anyway since it can make the higher-level code simpler, and it provides a good base if you have some other custom methods to add in.

Having arrow keys send clicks when using QListWidget

I've worked through the excellent Matplotlib GUI tutorial found at: http://blog.rcnelson.com/building-a-matplotlib-gui-with-qt-designer-part-1/. This program uses a QListWidget to select plots to show. Everything works correctly but I have one additional need. Once a item in the list is selected you can select the next or previous item with the arrow keys. The next or previous item is highlighted. What I want is a means to trigger the same event that is triggered by clicking. The click event is handled by the following code:
self.mplfigs.itemClicked.connect(self.changefig)
I've tried the following and neither works:
self.mplfigs.itemEntered.connect(self.changefig)
self.mplfigs.currentRowChanged.connect(self.changefig)
Much Google searching hasn't helped so any hints are very welcome.
You probably need to use itemSelectionChanged signal, in your case self.mplfigs.itemSelectionChanged.connect(self.changefig) should trigger the function, I don't have the full code but that should work and please take look here
Adding a minimal working example:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
class myListWidget(QListWidget):
def Clicked(self,item=None):
if not item:
item = self.currentItem()
QMessageBox.information(self, "ListWidget", "You clicked: "+item.text())
def main():
app = QApplication(sys.argv)
listWidget = myListWidget()
#Resize width and height
listWidget.resize(300,120)
listWidget.addItem("Item 1");
listWidget.addItem("Item 2");
listWidget.addItem("Item 3");
listWidget.addItem("Item 4");
listWidget.setWindowTitle('PyQT QListwidget Demo')
listWidget.itemSelectionChanged.connect(listWidget.Clicked)
listWidget.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Original code is take from here
#Achayan got me very close. Here is what solved the problem. Using the following line is as was suggested:
self.mplfigs.itemSelectionChanged.connect(self.changefig)
I needed to change changefig from:
def changefig(self, item):
text = item.text()
self.rmmpl()
self.addmpl(self.fig_dict[text])
To:
def changefig(self, item=None):
if not item:
item = self.mplfigs.currentItem()
text = item.text()
self.rmmpl()
self.addmpl(self.fig_dict[text])
Unlike itemClicked, itemSelectionChanged doesn't emit the item so the extra if statement was needed to get the particular item necessary within changefig.
However, the following line of code seems to work without modifying changefig.
self.mplfigs.currentItemChanged.connect(self.changefig)
Evidently currentItemChanged emits the item like itemClicked does.

Resources