custom Tkinter button won't grid properly inside Frame - python

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.

Related

Tkinter automatically update Label after Entry in Python

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):
...

Choosing from a List of methods in a tkinter Button

Good Day,
I'm new to this forum (and quite new to programming), so I hope my question is properly formulated.
I've been trying to create a GUI in python using tkinter, and I want to have two buttons calling methods of two different classes. One method is defining an integer, the second one is reporting content. I'd have a list of objects of the latter class, and I want to choose the right instance by the integer. Here's a MWE:
import tkinter as tk
class data:
def __init__(self, content):
self.content = content
def report(self):
print("This is reported as self.content:" + str(self.content)) #This doesnt report the correct value for some reason?
print("The Class does register the correct idx:" + str(Selector.idx))
print("Using the Dict the correct value can be returned:" + str(vocables[Selector.idx].content))
class increment:
def __init__(self):
self.idx = 0
def increase(self):
self.idx += 1
print(self.idx)
vocables[self.idx].report()
root = tk.Tk()
Selector = increment()
vocables = []
for id in range(10):
vocables.append(data(id))
# print(vocables[id].content)
CheckVocable = tk.Button(root, text="Report", command=vocables[Selector.idx].report)
CheckVocable.pack()
NextVocable = tk.Button(root, text="Increase Index", command=Selector.increase)
NextVocable.pack()
root.mainloop()
I do not understand why the print of line 8 always reports the value of the first item in the list (vocabules[0] in this instance) instead of my desired value, which is returned in all other print cases. Am I messing up the work with classes or is the button behavior confusing me?
Thanks in advance!

sikuli observing multiple images with priority

I've defined a sikuli module which is used to click on an image when something appears inside a region.
# observer.py
from sikuli import *
class Observer:
# When "observedImage" appears inside "region", double click on "reactImage"
def __init__(self, region, observedImage, reactImage):
self.region = region
self.observedImage = observedImage
self.reactImage = reactImage
def start(self):
self.region.onAppear(self.observedImage, self.appearHandler)
self.region.observe(FOREVER, background = True)
def appearHandler(self, event):
doubleClick(self.reactImage)
event.repeat()
def stop(self):
self.region.stopObserver()
Here's how to use it:
import observer
import time
observer.Observer(Region(111,222,333,444), "imageToBeDetected1.png", "imageToBeClicked1.png").start()
observer.Observer(Region(555,666,66,666), "imageToBeDetected2.png", "imageToBeClicked2.png").start()
while True:
print('waiting')
time.sleep(1)
The problem with the above code is that when imageToBeDetected1 and imageToBeDetected2 both appear in Region(111,222,333,444) and Region(555,666,66,666) respectively, my mouse will move between imageToBeClicked1 and imageToBeClicked2. I want only imageToBeDetected1 to be clicked in this situation.
imageToBeDetected2 should be ignored when imageToBeDetected1 and imageToBeDetected2 both appear in Region(111,222,333,444) and Region(555,666,66,666), respectively.
How can I modify my code so that imageToBeDetected1 has a higher priority over imageToBeDetected2?
Or is there a better way to observe multiple images with sikuli?

Multiple interact() commands duplicate widgets in IPython

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)

Python PyQt scrollbar in the end position event

I want to load some part of the very big file into my QListWidget on Python PyQt. When user moves the scrollbar of the QListWidget and reaches the end of the scrollbar, the event is activating and the next part of the file is loading (appends) to the QListWidget. Is there some event for controlling the end position of the scrollbar?
There's no dedicated signal for "scrolled to end", but you can easily check that in the valueChanged signal:
def scrolled(scrollbar, value):
if value == scrollbar.maximum():
print 'reached max' # that will be the bottom/right end
if value == scrollbar.minimum():
print 'reached min' # top/left end
scrollBar = listview.verticalScrollBar()
scrollBar.valueChanged.connect(lambda value: scrolled(scrollBar, value))
EDIT:
Or, within a class:
class MyWidget(QWidget):
def __init__(self):
# here goes the rest of your initialization code
# like the construction of your listview
# connect the valueChanged signal:
self.listview.verticalScrollBar().valueChanged.connect(self.scrolled)
# your parameter "f"
self.f = 'somevalue' # whatever
def scrolled(self, value):
if value == self.listview.verticalScrollBar().maximum():
self.loadNextChunkOfData()
def loadNextChunkOfData(self):
# load the next piece of data and append to the listview
You should probably catch up on the docs for lambda's and the signal-slot framework in general.

Categories