How to generate buttons with different images [duplicate] - python

This question already has an answer here:
How to add an Image to a widget? Why is the image not displayed?
(1 answer)
Closed 4 years ago.
So I'm writing basic stuff in python with tkinter and PIL and the problem here is that when I run the program, only the last button show the image of the item I asked him to show.
So my program was expected to extract name of items from lines containing the name of a game Champion in a txt document.
Each line in the document looks like "ItemName/stat1/stat2/stat3/ChampionName".
The code was then supposed to create buttons with a picture of the item on it(I made sure to name the pngs and the item name in the .txt the same and to put everything in the same folder) but in the end, only the last button had an image on it.
What I tried :
I tried to reduce the number of elements in the txt, it didn't work
I then thought that the problem was that the variable icon being updated, the image shown would disappear. So I tried to make an array but it didn't work either because what I put in isn't an int value, I know it's about the 'i' but I don't know if I can put something else appropriate.
My first attempt :
from PIL import Image, ImageTk
import tkinter as tk
itemwindo = tk.Tk()
itemwindo.title("Items")
data = open("Ressource.txt","r")
for line in data:
if 'Vi' in line:
(a,b,c,d,e) = line.split("/")
icon = ImageTk.PhotoImage(Image.open(a + '.png'))
bt = tk.Button(itemwindo,image=icon)
bt.pack()
itemwindo.mainloop()
And my second with arrays :
data = open("Ressource.txt","r")
imglist = arr.array('i')
for line in data:
if 'Vi' in line:
(a,b,c,d,e) = line.split("/")
icon = ImageTk.PhotoImage(Image.open(a + '.png'))
imglist.extend([icon])
p = len(imglist)
bt = tk.Button(itemwindo,image=imglist[p])
bt.pack()
itemwindo.mainloop()
I would like each button to display the picture of the affiliated item on it.

In the loop that cretes the buttons you use the same name for
every image. This creates a new object every iteration and the
previous buttons can no longer find a reference to the previous image.
To fix this you can assign the image attribute of the button
at creation and then it does no longer make any difference if you reuse the icon or bt name. Study the example below:
import tkinter as tk
itemwindo = tk.Tk()
file_list = ['beer.png', 'test.gif']
for file in file_list:
icon = tk.PhotoImage(file=file)
bt = tk.Button(itemwindo, image=icon)
bt.pack()
bt.image = icon # Save reference to icon in button
itemwindo.mainloop()

Related

Tkinter image not adding to text widget

I am trying to make text widget that get images from reddit then displaying it on a text widget, but for some reason, with no error, the image is not displaying on the widget.
This is the part of my code that get the image from reddit and adding it on the widget.
subreddit = reddit.subreddit("MinecraftMemes")
filtered = [x for x in subreddit.top() if not x.stickied and not x.is_self]
url = filtered[random.randint(0, len(filtered) - 1)].url
url_type = self.get_url_type(url)
urllib.request.urlretrieve(url,f"minecraft{url_type}")
image = Image.open(f"minecraft{url_type}")
image.save('minecraft.png')
img = tkinter.PhotoImage(file=r"minecraft.png")
self.text_widget.image_create(END,image=img)
print("activated")
The activated is printed, the minecraft.png is seen on file explorer, so it is the part of the code with tkinter that is not working
I believe the issue is that the image is an object and it gets discarded when the code executes, so you might want to do self.img.

Tkinter code runs fine from spyder but when converted to .exc doesn't open (python)

I have some code that is meant to take a the path to a folder of data files, extract and tidy the data, then return a single pandas dataframe that can be saved. To make it more user friendly I have added a user interface with tkinter. It all runs fine in spyder, and making the executable with pyinstaller seems to run ok, but when I try to run the .exc it simply opens the blank command window without opening the UI. It doesn't give any errors so I don't know where to start on the debugging, any help gratefully received.
My code consists of four files:
tidyEPR.py (a single function that opens and cleans up a single epr file. Packages:Pandas)
rowNames.csv (contains variable names.)
combEPRs.py (a single function that iterates through data files and calls tidyEPR() to combine data into a single dataframe and opens rowNames.csv to rename the rows. Packages:Pandas, pathlib, glob)
practice.py (a tkinter application that allows the user to input data and save locations and press a button to run, calls combEPRs. Packages: Tkinter, os, sys)
To make the .exc I put all the above into one folder, then use
pyinstaller --onefile practice.py
or
pyinstaller practice.py
in cmd, in both cases it appears to run successfully and makes the .exc file.
No errors are raised. I have tried converting a different tkinter script with the above method and it works fine so it is definitely something wrong with my code.
I have never tried to do this kind of thing before so my code may be a bit of a mess, sorry.
Here is practice.py:
import tkinter as tk
import os
import sys
#import custom functions
from combEPRs import combEPRs
#set working directory to file location
os.chdir(os.path.dirname(sys.argv[0]))
window = tk.Tk()
#initilaise df
df="empty"
def makeFile():
global df
#retrive entered text
enteredText = ent_dataPath.get()
#create dataframe from data files in entred path
df = combEPRs(enteredText)
#update label to show the finished dataframe
lbl_ConsoleReadout.config(text=df)
def saveFile():
global df
#change label to say "saving..."
lbl_ConsoleReadout.config(text="saving...")
#retrive entered text
enteredText = ent_savePath.get()
#save dataframe
df.to_csv(enteredText)
#change label text to say "Done!"
lbl_ConsoleReadout.config(text="Done!")
frame = tk.Frame(borderwidth=5)
frame.pack()
#make label to explain what to do
lbl_info = tk.Label(
text="Put full path to the data folder then press go, if you are hapy with output put in full save path and press save",
master = frame,
)
lbl_info.pack(pady=10)
#make text entry widget to get path to data folder
ent_dataPath = tk.Entry(
master = frame,
width = 100
)
ent_dataPath.pack(pady=10)
#make button to press to submit folder to combEPRs
btn_go = tk.Button(
text="GO",
width = 10,
height = 2,
master = frame,
command = makeFile
)
btn_go.pack(pady=10)
#make text widget to print output to.
lbl_ConsoleReadout = tk.Label(
width = 100
)
lbl_ConsoleReadout.pack()
#make text entry widget to get path to to save to
ent_savePath = tk.Entry(
master = frame,
width = 100
)
ent_savePath.pack(pady=10)
#make save button
btn_save = tk.Button(
text="SAVE",
width = 10,
height = 2,
master = frame,
command = saveFile
)
btn_save.pack(pady=10)
window.mainloop()

Use PNG files from a dictionary to display in a Image widget in PySimpleGUI (Python)

I have Python GUI using PySimpleGUI that needs to display multiple plots which I intend to navigate via a set of buttons. I know I can have all the plots saved as PNG in a given folder and simply load them in a Image object and use the Update method of the element to load a new image when a button is clicked.
Something like the below works well:
[sg.Image(filename=os.getcwd() + pngFileName, key='key1', size=(5, 6))]
where I need to pass a filename of the plot I want to read from my current directory and show in the Image widget.
But this means that I will have all the files saved in a folder while I would rather prefer to have all the PNGs in a dictionary and refer that dictionary when I need to pass a given filename to the sg.Image().
The advantage I see is that this way I don't have to occupy space on the hard drive to store the PNGs and also instead of having to write and then read from disk, I guess it will be faster to get the PNGs directly from the dictionary that is in memory at run time.
I am not able to achieve this as the code seems to expect a filename that has a specific path rather than passing a specific value of a dictionary containing PNGs.
How can I achieve this?
Question: Use PNG files from a dictionary to display in a Image widget in PySimpleGUI (Python)
The class Image is defined as:
class Image(Element):
def __init__(self, filename=None, data=None, ...):
"""
:param filename: (str) image filename if there is a button image.
GIFs and PNGs only.
:param data: Union[bytes, str] Raw or Base64 representation of the image
to put on button.
Choose either filename or data
You can do:
import PySimpleGUI as sg
import os
cwd = os.getcwd()
fname = 'image1.png'
with open('{}/{}'.format(cwd, fname)) as fh:
image1 = fh.read()
[sg.Image(data=image1, key='key1', size=(5, 6))]
Something like this should work (assuming two images:image1, image2):
import PySimpleGUI as sg
# All the stuff inside your window.
layout [
[sg.Image(data=image1, key='__IMAGE__', size=(5, 6))]
]
# Create the Window
window = sg.Window('Window Title', layout)
# Event Loop to process "events" and get the "values" of the inputs
while True:
event, values = window.read()
if event in (None, 'Cancel'): # if user closes window or clicks cancel
break
window.Element('_IMAGE_').Update(data=image2)
window.close()
One of the Demo Programs listed on the PySimpleGUI GitHub is Demo_Img_Viewer.py. In it you'll find a function that takes a filename and returns the data that you can pass to the update method of your Image element.
This function is part of that demo. It will render a file into the format that the update method expects.
from PIL import Image
def get_img_data(f, maxsize=(1200, 850)):
"""
Generate image data using PIL
"""
img = Image.open(f)
img.thumbnail(maxsize)
bio = io.BytesIO()
img.save(bio, format="PNG")
del img
return bio.getvalue()
You can loop through your files and "pre-render" them by calling this function and saving the results.
Then later you can update your image element with one of these pre-rendered images.
window['My Image Element Key'].update(data=rendered)

Tkinter : How to change position of text on image

I have developed some application on my laptop with python and tkinter. Then, I was stuck at some point. Question is : how can I change text position on image.
import tkinter as tk
from PIL import Image, ImageTk
path_to_pic = "....."
root = tk.Tk()
pic = Image.open(path_to_pic)
tkpic = ImageTk.PhotoImage(pic)
tk.Label(root, image = tkpic, text = ".....", compound = tk.CENTER).pack()
root.mainloop()
This shows that my text appears on the picture, only on the center. I would like to move my text little by little and find best position. Do you know any solution or similar way to achieve this ?
You can move text horizontally and vertically by adding spaces and '\n's respectively, to any side(s) of the text you wish:
text = "caption \n\n\n\n\n\n\n"
This will put "caption" at the top left of the text.

Printing main window in PyQt

I need to be able to save the main window of a pyqt app in a PS or similar file format so that I can send it to a printer. I would just make a built in screen shot function but my main window exceeds the size of my screen. Anyone know of a way to capture the window in it's entirety or is there a prebuilt class that could do this?
QPixmap has the static method grabWidget.
Pointing this method at your window will give you a pixmap that you can save to a file or use for printing.
If calling from inside your main window class:
sshot = QPixmap.grabWidget(self)
sshot.save('sshot.png')
QPixmap.grabWiget has been deprecated. We can instead use QWidget.grab() function instead to capture window. However, it only captures the currently visible parts of the screen which can be a problem when you have a window with a scroll area. So the only method/hack that worked for me was to use ScrollArea's page step functionality paired with widget grab.
# Get total pages in window
page_count = self.scrollArea.verticalScrollBar().maximum() / self.scrollArea.verticalScrollBar().pageStep()
image_list = []
# iterate through each page step
for i in range(int(round(page_count)) + 1):
step = self.scrollArea.verticalScrollBar().pageStep() * i
self.scrollArea.verticalScrollBar().setValue(step)
# capture and save each image
self.scrollArea.grab().save(f"page - {i}.jpg", quality=100)
# convert all images to Pillow Image() to later convert to pdf
image_list.append(Image.open(f"report_page - {i}.jpg"))
# save as pdf file
pdf_file_name = f'pdf_file.pdf'
image_list[0].save(pdf_file_name, "PDF", resolution=100.0, save_all=True, append_images=image_list[1:])
# delete images if not neccessary
for i in range(len(image_list)):
os.unlink(f"page - {i}.jpg")
P.s. Please let me know if there a more elegant solution to this problem

Categories