ipywidgets: button.on_click() has output delay - python

Introduction
I am trying to make a small tool for classifying images using the ipywidgets in a Jupyter Notebook, but I am having some trouble aligning the classes and the images. Do you have any suggestion how to fix this.
What I did
import ipywidgets as widgets
from IPython.display import display
import glob
# My images
image_paths = glob.glob("./images/*.png")
# Display image
def display_image(path):
file = open(path, "rb")
image = file.read()
return widgets.Image(
value=image,
format='png',
width=700,
height=700,
)
# Dropdown
def create_dropdown():
return widgets.Dropdown(
options=["1","2","3","4","5","6","7","8","9","10"],
value='5',
description='Category:',
disabled=False
)
# Creating widgets
input_dropdown = create_dropdown()
button = widgets.Button(description="Submit")
output_image = widgets.Image()
output_image.value = display_image(image_paths[-1]).value
# Define function to bind value of the input to the output variable
def bind_input_to_output(sender):
image_path = image_paths[-1]
image_score = input_dropdown.value
next_image_path = image_paths.pop()
print(image_score, image_path)
output_image.value = display_image(next_image_path).value
# Tell the text input widget to call bind_input_to_output() on submit
button.on_click(bind_input_to_output)
# Displaying widgets
display(output_image, input_dropdown, button)
Results
With the above code I end up categorising the upcoming picture, but I really don't understand why. It seems the widgets does not update the image the first time I press the button.

def bind_input_to_output(sender):
image_path = image_paths.pop()
image_score = input_dropdown.value
next_image_path = image_paths[-1]
print(image_score, image_path)
output_image.value = display_image(next_image_path).value
pop first and give next filename at last item

Related

Why does this not display the current song image? [duplicate]

This question already has answers here:
Python 3.7, tkinter, jpg: couldn't recognize data in image file
(3 answers)
Closed 1 year ago.
I'm trying to get the image from the song album to display in the window with the song title and artist but it just doesn't do anything. I've tried replacing the "imageLabel" with
"imageLabel = tkinter.Label(window,image=tkinter.PhotoImage(file="CurrentSong.jpg"))" but it still doesn't work.
import requests
import time
import tkinter
token = ''
endpoint = "https://api.spotify.com/v1/me/player/currently-playing"
spotifyHeaders = {'Authorization':'Bearer ' + token}
requestAmount = 1
window = tkinter.Tk(className="|CurrentSong Spotify Song|")
window.geometry('400x400')
canvas = tkinter.Canvas(window,height=1000,width=1000)
canvas.pack()
songLabel = tkinter.Label(window,bg='grey')
songLabel.pack()
def GrabSpotifyCurSong(curSongJson):
return curSongJson['item']['name']
def GrabSpotifyCurArtist(curSongJson):
return curSongJson['item']['artists'][0]['name']
def GrabCurrentSongImage(curSongJson):
return curSongJson['item']['album']['images'][0]['url']
def displaySongs():
while True:
try:
curSong = requests.get(endpoint, headers=spotifyHeaders)
curSongJson = curSong.json()
break
except:
print("Please start listening to a song")
time.sleep(2)
with open('CurrentSong.png','wb+') as SongImage:
response = requests.get(GrabCurrentSongImage(curSongJson))
SongImage.write(response.content)
currentSong = GrabSpotifyCurSong(curSongJson)
currentArtist = GrabSpotifyCurArtist(curSongJson)
img = tkinter.PhotoImage(file="CurrentSong.png")
imageLabel = tkinter.Label(window,image=img)
# songLabel['text'] = f'{currentArtist} - {currentSong}'
# songLabel.place(height=400,width=400)
print(f'{currentArtist} - {currentSong}')
window.after(2500,displaySongs)
displaySongs()
window.mainloop()
Images with tkinter has to be PhotoImage instances, here it is just a string of location of the image and tkinter does not understand that. Furthermore, tkinter.PhotoImage does not recognize JPEG format, so you have to convert it to PNG or use PIL.ImageTk.PhotoImage to use JPEG.
For JPEG and other formats too:
First pip install Pillow and then:
import tkinter
from PIL import Image, ImageTk
....
img = ImageTk.PhotoImage(Image.open("CurrentSong.jpg"))
imageLabel = tkinter.Label(window,image=img)
Adding further here, you can also use ImageTk.PhotoImage(file="CurrentSong.jpg") but that will remove the flexibility that you could get if you want to, say, resize or do some filters to your image. If not, then use that.
For GIF, PGM, PPM, and PNG:
img = tkinter.PhotoImage(file="CurrentSong.png")
imageLabel = tkinter.Label(window,image=img)
Also note that if these are inside function you have to keep reference to the object to avoid it being collected by the gc after the function finishes running.

Button callback function not completely executed by Panel library in Python

I am currently struggling trying to use the panel library in Python, in order to build an interactive dashboard to analyze and display CSV data. My current goal is to let the user enter an initial and a final date, which will be used to filter a DataFrame once a button is pressed. However, whenever I press the button, the on_click function is not completely executed before the script stops running. The code snippet is the following:
import panel as pn
pn.extension()
def acquire_data(dateBeginning, dateEnd):
eventDF = pd.read_csv('multi.csv')
eventDF['Date']= pd.to_datetime(eventDF['Date'])
dateDF = eventDF[eventDF.upvotes > 8]
print(eventDF)
def register_dates(event, save=True):
dateBeginning = date1Picker.value
dateEnd = date2Picker.value
if dateBeginning < dateEnd:
text = pn.widgets.StaticText(name='Static Text', value='A string')
spinner = pn.indicators.LoadingSpinner(width=50, height=50, value=True, color='info', bgcolor='light')
layout = pn.Column(text, spinner, align='center')
layout.app()
print('getting in')
acquire_data(dateBeginning, dateEnd)
print('getting out')
spinner.value = False
else:
print('Not working')
#pn.pane.Alert('## Alert\nThis is a warning!')
return save
date1Picker = pn.widgets.DatePicker(name='Date Initiale', margin=25)
date2Picker = pn.widgets.DatePicker(name='Date Finale', margin=25)
button = pn.widgets.Button(name="Analyse", button_type='primary', margin=(25, 0, 20, 200), width=200)
button.on_click(register_dates)
dateLayout = pn.Row(date1Picker, date2Picker)
layout = pn.Column(dateLayout, button, width=200, align='center')
layout.app()
I was also aiming at having the first layout be replaced by the one with the spinner and the text once the button is pressed, but I haven't found anything in the doc mentioning how to do so. If anyone could give me a hint regarding these issues, that would really help me!
In def acquire_data(dateBeginning, dateEnd):
pd.read_csv('multi.csv'), pd.to_datetime(eventDF['Date'])
For start, in this function I think you forgot to import panda and your app just crash.
add: import pandas as pd
Ex:
import panel as pn
import pandas as pd

Opencv GUI python: arrange the created buttons

I use python 3.8 and Opencv in Linux.
I have several buttons that have stacked horizontally. How can I arrange them as I like (e.g., in a grid way?)
Is it possible to show some icons for each of the buttons?
Is it possible to make the fonts of the buttons bar larger?
Part of my script: (any suggestion to make my script better is appreciated)
if __name__== "__main__":
Folder_name = "male"
data_path = "path/to/images"
data_path = os.path.join(data_path, Folder_name)
all_imgs_path = glob.glob("{}/*.jpg".format(data_path))
all_imgs_path = sorted(all_imgs_path)
annot = annotation_tool(nof_imgs=len(all_imgs_path))
for index, im_dir in enumerate(all_imgs_path):
annot[index] = im_dir
item_path = "guid.jpg"
img = cv2.imread(item_path)
img_name = item_path.split("/")[-1]
cv2.imshow("{}".format(img_name), img)
cv2.createButton('Next', annot.Next, ["Next Image"])
cv2.createButton('Back', annot.Back, ["Previous Image"])
cv2.createButton('Submit', annot.Submit, ["Submit"])
# there are many of these buttons
UB_Tshirt = cv2.createButton("UB_Tshirt", annot.checkbox, "UB_Tshirt", 1, 0)
UB_Shirt = cv2.createButton("UB_Shirt", annot.checkbox, "UB_Shirt", 1, 0)
UB_Coat = cv2.createButton("UB_Coat", annot.checkbox, "UB_Coat", 1, 0)
cv2.waitKey(0)
print("end")
Edit:
As you see in the image, the buttons bar is very long and goes out of the screen. I would like to create a button pad that is squared.
This isn't a complete answer, but in regards to button arrangement, you have a little control using 'cv2.QT_NEW_BUTTONBAR'.
There's further detail here: https://docs.opencv.org/4.x/dc/d46/group__highgui__qt.html#gad15c7adb377e778dc907c0e318be193e

how to generate multiple buttons with a loop?

I have programmed software that displays a "tuile".
Definition of a tuile:
A tuile is a Frame which contains a button which displays an image and an explanatory text.
I would like to display 3 tuiles with 3 different settings.
listes_icones = ["icone1.png","icone2.png","icone3.png"]
listes_relx = [".3",".4",".5"]
listes_text = ["SYSTEM", "USER", "GAME"]
for i in range(3):
gen_img = PhotoImage(file=listes_icones[i])
gen_cadre = Frame(home,width=100, height=100,bg=bg_root)
gen_cadre.place(anchor="c", relx=listes_relx[i], rely=.5)
gen_img_bouton = Button(gen_cadre, image=gen_img, relief="flat",bg=bg_root)
gen_img_bouton.pack()
gen_text = Label(gen_cadre, text=listes_text[i], bg=bg_root, fg=text_color,font="blocktastic 18")
gen_text.pack()
I manage to display the text but not the button and the image, the variable is overwritten. How to solve this problem?
The problem that you are facing is like you said, the variable is overwritten in your loop. To solve this you need to keep track of your generated images. A simple solution is to store them in a list and get them in the next step. Here is an exampel:
import tkinter as tk
import PIL
listes_icones = ["icone1.png","icone2.png","icone3.png"]
gen_icons = []
listes_relx = [".3",".4",".5"]
listes_text = ["SYSTEM", "USER", "GAME"]
home = tk.Tk()
for i in range(3):
gen_img = tk.PhotoImage(file=listes_icones[i])
gen_icons.append(gen_img)
gen_cadre = tk.Frame(home,width=100, height=100)
gen_cadre.place(anchor="c", relx=listes_relx[i], rely=.5)
gen_img_bouton = tk.Button(gen_cadre, image=gen_icons[i], relief="flat")
gen_img_bouton.pack()
gen_text = tk.Label(gen_cadre, text=listes_text[i], font="blocktastic 18")
gen_text.pack()
home.mainloop()

Changing image label dynamically causes entire window to grow

I'm trying to make my UI (which does other things ordinarily, this is the barebones version) update an image label from a web resource. When you press the button, the entire window grows instead of the image being replaced. I thought that my call to pack would take care of this, but apparently not.
Thanks for the input!
from Tkinter import *
import base64
import urllib2
BTNTEXT_NEW_COMIC = "New Comic"
def get_random_xkcd():
COMIC_PREFIX = 'http://imgs.xkcd.com/comics/'
response = urllib2.urlopen('http://c.xkcd.com/random/comic/')
html = response.read()
if COMIC_PREFIX in html:
index = html.find(COMIC_PREFIX)
substring = html[index:]
endquote = substring.find("\"")
url = substring[:endquote]
return url
else:
return
class LoggerWindow:
def __init__(self, master):
global frame
frame = Frame(master)
frame.pack()
self.newcomic_button = Button(frame, text=BTNTEXT_NEW_COMIC, command=self.new_xkcd)
self.newcomic_button.pack(side=LEFT)
self.new_xkcd()
def new_xkcd(self):
global frame
url = get_random_xkcd()
print url
u = urllib2.urlopen(url)
raw_data = u.read()
u.close()
b64_data = base64.encodestring(raw_data)
self.xkcd_image = PhotoImage(data=b64_data)
self.xkcd_label = Label(image=self.xkcd_image)
self.xkcd_label.pack()
root = Tk()
app = LoggerWindow(root)
root.mainloop()
You should be able to call self.xkcd_label.configure(image=self.xkcd_image) instead of creating a new image like you do. If that doesn't work, try calling self.xkcd_label.pack_forget() to remove the label before repacking it.

Categories