Storing Objects in Dictionary Values - python

class Draw():
'''Class using, for example opengl, to display something on the screen'''
def add(self,size,file_name):
file_name= file_name
size = size
class Image(Draw):
def __init__(self,size,file_name):
self.size = size
self.add(self.size,file_name)
class Gui():
file_names = ['a.jpg','b.jpg']
images = {}
def __init__(self):
for e in self.file_names:
self.image = Image((50,50),file_name=e)
self.images[e] = self.image
def print_size(self):
print(self.image.size)
a = Gui()
a.print_size() #this gives me a proper (50,50) size
for e in a.images.values():
print(e.size) #this gives me wrong size
This is simplified version of my code. I don't have experience with storing objects in dictionary values.
My question is: is this normal that I don't access the right attributes of stored objects in dictionary? In this sample everything works ok, but is this a wrong way to writing code?

I run your code and it works as it should. In terminal there was printed
(50, 50)
(50, 50)
(50, 50)
Did you expected something else? Now I will try to explain few things.
class Draw():
'''Class using, for example opengl, to display something on the screen'''
I may not fully understand how it is supposed to work, but if you want to save size and file_name in add method you shouls use self. before variables, so it would look like
def add(self,size,file_name):
self.file_name = file_name
self.size = size
class Image(Draw):
def __init__(self,size,file_name):
self.size = size
self.add(self.size,file_name)
class Gui():
file_names = ['a.jpg','b.jpg']
images = {}
def __init__(self):
Now in every iteration you create new Image with the same size (50, 50), but diffrent filename and assing to map.
for e in self.file_names:
self.image = Image((50,50),file_name=e)
self.images[e] = self.image
In loop in above init method to self.image you assing the last image you created based on file_names ('b.jpg'), so self.image and self.images['b.jpg'] points to the same object.
Method print_size prints size of self.image / self.images['b.jpg'], which is (50, 50)
def print_size(self):
print(self.image.size)
a = Gui()
a.print_size() #this gives me a proper (50,50) size
Now you iterate on your images. There are 2: the one with file_name 'a.jpg' and second that was already printed before 'b.jpg'. Both of them has the same size of (50, 50).
for e in a.images.values():
print(e.size) #this gives me wrong size
I hope I clarified a little bit and it will help you

Related

Kivy Rectangle source doesn't get updated?

(I already read a lot of other posts about this but they do not seem to help me (or I simply don't understand them) )
I have a function Add() in which another function Grid() gets called that creates a Grid.png file and saves that to my desktop. This Add() function gets called multiple times (through a button) and with that also the Grid() function within. Here is a little code snippet:
Width = 700
Height = 700
def __init__(self,**kwargs):
super(Drw, self).__init__(**kwargs)
self.CellCount = 1
time.sleep(0.5)
self.cellAdd= int(input("\nCells to add: "))
self.bg = ""
with self.canvas:
self.add = Button(text = "add", font_size =40, pos = (700,300))
self.sub = Button(text="sub", font_size=40, pos=(700, 400))
self.add.bind(on_press = self.Add)
self.sub.bind(on_press= self.Sub)
self.add_widget(self.sub)
self.add_widget(self.add)
def Add(self, instance):
self.CellCount += self.cellAdd
Grid(self.CellCount, self.Width, self.Height)
with self.canvas:
self.bg = Rectangle(source= r"C:\Users\Max\Desktop\Grid.png", pos=(0,0), size= (self.Width, self.Height))
self.L = Label(text=str(self.CellCount)+" columns", pos=(500, 300))
What happens is that the first time I press the "Add" button, it does what it should so Add() gets called and in turn Grid() gets called and creates a new image on my desktop. Then the "bg" (background) is created and the image is correctly displayed. This only works 1 time however. After that, when I continue to press Add, nothing happens even though the Grid.png is getting changed on my desktop everytime I press "Add". The image just doesnt get updated somehow. The path always remains the same so I dont understand why it does not change the image to the new one?
I already tried to manually update the source with
self.bg.source = r"C:\Users\Max\Desktop\Grid.png"
but that does not do anything. I am pretty new to Kivy so I apologize if this gets asked alot.
Thank you for reading!
EDIT
I fixed it with this:
def Add(self, instance):
self.CellCount += self.cellAdd
Grid(self.CellCount, self.Width, self.Height)
with self.canvas:
self.canvas.clear()
self.bg =Image(source= r"C:\Users\Max\Desktop\Grid.png", pos=(0,0), size= (self.Width, self.Height))
self.bg.reload()
self.L = Label(text=str(self.CellCount)+" columns", pos=(500, 300))
I still dont know why Cache.remove() doesnt work as it seems logical to me but at least .reload() works good enough. Thank you for your answers!
Probably the image source is being cached by Kivy's image loader, so you need to inform it of the update. Try from kivy.cache import Cache and Cache.remove("kv.texture", your_filename) (or omit the your_filename argument to clear the whole texture cache).

Display images in tkinter canvas

I'm getting some issues trying to creating canvas images, a class handles the creation of the images, and i want that the class creates as many images as the times that i call it.
My code is this
from tkinter import *
from random import *
canvas_width = 800
canvas_height = 800
master = Tk()
canvas = Canvas(master, width=canvas_width, height=canvas_height, bg="black")
canvas.pack()
def images():
for _ in range(3):
Image_creator().create_image()
class Image_creator:
def create_image(self):
start_x = randint(1, canvas_width//2)
start_y = randint(1, canvas_height//2)
img = PhotoImage(file="pac_inizio.png")
master.img = img
self.image = canvas.create_image(start_x, start_y, anchor=NW, image=img)
images()
mainloop()
Actually with this code is displayed only 1 of the 3 images, i think that the other 2 canvas images are created but without the image inside.
I tried to change the create_image function for create buttons instead of canvas images and understand if it was actually as i thought.
If u run the code with the modified function it diplays 3 buttons but only one with the image inside.
def create_image(self):
start_x = randint(1, canvas_width//2)
start_y = randint(1, canvas_height//2)
img = PhotoImage(file="pac_inizio.png")
master.img = img
self.image = Button( anchor=NW, image=img)
self.image.place(x=start_x, y=start_y)
I think that the issue is in the image reference but don't know how to solve it
You seem to be aware of the common problem of images in tkinter being garbage collected although they are still being used somewhere, unless there's an explicit reference to that images. Note, however, that with master.img = img you will always only have a reference to one of the three (identical) images. If those images are meant to be different in your actual code, make master.img a list (or use any other list in the global score to store the images).
However, since the images are the same, you could also just load the image once directly in the global scope, not in the method, and then use that image. Also, there's not much point in having that class just for that one method (more so since the instance is also garbage collected in the loop).
img = PhotoImage(file="pac_inizio.gif") # create once in global score
def create_image(): # regular function, not method
start_x = randint(1, canvas_width//2)
start_y = randint(1, canvas_height//2)
canvas.create_image(start_x, start_y, anchor=NW, image=img)

Returning value when instance "called"

I want a certain function callable on a class. Something similar to:
class foo():
def __init__(self):
self.img = pygame.Surface((20, 20))
def magicfunction(self):
return self.img
bar = foo()
screen = pygame.display.set_mode((200, 200))
screen.blit(bar)
Which magic-function do I have to use?
If I understand you correctly, you want to create a class of your own, which is also a surface. That sounds exactly like inheritance! Try making foo a child of pygame.Surface:
class foo(pygame.Surface):
def __init__(self):
pygame.Surface.__init__(self, (20, 20))
more_data = "You should be able to extend this class freely"

Resizing Image and fit it to the Canvas size

Can anyone help me to resize the image using ImageTk?
I have a canvas and I will put pictures there.
I have different kinds of pictures = different sizes for all pictures
And when I attach the picture (just one) in the canvas, I want the picture's size to resize so that it will fit in the canvas and it will still maintain its proportion.
Please help me! I am new in PIL, Tkinter,and Python.
Update:
I tried using thumbnail under Image but in resizing:
self.image.thumbnail(self.picsize,Image.ANTIALIAS)
the image is not fitting the canvas size and if the image is longer/wider than the canvas, it is just cut. (Not resizing to fit into the canvas)
Code:
from PIL import ImageTk
from Tkinter import *
import os,tkFileDialog,Image
picsize = 250,250 # I want to set this so that it will fit in the self.imagecanvas | Every images attached will share same Size
imagepath = "Default_ProPic.jpg"
class GUI():
global picsize
def display(self):
self.canvas = Canvas(width=1200,height=700)
self.canvas.pack()
self.imagecanvas = Canvas(self.canvas,width=400,height=400)
self.imagecanvas.place(x=980,y=180)
self.image = Image.open(imagepath)
self.image.thumbnail(picsize,Image.ANTIALIAS)
self.newimage = ImageTk.PhotoImage(self.image)
self.profile_picture=self.imagecanvas.create_image(0,0,anchor = NW,image=self.newimage)
attachbutton = Button(self.canvas,text=" Profile Pic ",command=lambda:self.attachpic())
attachbutton.place(x=1030,y=320)
mainloop()
def attachpic(self):
global picsize
attachphoto = tkFileDialog.askopenfilename(title="Attach photo")
self.image = Image.open(attachphoto)
self.image.thumbnail(picsize,Image.ANTIALIAS)
self.newimage = ImageTk.PhotoImage(self.image)
self.imagecanvas.itemconfigure(self.profile_picture, image=self.newimage)
GUI = GUI()
GUI.display()
Picture used above:
Try saving the thumbnail as a separate variable:
self.thmb_img = self.image.thumbnail(picsize, Image.ANTIALIAS)
I suspect it may be taking the original self.image = Image.open(attachphoto)
I would suggest watching what sizing is going on with:
def attachpic(self):
picsize = 250, 250
attachphoto = tkFileDialog.askopenfilename(title="Attach photo")
self.image = Image.open(attachphoto)
print self.image.size()
self.thmb_img = self.image.thumbnail(picsize,Image.ANTIALIAS)
print self.thmb_img.size()
Check the output size and verify it is the same as the original and the desired (250, 250) thumbnail.

threading and tkinter

I'm making an image manipulation class to be used in an intro CS course, using PIL for image manipulation and Tkinter to show the picture. In order for the users to be able to see the picture while manipulating it, I have the graphics operations running on a separate thread, using code similar to this question. This seems to be working (i.e., nothing crashes), but I can't get the image to display -- Tk is starting up, but no window comes up. The code looks like this:
self.root = Tk.Toplevel()
self.frame = tk.Frame(self.root, width=self.image.size[0], height=self.image.size[1])
img = ImageTk.PhotoImage(self.image)
self.label = tk.Label(self.frame, image=img)
self.label.image = img
self.label.pack()
self.frame.pack()
tick()
self.root.mainloop()
The tick function is similar to the one in the linked question. I suspect that my problem is due a a misunderstanding of Tkinter, but I really have no idea.
Also, I can't seem to get the program to exit nicely -- even if I set daemon=True when I construct the Thread which this is running on, I still have to hit C-c when I'm finished. That looks kinda ugly, and I'd rather not bother the students with spurious error messages.
EDIT: Here's some more code.
class Picture():
##
# Constructor. Creates Picture either by passing in the path of an image file as param
# or by passing in a tuple in the format of (x, y) to indicate the size of a blank image.
# Example 1: Picture("image.jpg") constructs Picture with image.jpg
# Example 2: Picture((500, 500)) constructs Picture with a blank image of size 500*500
def __init__(self, param):
# Check if parameter is the right type, because we can't
# overload functions
if isinstance(param, tuple) and len(param) == 2:
self.image = Image.new('RGB', (param))
elif isinstance(param, str):
self.image = Image.open(param)
else:
raise TypeError('Parameter to Picture() should be a string or 2-tuple!')
# Default values for pen
self.pen_color = (0, 0, 0)
self.pen_position = (0, 0)
self.pen_width = 1.0
self.pen_rotation = 0
# Pixel data of the image
self.pixel = self.image.load()
# Draw object of the image
self.draw = ImageDraw.Draw(self.image)
# The main window, and associated widgets.
self.root = None
self.label = None
self.frame = None
# Threading support, so that we can show the image while
# continuing to draw on it.
self.request_queue = queue.Queue()
self.result_queue = queue.Queue()
self.thread = threading.Thread(target=self._thread_main)
self.thread.start()
def _thread_main(self):
"""
Runs the main Tkinter loop, as well as setting up all the
necessary GUI widgets and whatnot. By running Tkinter on
a separate thread, we can keep the picture displaying
even after the user's program is finished drawing on it.
"""
def tick():
"""
Called whenever Tk's main loop is idle. This lets us perform
drawing operations on the right thread.
"""
try:
f, args, kwargs = self.request_queue.get_nowait()
except queue.Empty:
pass
else:
value = f(*args, **kwargs)
self.result_queue.put(value)
self.root.after_idle(tick)
self.root = tk.Toplevel()
self.frame = tk.Frame(self.root, width=self.image.size[0], height=self.image.size[1])
img = ImageTk.PhotoImage(self.image)
self.label = tk.Label(self.frame, image=img)
# This line ensures that Python doesn't try to garbage collect
# our photo, due to a bug in Tk.
self.label.image = img
self.label.pack()
self.frame.pack()
tick()
self.root.mainloop()
def _submit_operation(self, f, *args, **kwargs):
"""
Submits an operation to the request queue. The arguments
should consist of a function, any positional arguments
to said function, and any keyword arguments to the function.
If f returns a value, that value will be returned.
Any function that does something with the picture (i.e.,
saving it, drawing to it, reading from it, etc.) should
be called only by submitting it to the queue.
"""
self.request_queue.put((f, args, kwargs))
return self.result_queue.get()
##
# Display the picture.
def display(self):
def display_func():
img = ImageTk.PhotoImage(self.image)
self.label.configure(image=img)
self.label.image = img
self.label.pack()
self.frame.pack()
self._submit_operation(display_func)

Categories

Resources