Changing image label dynamically causes entire window to grow - python

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.

Related

ipywidgets: button.on_click() has output delay

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

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()

How To Clear a Label Assigned to a Grid in Specific Scenario Tkinter

I'm currently writing a manga reader and viewer program using the tkinter library in python for a gui, Im trying to make it so it lists the titles currently, and as I do this I realize its overlapping them, ive searched far and wide but wasnt able to find a good procedure to lets say "remove/forget" them on the new button press.
My code is listed below:
import json, webbrowser, requests
import tkinter as tk
from tkinter.ttk import *
from urllib import *
import urlopen
from urllib.request import *
import os
os.system("chcp 65001")
app = tk.Tk()
def get_button():
mid = entry.get()
if mid == "soap":
mid = "176758"
url = f"https://somewebsite/api/gallery/{mid}"
#label and pack for manga id
mangaid = tk.Label(text=f"ID : {mid}")
mangaid.grid(column=0, row=4, columnspan=2,)
#prints the url
print(url)
#open url data
uf = requests.request(method="get",url=url)
j_result = uf.json()
title = j_result['title']
j_title = title['japanese']
e_title = title['english']
#shows the title text
mangaide = tk.Label(text=f"English Title : {e_title}")
mangaidj = tk.Label(text=f"Japanese Title : {j_title}")
mangaide.grid(column=0, row=5, columnspan=2,)
mangaidj.grid(column=0, row=6, columnspan=2,)
def on_open():
mid = entry.get()
if mid == "soap":
mid = "176758"
URL = f"https://somewebsite.net/g/{mid}/"
#opens url
webbrowser.open(URL, new=2)
print(URL)
enterid = tk.Label(text="Enter ID or Name")
entry = tk.Entry()
button = tk.Button(text="Get", command=get_button)
button2 = tk.Button(text="Open", command=on_open)
enterid.grid(column=0, columnspan=2, pady=(10))
entry.grid(column=0, columnspan=2, padx=(50))
button.grid(row=3, column=0, pady=(10))
button2.grid(row=3,column=1)
app.mainloop()
If you look at lines 29-32 Im assigning a label and placing it on the grid, although when I press the button again, to get new data, it proceeds to do the following:
1st Data Grab
2nd Data Grab
In the first one you can see that it worked perfectly, but in the 2nd grab you can see that it took the previous answers and overlayed them behind, so in gist m trying to figure out a way to fix this, my main goal is to find a way to remove the overlayed text.
Im sorry if this isnt specific enough, if it isnt please contact me on discord (Ganoosh#4020) or through stack overflow comments.
Create empty Labels above app.mainloop() like this:
mangaide = tk.Label()
mangaidj = tk.Label()
mangaide.grid(column=0, row=5, columnspan=2,)
mangaidj.grid(column=0, row=6, columnspan=2,)
and put text on them inside get_button() function with the use of global keyword. Modify your get_button() function like this:
def get_button():
global mangaidj, mangaide # Using global keyword to access those Labels
mid = entry.get()
if mid == "soap":
mid = "176758"
url = f"https://somewebsite/api/gallery/{mid}"
#label and pack for manga id
mangaid = tk.Label(text=f"ID : {mid}")
mangaid.grid(column=0, row=4, columnspan=2,)
#prints the url
print(url)
#open url data
uf = requests.request(method="get",url=url)
j_result = uf.json()
title = j_result['title']
j_title = title['japanese']
e_title = title['english']
#shows the title text
mangaide.config(text=f"English Title : {e_title}") # These lines will
mangaidj.config(text=f"Japanese Title : {j_title}") # update the text each time
Hpoe this helps :)

Delete/hide HBox and children widgets using Button inside HBox

I am trying to make an input widget for a module i have made (see this SO question).
The input widget should have a title bar and a variable number of input lines below. I had in mind to have an delete button at the end of each input line.
The delete button should ideally delete the container widget and all the children widgets, but hiding the container widget and children would also be acceptable.
I have not been able to find a useful recipe to this problem.
Currently i got this code, but i have no clue less as how to solve the problem.
import ipywidgets as w
​
def add_btn_clicked(b):
input_box.children = (input_box.children[0], line()) + input_box.children[1:]
​
def delete_btn_clicked(b):
# ???
with output:
print(b.keys)
return None
add = w.Button(icon="plus-circle")
add.on_click(add_btn_clicked)
​
title = w.HBox([w.Label(value=str(i)) for i in range(3)]+[add])
​
def line():
delete = w.Button(icon="trash")
delete.on_click(delete_btn_clicked)
return w.HBox([w.FloatText(value=i) for i in range(3)]+[delete])
​
input_box = w.VBox([title,line()])
output = w.Output()
​
display(input_box)
display(output)
Is there a way to tell what the parent element is from the button click or another way to achieve what I am attempting?
You can create the widgets and container separately, then define the .parent attribute on the children as the container, before assembling together. That way you can effectively hide the container when the button is clicked (with .parent.layout.display = 'none').
import ipywidgets as w
def add_btn_clicked(b):
input_box.children = (input_box.children[0], line()) + input_box.children[1:]
def delete_btn_clicked(b):
b.parent.layout.display = 'none'
add = w.Button(icon="plus-circle")
add.on_click(add_btn_clicked)
title = w.HBox([w.Label(value=str(i)) for i in range(3)]+[add])
def line():
delete = w.Button(icon="trash")
delete.on_click(delete_btn_clicked)
val_widgets = [w.FloatText(value=i) for i in range(3)]
container = w.HBox()
delete.parent = container
for widg in val_widgets:
widg.parent = container
children = val_widgets + [delete]
container.children = children
return container
input_box = w.VBox([title,line()])
output = w.Output()
display(input_box)
display(output)

Remove buttons created in for loop python tkinter

I want that when I go to secondpage and back to the mainpage it removes the buttons created in the mainloop. So when I open the second page again, it needs to make the buttons again. with the updated list
Now it shows the buttons 2 times. I know to not add it into a function it will render just 1 time but the point is it will be removed when it is in a function and it need to remaked every time I open the mainpage. So that I can edit for example the list when the application is open and it will be rendered again.
Here is my code:
from tkinter import *
items = ['ijs', 'water', 'lolly', 'laptop']
root = Tk()
def buttonmaker():
for x in items:
button = Button(master=secondpage, text=x ).pack()
def mainpagetosecondpage():
mainpage.pack_forget()
buttonmaker()
secondpage.pack()
def secondpagetomainpage():
secondpage.pack_forget()
mainpage.pack()
#mainpage
mainpage = Frame(master=root)
main_to_second_button = Button(master=mainpage, text='secondpage', command=mainpagetosecondpage).pack()
#scondpage
secondpage = Frame(master=root)
Second_to_main_button = Button(master=secondpage, text='mainpage', command=secondpagetomainpage).pack()
mainpage.pack()
root.mainloop()
If something is unclear, please ask
You need to call buttonmaker only once, during the setup of the second frame:
from tkinter import *
root = Tk()
def buttonmaker():
items = ['ijs', 'water', 'lolly', 'laptop']
for x in items:
button = Button(master=secondpage, text=x )
button.pack()
def mainpagetosecondpage():
mainpage.pack_forget()
secondpage.pack()
def secondpagetomainpage():
secondpage.pack_forget()
mainpage.pack()
#mainpage
mainpage = Frame(master=root)
main_to_second_button = Button(master=mainpage, text='secondpage', command=mainpagetosecondpage)
main_to_second_button.pack()
#scondpage
secondpage = Frame(master=root)
Second_to_main_button = Button(master=secondpage, text='mainpage', command=secondpagetomainpage)
Second_to_main_button.pack()
buttonmaker()
mainpage.pack()
root.mainloop()
Also, you need to avoid putting the layout on the same line as the initialization. Code like variable = Widget(args).pack() will lead to bugs. Use 2 lines like I did above.

Categories