I made an tkinter window for some kind of calculation from the data entered. I simplified my code below to illustrate my question. Currently, the result will show up once I click search. However, I want the result to show up automatically once text are entered in the entry box without the "search" button. I tried "after" using window.after(100, self.searchbarcode) but it did not work for me. Appreciate your inputs. Thank you!
from tkinter import *
import pandas as pd
import tkinter as tk
import os.path
import numpy as np
class searchloc:
def __init__(self):
window=tk.Tk()
window.geometry("800x300")
window.title("Search Location")
Label(window, text="Scan",font="Helvetica 24").grid(row=1,column=1,sticky=W)
self.barcode=StringVar()
self.outcomes=StringVar()
self.text1=tk.Entry(window,textvariable=self.barcode,font="Helvetica 36")
self.text1.grid(row=1,column=2,padx=(0,5))
Label(window,textvariable=self.outcomes,font="Helvetica 68 bold").grid(row=7,column=2,sticky=E)
wsheet1 = gsheet.worksheet("Sheet2")
mydata1 = wsheet1.get_values()
mydata2=mydata1[1:]
cool=mydata1[0]
look = pd.DataFrame(mydata2, dtype=str)
#window.after(1, self.searchbarcode())
#self.text1.bind('<Enter>', self.searchbarcode())
Button(window,text='search',command=self.searchbarcode,font="Helvetica 38").grid(row=5,column=2,padx=(100,5),pady=5,sticky=W)
#Button(window,text='clear',command=self.clear_text,font="Helvetica 38").grid(row=5,column=2,padx=(100,5),pady=5,sticky=E)
window.mainloop()
#def clear_text(self):
def searchbarcode(self):
bar = self.barcode.get()
outtt=bar[1:5]
self.outcomes.set(outtt)
self.text1.delete(0, 'end')
searchloc()
If you want to happen when you press the return key, you almost had it right. The function has to accept an event parameter even if you don't use it, and you need to make sure you pass the function itself, not the result of calling the function (ie: self.searchbarcode instead of self.searchbarcode()).
Also, the event is <Return>. <Enter> is for when the mouse enters the widget.
def __init__(self):
...
self.text1.bind('<Return>', self.searchbarcode)
...
def searchbarcode(self, event):
...
If you want to call searchbarcode both with or without the event parameter, give it a default value of None:
def searchbarcode(self, event=None):
...
Related
I have two buttons on my interface. I want both of them to be able to call their respective functions when I either click on them or a hit the Enter Key.
The problem I'm having is that only the last button in the traveral focus gets activated when I hit the Enter Key, even if the preceeding one has the focus. What can I do to resolve this problem.
Useful answer are welcome and appreciated.
This is the problem in question:
from tkinter import *
w = Tk()
def startProgram(event = None):
print('Program Starting')
def readyContent(event = None):
print('Content being prepared')
# Buttons
Button(text='Prepare', command=readyContent).grid(row=10,column=2)
w.bind('<Return>',readyContent) # Binds the Return key to a Function
Button(text='Start', command=startProgram).grid(row=10,column=3)
w.bind('<Return>',startProgram) # Binds the Return key to a Function
w.mainloop()
When you click on the Prepare or Start button, in return you get either Content being prepared or Program Starting repectively. Nothing like that happens when you use the Tab Key to give focus to one button or the other. Even if the focus is on the Prepare button, when you hit Enter you get: Program Starting
This is the solution to my problem.
I hope it helps anyone else having the same problem as me.
from tkinter import *
w = Tk()
def startProgram(event = None):
print('Program Starting')
def readyContent(event = None):
print('Content being prepared')
# Buttons
btn1 = Button(text='Prepare', command=readyContent)
btn1.grid(row=10,column=2)
btn1.bind('<Return>',readyContent) # Binds the Return key to a Function
btn2 = Button(text='Start', command=startProgram)
btn2.grid(row=10,column=3)
btn2.bind('<Return>',startProgram) # Binds the Return key to a Function
w.mainloop()
Have a good day! :)
I have a list named chosenTestHolder (imported from the my_config file) that consists of several objects each with the attribute 'sentence'.
When pressing the button 'Press' for the first time, the attribute 'sentence' of the first object in the chosenTestHolder should be displayed in the text widget. The next time the button 'Press' is pressed the attribute 'sentence' of the second object in chosenTestHolder should be displayed and so on.
I am using lambda event for binding the 'Press' button and tries to use a new sentences as its first arguments after each pressing of the 'Press' button. However, it keeps showing the first sentence.
When searching Stackoverflow I have seen in
Using lambda function to change value of an attribute that you can't use assignments in lambda expressions but by reading that I still have not figured out how to solve my problem.
Grateful for help! Code is below!
main.py
from tkinter import font
import tkinter as tk
import tkinter.ttk as ttk
import my_config
import Testlist as tl
class TestWidgetTest:
def __init__(self):
ram = tk.Frame(root)
ram.grid(in_=root,row=0, column=0)
self.myText = tk.Text(ram, height = 5)
self.myText.grid(row=0,column=1)
my_config.counter = 0
self.myButton = tk.Button(ram, text = 'Press')
self.myButton.grid(row =1, column =0, columnspan =2)
indata =[my_config.chosenTestHolder[my_config.counter] , self.myText]
self.myButton.bind('<ButtonRelease-1>',lambda event, arg=indata : self.TagConfigure(event, arg))
def TagConfigure(self, event, arg):
arg[1].delete('1.0',tk.END)
arg[1].insert('1.0',arg[0].sentence)
my_config.counter += 1
root = tk.Tk()
TestWidgetTest()
root.mainloop()
my_config.py
import Testlist as tl
testListHolder = [ ['Fabian was very tired'],
['Thomas light the fire'],
['Anna eat a red apple ']]
chosenTestHolder = []
count = 0
while count <(len(testListHolder)):
chosenTestHolder.append(tl.Testlist(testListHolder[count][0]))
count += 1
counter = 0
Testlist.py
class Testlist:
def __init__(self, sentence):
self.sentence = sentence
Your issue is the assignment of indata.
You do only assign in init.
To get your code working you need to re-configure your sentecte...
indata =[my_config.chosenTestHolder[my_config.counter] , self.myText]
self.myButton.bind('<ButtonRelease-1>',lambda event, arg=indata : self.TagConfigure(event, arg))
I would advise to keep track of the current sentence as an instance variable.
class Test_widget(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, args, kwargs)
self.sentences=["a", "b", "c", "d"] # the data
self.show = tk.StringVar() # the current displayed data
self.show.set("NULL")
self.counter=0 # the indexer
tk.Label(self, textvariable=self.show).grid(row=0)
tk.Button(self, command=self.click).grid(row=1)
def click(self, event):
self.show.set("%s"%self.sentences[self.counter]) # use the indexer to access the data
self.counter = self.counter + 1 # modify the indexer
if self.counter = len(self.sentences): # make sure you dont run in index-err
self.counter = 0
As you see, there is no need at all for the lambdas.
Edit
As to your questions:
The change in your original code was not intended.
I do not see a use case where you can use a lambda for its use inside your code.
At least none where a lambda is necessary.
Please remember to use lambda only and exclusively if there are
no ( == NULL ) other options.
Using inheritance (thats what the mechanism is called), you can inherit functions, "default" behaviour from other classes. It is a common mechanism in programming and not exclusive to python.
It is used like any normal object except you have to call the constructor of the base class (what I do using tk.Frame.__init__(self, args, kwargs) inside the init method. For more information on inheritance please refer to the uncounted manuals and tutorials available for that topic (google is your friend now that you know what the mechanism is called).
I have created a custom Tkinter Button widget which inherits from the standard Button, purely for aesthetic purposes so that I can repeat the same style throughout all my program without have to configure it manually every time. The code for my custom Button is here:
import tkinter as tk
class gameButton(tk.Button):
def __init__(self,*args,**kwargs):
super(gameButton,self).__init__()
self.root=args[0]
self.configure(text=kwargs["text"])
self.configure(fg=kwargs["fg"],
activeforeground=kwargs["fg"])
try:
self.configure(command=kwargs["command"])
except KeyError:
pass
self.configure(relief="flat",cursor="hand2")
self.bind("<Enter>",self.hover)
self.bind("<Leave>",self.leave)
self.old=kwargs["bg"]
self.dark=self.darken(self.old)
self.configure(bg=self.old,activebackground=self.dark,
bd=1,relief="solid")
def getRGB(self,h):
it=tuple(int(h[i:i+2], 16) for i in (0, 2 ,4))
return it
def getHex(self,h):
it='#%02x%02x%02x' % h
return it
def darken(self,h):
currentHex=self.old.replace("#","")
currentRGB=self.getRGB(currentHex)
currentR=currentRGB[0]
currentG=currentRGB[1]
currentB=currentRGB[2]
if currentR>30:
newR=round(currentR-30)
else:
newR=0
if currentG>30:
newG=round(currentG-30)
else:
newG=0
if currentB>30:
newB=round(currentB-30)
else:
newB=0
newRGB=(newR,newG,newB)
newHex=self.getHex(newRGB)
return newHex
def hover(self,event):
self.configure(bg=self.dark)
def leave(self,event):
self.configure(bg=self.old)
The section of the code where I'm trying to grid them in a Frame is here (I imported gameButton as GB):
game=Frame(notebook,bg=bg)
notebook.add(game,text="Game")
mapb=GB(game,text="Map",compound="left",bg=bg,fg=fg)
travelb=GB(game,text="Travel",compound="left",bg=bg,fg=fg)
bagb=GB(game,text="Bag",compound="left",bg=bg,fg=fg)
pokedexb=GB(game,text="Pokédex",compound="left",bg=bg,fg=fg)
partyb=GB(game,text="Party",compound="left",bg=bg,fg=fg)
saveb=GB(game,text="Save",compound="left",bg=bg,fg=fg)
mapb.grid(row=0,column=0,padx=5,pady=5,sticky="nesw")
travelb.grid(row=0,column=1,padx=5,pady=5,sticky="nesw")
bagb.grid(row=0,column=2,padx=5,pady=5,sticky="nesw")
pokedexb.grid(row=1,column=0,padx=5,pady=5,sticky="nesw")
partyb.grid(row=1,column=1,padx=5,pady=5,sticky="nesw")
saveb.grid(row=1,column=2,padx=5,pady=5,sticky="nesw")
The result is this:
The button grids fine in a normal root window, but it refuses to behave properly inside a Frame inside a root.
I'm trying to get content of a ScrolledText but so far success is not with me :)
I don't understand where i'm wrong.
Here a very simple example of not working code...
from Tkinter import Tk
from ScrolledText import ScrolledText
def returnPressed(value):
print "CONTENT: " + value
root = Tk()
st = ScrolledText(root)
st.bind("<Return>", lambda event, i=st.get("1.0", "end-1c"): returnPressed(i))
st.insert("insert", "TEST")
st.pack()
root.mainloop()
Ok this is because of lambda definition.
By this way, function is created with constant "i" value, which is the value at declaration of function.
By rewording lambda as it, it works!
st.bind("<Return>", lambda event: returnPressed(st.get("1.0", "end-1c")))
You are getting the value at the time you create the button, which means the value will always be empty. You need to get the value at the time that the event is processed.
You should avoid using lambda, it is a somewhat advanced concept that in this case adds complexity without adding any value. Simply get the value from within the function:
def returnPressed(event):
value = event.widget.get("1.0", "end-1c")
print "CONTENT: " + value
...
st.bind("<Return>", returnPressed)
I am using an IPython Jupyter notebook. In the following situation, I call a function using interact(), which in turns calls a second function again using interact().
def fun1(dataset_id):
dataset = read_dataset(dataset_id)
interact(fun2, data=dataset, var=(0,dataset.property,0.1))
def fun2(data, var):
# something
interact(fun1, dataset_id=(0,5,1))
When first running this, it display 2 slider widgets: one for dataset_id, and one for the variable var. But if I vary the dataset_id slider once, a second slider for var is added below the first var slider, so now I have 3 sliders in total. How can I avoid this?
This is only one step less hacky, but at least you don't have to have a button:
from ipywidgets import *
from IPython.display import display
datasets=[{"property":1},{"property":2},{"property":3},{"property":4},{"property":5}]
def read_dataset(dataset_id):
return datasets[dataset_id]
def fun1(dataset_id):
global sliders
try:
sliders.close()
except NameError:
pass
dataset = read_dataset(dataset_id)
sliders = interactive(fun2, data=fixed(dataset), var=(0,dataset["property"],0.1)) # note I am now using interactive, instead of interact, because I need the close() function
display(sliders)
def fun2(data, var):
print var
interact(fun1, dataset_id=(0,5,1))
After a frustrating day, I came up with a totally hacky way to solve this (but at least it achieves 100% what I want). I am adding a button which, when clicked, invokes .close() on the second slider, as well as on the button itself. Therefore, before each time I need to move the first slider, I press this button to clear up.
Here is a fully-functioning code based on the snippet in the question, that you can copy-paste in your interpreter.
from ipywidgets import *
from IPython.display import display
datasets=[{"property":1},{"property":2},{"property":3},{"property":4},{"property":5}]
def read_dataset(dataset_id):
return datasets[dataset_id]
def fun1(dataset_id):
dataset = read_dataset(dataset_id)
sliders = interactive(fun2, data=fixed(dataset), var=(0,dataset["property"],0.1)) # note I am now using interactive, instead of interact, because I need the close() function
close_button = widgets.Button(description="Remove sliders")
def remove_sliders(b):
sliders.close()
b.close()
close_button.on_click(remove_sliders)
display(sliders)
display(close_button)
def fun2(data, var):
print
# something
interact(fun1, dataset_id=(0,5,1))
Here is another solution, you could create two sliders, and make the "max" of the second slider dependent on the property selected with the first slider:
import ipywidgets as widgets
from ipywidgets import *
from IPython.display import display
datasets=[{"property":1},{"property":2},{"property":3},{"property":4},{"property":5}]
def read_dataset(dataset_id):
return datasets[dataset_id]
w_slider1 = IntSlider(min=0, max=len(datasets)-1, step=1)
w_slider2 = FloatSlider(min=0, step=0.1)
def fun1(dataset_id):
dataset = read_dataset(dataset_id)
#you could get rid of function "read_dataset"
#dataset = datasets[dataset_id]
w_slider2.max = dataset['property']
def fun2(data, var):
#call fun1 to update the size of 2nd slider
fun1(data)
#do something
print(data, var)
interact(fun2, data=w_slider1, var=w_slider2)