How to display a Numpy matrix, as a bitmap, into a Tkinter canvas?
More precisely, how to fill a PhotoImage with content from a matrix?
photo = ImageTk.PhotoImage(...)
self.canvas.create_image(0,0,image=photo,anchor=Tkinter.NW)
Here is working solution, slightly modified to make it work (some function was deprecated) and to simplify it to keep only the necessary part. We have to use Image.frombytes(...) to read the data in the numpy matrix.
import Tkinter
from PIL import Image, ImageTk
import numpy
class mainWindow():
def __init__(self):
self.root = Tkinter.Tk()
self.frame = Tkinter.Frame(self.root, width=500, height=400)
self.frame.pack()
self.canvas = Tkinter.Canvas(self.frame, width=500,height=400)
self.canvas.place(x=-2,y=-2)
data=numpy.array(numpy.random.random((400,500))*100,dtype=int)
self.im=Image.frombytes('L', (data.shape[1],data.shape[0]), data.astype('b').tostring())
self.photo = ImageTk.PhotoImage(image=self.im)
self.canvas.create_image(0,0,image=self.photo,anchor=Tkinter.NW)
self.root.update()
self.root.mainloop()
mainWindow()
Related
I am doing a painting program where one can draw things, change the background color and save it as a file on the computer. Everything works except that changing the background color takes way too much time.
This is the code:
from tkinter import *
import tkinter.filedialog as tk
from tkinter import Menu
from tkinter.colorchooser import askcolor
from tkinter.filedialog import asksaveasfile,askopenfilename
import os
from PIL import Image as im
from PIL import ImageTk,ImageDraw,ImageColor
class P:
x=y=None
image=None
my_image=im.new("RGB",(600,600),(255,255,255))
draw=ImageDraw.Draw(my_image)
def __init__(self,window):
self.window = window
self.upper_frame = Frame(window)
self.upper_frame.grid(row=0,column=0, padx=10, pady=5,sticky="ew")
self.lower_frame = Frame(window)
self.lower_frame.grid(row=2, column=0, padx=10, pady=5,sticky="ew")
self.canvas= Canvas(self.lower_frame,width=700,height=530,bg="white")
self.canvas.grid()
self.bg = Button(self.upper_frame,text="Background",command= self.bgcolor) #change bg color
self.bg.grid(row=2,column=1,padx=(100,10))
self.upper_menu()
def bgcolor(self):
chosen_color = askcolor(color=self.canvas["bg"])[1]
self.canvas.configure(bg=chosen_color)
color_RGB = ImageColor.getcolor(chosen_color, "RGB")
img = self.my_image
for i in range(0,600):#pixels in width
for j in range(0,600): #height = 600 pix
data = img.getpixel((i,j)) #gives color of pixel
if (data[0]==255 and data[1]==255 and data[2]==255): #data represent RGB color
img.putpixel((i,j),color_RGB) #changes pixel color
def save_pic(self,event=None): #save image on comp.
my_file=asksaveasfile(mode="w",defaultextension=".png",filetypes=[("png files","*.png")],
initialdir="/home/b/",parent=window)
if my_file is not None:
path=os.path.abspath(my_file.name)
self.my_image.save(path)
def upper_menu(self):
self.menubar = Menu(window)
self.menu1 = Menu(self.menubar, tearoff=0)
self.menu1.add_command(label="Save pic", command=self.save_pic)
self.menu1.add_separator()
self.menu1.add_command(label="Exit", command=window.destroy)
self.menubar.add_cascade(label="Settings", menu=self.menu1)
self.menu2 = Menu(self.menubar, tearoff=0)
self.window.config(menu=self.menubar)
window = Tk()
window.geometry("720x590")
p = P(window)
window.mainloop()
I use the method bgcolor to change the background. How to make it work faster?
I suspect the problem is with calling putpixel 360,000 times. Instead, try creating the color data in the loop and then call putdata once after the loops have finished.
I'm not overly familiar with PIL, but this makes a huge difference when doing similar things with the tkinter PhotoImage class: doing one pixel at a time is slow, doing an array of pixels is fast.
I am trying to build a GUI calculator with tabbed pages using tkk.Notebook. The different pages consist of objects inheriting from the ttk.Frame widget. I am able to create the frames but they end up being very small and the widgets I have placed on them overlap and impede on each other. Using the width and height options with the frames does not seem to change the dimensions at all either. I am fairly new to object oriented programming, so I think the issue has to do with how I am creating the frames, but I cant seem to figure it out.
import tkinter as tk
from tkinter import ttk
from ttkthemes import ThemedStyle
class DimFrame(ttk.Frame):
def __init__(self, master):
super().__init__(master)
self.length_label = ttk.Label(self, text='Length: ')
self.length_label.grid(row=0)
self.length = float()
self.length_entry = ttk.Entry(self, textvariable=self.length, width=10)
self.length_entry.grid(column=1,row=0)
...more methods and widgets, etc...
def main():
root = tk.Tk()
root.geometry('1000x500')
s = ttk.Style()
style = ThemedStyle(root)
style.set_theme('black')
notebook = ttk.Notebook(root)
notebook.grid()
dimensions_frame = DimFrame(root)
dimensions_frame.grid()
notebook.add(dimensions_frame, text='Room Dimensions')
root.mainloop()
if __name__ == '__main__':
main()
I am new to python and am trying to create an application that displays different information like a clock, current news, notice board etc.
I got the clock to work however I am encountering a few bugs. One is that a smaller window launches when the application does. I thought this was something to do with the self.root = tk.Tk() in the initialisation however doing anything to this line produces errors.
The other bug is that while the background image (0.png) used to fill up the entire screen as it is the same size as my monitor, when I added the clock to the application, the image is shifted to the bottom right of the screen, leaving a small white line to the top and left of the screen. I have tried to fix this by messing with the panel.pack changing it to grid and place however both of this did nothing to the lines. I feel like something is overwriting this line.
None of these bugs are showing up in the console and I don't know what to do. Here is the code I am running:
from tkinter import *
from PIL import ImageTk, Image
import os
import time
import tkinter as tk
class App(Tk):
def __init__(self):
self.root = tk.Tk()
self.label = tk.Label(text="",font=('comic',50,'bold'),bg = '#464545',fg = '#1681BE')
self.label.place(height = 206,width = 487, x = 1384, y = 824)
self.update_clock()
self.root.mainloop()
def update_clock(self):
now = time.strftime('%H:%M:%S')
self.label.configure(text=now)
self.root.after(1000, self.update_clock)
root = Tk()
img = ImageTk.PhotoImage(Image.open("0.png"))
panel = Label(root, image = img)
panel.pack()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.overrideredirect(1)
app = App()
root.geometry("%dx%d+0+0" % (w, h))
root.mainloop()
I hope someone can find what's wrong with it because I certainly can't!
Since your App class inherit from Tk, you don't need to create another root window. So I gathered all your code inside the App class. When I use an image the side of my screen, I don't see any line at the top or at the left of the screen, so I hope it will work for you as well.
from PIL import ImageTk, Image
import os
import time
import tkinter as tk
class App(tk.Tk):
def __init__(self):
# call the __init__ method of Tk class to create the main window
tk.Tk.__init__(self)
# background image
img = ImageTk.PhotoImage(Image.open("0.png"))
panel = Label(self, image=img)
panel.pack()
# clock
self.label = tk.Label(self, text="", font=('comic',50,'bold'),
bg='#464545', fg='#1681BE')
self.label.place(height=206, width=487, x=1384, y=824)
self.update_clock()
# window geometry
w, h = self.winfo_screenwidth(), self.winfo_screenheight()
self.geometry("%dx%d+0+0" % (w, h))
self.overrideredirect(True)
self.mainloop()
def update_clock(self):
now = time.strftime('%H:%M:%S')
self.label.configure(text=now)
self.after(1000, self.update_clock)
app = App()
I am trying to place the image in the same window but it opens up in a new window and I can't figure out why. I've tried google but couldn't find anything to help me. How do I specify the image should be placed in the initial window that opens up instead of its own separate window?
import Tkinter as tk
import io
import base64
# Import the function for downloading web pages
from urllib import urlopen
# Import the regular expression function
from re import findall
# Import the Tkinter functions
from Tkinter import *
# Import Python's HTML parser
from HTMLParser import *
class MainWindow(tk.Frame):
root = tk.Tk()
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
def createimage(self):
# a little more than width and height of image
w = 520
h = 320
x = 80
y = 100
# use width x height + x_offset + y_offset (no spaces!)
# this GIF picture previously downloaded from tinypic.com
image_url = "http://i46.tinypic.com/r9oh0j.gif"
image_byt = urlopen(image_url).read()
image_b64 = base64.encodestring(image_byt)
self.photo = tk.PhotoImage(data=image_b64)
# create a white canvas
#topframe = Frame(root)
cv = tk.Canvas(bg='white')
cv.pack(side='top', expand='yes')
# put the image on the canvas with
# create_image(xpos, ypos, image, anchor)
cv.create_image(10, 10, image=self.photo, anchor='nw')
if __name__ == "__main__":
root = tk.Tk()
main = MainWindow(root)
root.geometry("400x300")
main.pack(side="top", fill="both", expand=True)
main.createimage()
root.mainloop()
1. Why does it open up in a separate window?
You need to pass a master to each constructor. This should be the widget (Tk, Frame, Toplevel, Canvas, ...) that the new widget should be placed inside. Like this:
tk.Canvas(master = self, g='white')
tk.PhotoImage(master = self, data=image_b64)
When the master is destroyed, widgets with it as master are also destroyed.
2. In which window does it open up?
Tkinter has the default root, the first window which is created. This window is used for any widget you do not pass a master to.
i.e. the first root = tk.Tk()
i folks,
I'm trying to use the subsample method in the PhotoImage class to resize an image I have in a list that links to a Label widget in Tkinter but python says there is no such method. I know I could use Image.resize before calling Photoimage but since I'd like to resize the image at any time I don't know what to do once it's been converted to PhotoImage. Could someone please show me what I'm doing wrong? I'm relatively new to Tkinter and very new to PIL...Thanks...
from Tkinter import *
from PIL import Image, ImageTk
class imgList(object):
def __init__(self,imgs):
self.list=[]
for i in imgs:
image=Image.open(i)
photo=ImageTk.PhotoImage(image)
self.list.append(photo)
def resize(self,num,fact):
self.list[num]=self.list[num].subsample(fact)
#image=image.resize((50,100),Image.ANTIALIAS)
#photo=ImageTk.PhotoImage(image)
#photo.subsample(2,2)
#self.list[num]=photo
class Slot(object):
def __init__(self,root,canvas,appimg,x,y):
self.root=root
self.canvas=canvas
self.appimage=appimg
self.l=Label(self.root,image=self.appimage)
self.l.place(x=x,y=y)
class View(object):
def __init__(self,canvas):
self.canvas=canvas
class win1(View):
def slotbind(self,event):
print("heloo")
self.imglist.resize(0,2)
def draw(self,root,tv):
self.canvas.delete(ALL)
self.root=root
#self.photolist=pl
self.imglist=imgList(["pic1.bmp"])
self.tv=tv
self.s1=Slot(self.root,self.canvas,self.imglist.list[0],10,10)
self.s1.l.bind("<1>",self.slotbind)
self.qbtn=Button(self.root,text="quit",command=self.quit)
self.qbtn.place(x=270,y=100)
self.qbtn.lift()
def quit(self):
self.qbtn.destroy()
self.s1.l.destroy()
self.tv[1].draw(self.root,self.tv)
class win2(View):
def draw(self,root,tv):
self.canvas.delete(ALL)
self.root=root
self.tv=tv
imglist=imgList(["pic3.bmp","pic4.bmp"])
self.s1=Slot(self.root,self.canvas,imglist.list[1],270,10)
self.qbtn=Button(self.root,text="quit2",command=self.quit)
self.qbtn.place(x=500,y=100)
self.qbtn.lift()
def quit(self):
self.qbtn.destroy()
self.s1.l.destroy()
self.tv[0].draw(self.root,self.tv)
class win3(View):
def draw(self):
pass
class App(object):
def __init__(self, width=640, height=480):
self.width = width
self.height = height
self.root = Tk()
self.root.title("tkinter_test01")
self.root.geometry("%sx%s"%(self.width, self.height))
self.canvas = Canvas(self.root, width=self.width, height=self.height)
self.theviews=[win1(self.canvas),win2(self.canvas),win3(self.canvas)]
self.theviews[0].draw(self.root,self.theviews)
self.canvas.pack()
self.root.mainloop()
app=App()
Bad news, subsample and zoom are not available in ImageTk.PhotoImage, and only in Tkinter.PhotoImage. The later only accepts PPM, PGM, GIF, and if you are using Python with Tk 8.6b2 or later (very unlikely at this time) then there is also support for PNG images.
Maybe this code will solve the problem - I tried it succesfully:
# http://effbot.org/imagingbook/image.htm
import Tkinter
# from PIL
import Image, ImageTk
im = Image.open('z:/g1.jpg')
# imc=im.copy()
imc=im.transpose(Image.ROTATE_270)
(x0,y0,x1,y1)=imc.getbbox() # returns (0,0,w,h)
print 'x0=%d y0=%d x1=%d y1=%d\n' % (x0,y0,x1,y1)
tkroot=Tkinter.Tk()
# tki=ImageTk.PhotoImage(im)
# tkl=Tkinter.Label(tkroot, image=tki)
# tkl.pack()
imc.thumbnail((1+x1/5,1+y1/5)) # changes image in place!
tkic=ImageTk.PhotoImage(imc)
tklc=Tkinter.Label(tkroot,image=tkic)
tklc.pack()
tkroot.mainloop() # Start the GUI