this is my first post in stackoverflow, be patient :D
I want to build a little picture editor with python/kivy.
It is my first time using kivy. I got some experience in python.
I don´t know how to update/reload an added image (widget). For example if I want to setup the contrast of a picture, the picture needs to be reloaded. I want to avoid saving pictures to disk, thats why I use BytesIO -> speed.
My solution right now is to delete the old image(widget) and add a new one. But I get in trouble if want to add other widgets, like buttons. So right now the code only works with one widget :-/
I tried also with kv language, but while I use BytesIO for processing my Image I cant use the kv/id function. Source only accepts string (path/files). But I got ByteArray.
Please take a look at my code, may you can show me how to access an added widget and update it later on in the running app. Or maybe there is a solution with kv and BytesIO?
Most of the code is gathered from web and put together...
from kivy.app import App
from kivy.uix.image import Image as UixImage
import rawpy
import imageio
from PIL import Image, ExifTags, ImageEnhance
import colorcorrect.algorithm as cca
from colorcorrect.util import from_pil, to_pil
from kivy.core.image import Image as CoreImage
import io
from kivy.clock import Clock
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
import kivy.uix.button as btn
import time
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import NumericProperty, ObjectProperty
# used .raf (Fujifilm Raw) in this project
# https://filesamples.com/samples/image/raf/sample1.raf
class MyImageWidget(GridLayout):
def __init__(self,**kwargs):
super(MyImageWidget,self).__init__(**kwargs)
self.cols = 1 #1 column grid layout
self.contrast_counter = 0 # counter for contrast sweep
# Load Image (+standard features)
self.thumbnail_path = self.create_thumbnailpath() # set path to thumbnail (create thumbnail)
self.image = Image.open(self.thumbnail_path) # open thumbnail
self.image = self.set_size(self.image, 0.1) # minimize thumbail size for speed
self.image = self.whitebalance_auto(self.image) # do whitebalance on thumbnail
self.image = self.set_greyscale(self.image) # do black white conversion
# add Image to widget (kivy_image)
self.kivy_image = UixImage(source=self.thumbnail_path)
self.add_widget(self.kivy_image) # <--- how can i access this widget later?
# here a object is creatd for the widget, but how do I access it later on?
print(self.children)
# TEST: reload Image in cycles (to see changing contrast effect on picture)
Clock.schedule_interval(self.update_pic,0.025) # refreshrate of app (25ms)
def update_pic(self, dt):
new_image = self.process_image(self.image)
self.remove_widget(self.kivy_image) # current widget need to be removed, so that contrast sweep is shown
self.kivy_image = new_image
self.add_widget(self.kivy_image) # new processed widget need to be added, so that contrast sweep is shown
#self.kivy_image.reload() # <--- how can i reload the widget (kivy_image), to not need to remove the old widget?
#print("reloaded")
def create_thumbnailpath(self):
# https://filesamples.com/samples/image/raf/sample1.raf
path = 'lab2raw/sample1.raf'
with rawpy.imread(path) as raw_image:
thumbnail_path = self.create_thumbnail(raw_image)
return thumbnail_path
def process_image(self,image):
self.contrast_counter = self.contrast_counter + 0.02 # cycle through contrast levels 0.0 - 2.0
if self.contrast_counter >= 2:
self.contrast_counter = 0 # reset contrast level cycle
#image = self.set_size(image, 0.2)
#image = self.whitebalance_auto(image)
#print_exif(image)
#image = self.set_greyscale(image)
#image = self.set_contrast(image,1.1)
image = self.set_contrast(image,self.contrast_counter)
#image = self.set_sharpness(image, 2.0)
image_processed = self.create_kivy_image(image, 'png')
return image_processed
def create_kivy_image(self,image, file_extension):
imgByteArr = io.BytesIO()
image.save(imgByteArr, file_extension)
# Return a Kivy image set from a bytes variable
imgByteArr.seek(0)
buf = io.BytesIO(imgByteArr.read())
buf.seek(0)
cim = CoreImage(buf, ext=file_extension)
image_to_show = UixImage(source='')
image_to_show.texture = cim.texture
return image_to_show
def whitebalance_auto(self,im):
image = to_pil(cca.retinex_with_adjust(from_pil(im)))
return image
def create_thumbnail(self,raw_image):
# Create JPG Thumbnail
try:
thumb = raw_image.extract_thumb()
except rawpy.LibRawNoThumbnailError:
print('no thumbnail found')
except rawpy.LibRawUnsupportedThumbnailError:
print('unsupported thumbnail')
else:
if thumb.format == rawpy.ThumbFormat.JPEG:
with open('lab2raw/thumb.jpg', 'wb') as file:
file.write(thumb.data)
return 'lab2raw/thumb.jpg'
elif thumb.format == rawpy.ThumbFormat.BITMAP:
imageio.imsave('lab2raw/thumb.tiff', thumb.data)
return 'lab2raw/thumb.tiff'
def print_exif(self,image):
img_exif = image.getexif()
if img_exif is None:
print('Sorry, image has no exif data.')
else:
for key, val in img_exif.items():
if key in ExifTags.TAGS:
if ExifTags.TAGS[key]=="XMLPacket":
pass
elif ExifTags.TAGS[key]=="PrintImageMatching":
pass
else:
print(f'{ExifTags.TAGS[key]}:{val}')
def set_greyscale (self,im):
image = im.convert("L")
return image
def set_contrast (self,im, contrast_level):
enhancer = ImageEnhance.Contrast(im)
return_image = enhancer.enhance(contrast_level)
return return_image
def set_sharpness (self,im, sharpness_level):
enhancer = ImageEnhance.Sharpness(im)
image = enhancer.enhance(sharpness_level)
return image
def set_size(self,im, size_factor):
width, height = im.size
im = im.resize((round(width*size_factor),round(height*size_factor)),Image.ANTIALIAS)
return im
def save_jpg(self,im,quality, optimize, path):
im.save(path,optimize=optimize, quality=quality)
class MyApp(App):
def build(self):
return MyImageWidget()
if __name__ == "__main__":
MyApp().run()
You don't have to delete self.kivy_image and create it again but you have to replace .texture instead of full Image
self.kivy_image.texture = new_image.texture
And this means you can also reduce code in create_kivy_image. You can return CoreImage instead of UixImage. You may also use one io.BufferIO
def create_kivy_image(self, image, file_extension):
buf = io.BytesIO()
image.save(buf, file_extension)
buf.seek(0)
return CoreImage(buf, ext=file_extension)
def update_pic(self, dt):
new_image = self.process_image(self.image)
self.kivy_image.texture = new_image.texture
You could also use io.BytesIo() to create thumbnail and you wouldn't have to write in file. I could test it only with JPG but I don't know if it is correct for BITMAP
def raw_to_pil(self, path):
"""Load RAW and convert to PIL.Image."""
with rawpy.imread(path) as raw_image:
try:
thumb = raw_image.extract_thumb()
except rawpy.LibRawNoThumbnailError:
print('no thumbnail found')
except rawpy.LibRawUnsupportedThumbnailError:
print('unsupported thumbnail')
else:
if thumb.format == rawpy.ThumbFormat.JPEG:
print('thumbnail JPG')
return Image.open(io.BytesIO(thumb.data))
elif thumb.format == rawpy.ThumbFormat.BITMAP:
print('thumbnail BITMAP')
buf = io.BytesIO()
imageio.imsave(buf, thumb.data, 'tiff')
buf.seek(0)
return Image.open(buf)
My full code for tests:
import io
import time
import rawpy
import imageio
from PIL import Image, ExifTags, ImageEnhance
import colorcorrect.algorithm as cca
from colorcorrect.util import from_pil, to_pil
from kivy.app import App
from kivy.clock import Clock
from kivy.core.image import Image as CoreImage
from kivy.uix.image import Image as UixImage
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.properties import NumericProperty, ObjectProperty
# used .raf (Fujifilm Raw) in this project
# https://filesamples.com/samples/image/raf/sample1.raf
class MyImageWidget(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 1 # 1 column grid layout
self.contrast_counter = 0 # counter for contrast sweep
# add Image to widget (kivy_image)
self.kivy_image = UixImage() # empty image
self.add_widget(self.kivy_image)
self.image = self.raw_to_pil('lab2raw/sample1.raf')
if self.image: # can be `None`
self.image = self.set_size(self.image, 0.1) # minimize thumbail size for speed
#self.image = self.whitebalance_auto(self.image) # do whitebalance on thumbnail
self.image = self.set_greyscale(self.image) # do black white conversion
core_image = self.pil_to_kivy(self.image, 'png')
self.kivy_image.texture = core_image.texture
# TEST: reload Image in cycles (to see changing contrast effect on picture)
Clock.schedule_interval(self.update_pic, 0.025) # refreshrate of app (25ms)
def update_pic(self, dt):
if self.image: # can be `None`
pil_image = self.process_image(self.image)
core_image = self.pil_to_kivy(pil_image, 'png')
self.kivy_image.texture = core_image.texture
def process_image(self, image):
"""Process PIL.Image without converting to other formats."""
self.contrast_counter += 0.02 # cycle through contrast levels 0.0 - 2.0
if self.contrast_counter >= 2:
self.contrast_counter = 0 # reset contrast level cycle
#image = self.set_size(image, 0.2)
#image = self.whitebalance_auto(image)
#image = self.set_greyscale(image)
#image = self.set_contrast(image, 1.1)
image = self.set_contrast(image, self.contrast_counter)
#image = self.set_sharpness(image, 2.0)
#self.print_exif(image)
return image
def pil_to_kivy(self, image, file_extension):
"""Convert PIL.Image to CoreImage (to get later .texture)."""
buf = io.BytesIO()
image.save(buf, file_extension)
buf.seek(0)
return CoreImage(buf, ext=file_extension)
def raw_to_pil(self, path):
"""Load RAW and convert to PIL.Image. Can return `None`."""
with rawpy.imread(path) as raw_image:
try:
thumb = raw_image.extract_thumb()
except rawpy.LibRawNoThumbnailError:
print('no thumbnail found')
except rawpy.LibRawUnsupportedThumbnailError:
print('unsupported thumbnail')
else:
if thumb.format == rawpy.ThumbFormat.JPEG:
print('thumbnail JPG')
buf = io.BytesIO(thumb.data)
return Image.open(buf)
elif thumb.format == rawpy.ThumbFormat.BITMAP:
print('thumbnail BITMAP')
buf = io.BytesIO()
imageio.imsave(buf, thumb.data, 'tiff')
buf.seek(0)
return Image.open(buf)
#return Image.new('RGB', (1500, 1500), 'white') # create empty Image
def print_exif(self, im):
im_exif = im.getexif()
if im_exif is None:
print('Sorry, image has no exif data.')
else:
for key, val in im_exif.items():
if key in ExifTags.TAGS:
if ExifTags.TAGS[key] == "XMLPacket":
print('pass (XMLPacket)')
pass
elif ExifTags.TAGS[key] == "PrintImageMatching":
print('pass (PrintImageMatching)')
pass
else:
print(f'{ExifTags.TAGS[key]}:{val}')
# ---
def whitebalance_auto(self, im):
return to_pil(cca.retinex_with_adjust(from_pil(im)))
def set_greyscale(self, im):
return im.convert("L")
def set_contrast(self, im, contrast_level):
return ImageEnhance.Contrast(im).enhance(contrast_level)
def set_sharpness(self, im, sharpness_level):
return ImageEnhance.Sharpness(im).enhance(sharpness_level)
def set_size(self, im, factor):
return im.resize((round(im.width*factor), round(im.height*factor)), Image.ANTIALIAS)
def save_jpg(self, im, quality, optimize, path):
im.save(path, optimize=optimize, quality=quality)
class MyApp(App):
def build(self):
return MyImageWidget()
if __name__ == "__main__":
MyApp().run()
I've got a couple of images in a folder and each of these gets resized and turned into an image for Tkinter. I want to use these images in other python scripts as a enum.
This is how I create the images:
import os
from PIL import Image, ImageTk
from enum import Enum
#Resize All Images and turn them into TkImages
def ResizeImages():
PieceImages = []
for images in os.listdir('Resources'):
cImage = Image.open('Resources/' + images)
cImage = cImage.resize((80, 80), Image.ANTIALIAS)
PieceImages.append(ImageTk.PhotoImage(cImage))
return PieceImages
#Called from outside to create PNGS list AFTER Tkinter.Tk() was called
def InitializeImages():
global PNGS
PNGS = ResizeImages()
#This is the part where I'm lost
class Black(Enum):
global PNGS
Queen = PNGS[3]
King = PNGS[4]
Tower = PNGS[5]
I want to create a Tkinter Button with these images on like this:
#PieceImages is the .py file above
import PieceImages import Black
form tkinter import *
root = Tk()
root.geometry('800x800')
someButton = tk.Button(root, image=Black.Tower).place(x=100, y=100)
My Question is:
Is it even possible to create enums of this type and if so, how?
I'd recommend staying away from global variables; you can pass arguments to a class directly:
class Black():
def __init__(self, pngs):
self.name1 = pngs[0]
self.name2 = pngs[1]
Instantiate by passing pngs:
black = Black(pngs)
Then black.name1, black.name2 etc will be your desired images.
I have a tk.Label object with an image (via tk.Label(self.root,image='image.png')) and want to add a color filter to it, i.e. make the image more red. How can I do that?
You can modify the image with PIL, and save it as a new file. In Tkinter, you can use PhotoImage(file="file.png") to use the image as a button:
# importing image object from PIL
from PIL import ImageOps, Image
import PIL.Image
# creating an image object
img = Image.open(r"test.png").convert("L")
# image colorize function
img = ImageOps.colorize(img, black="red", white="white")
# save the image as new.png
img.save("new.png")
from tkinter import *
root = Tk()
# set the img1 variable as the image we just created
img1 = PhotoImage(file="new.png")
b = Button(image=img1)
b.pack()
root.mainloop()
I am trying to code for make a registration System and Identity Card printer first form done with making data import to database using sqlite3 database and tkinter GUI.this form for get data from database and write that data text on png image.that png image after written data it is the identity card.
**so this is the problem..code run without any errors.but image not written data.for try this code firstly i didn't use database data.i used data from string variable **
import sqlite3
from tkinter import Tk, Button, Canvas
from PIL import Image, ImageFont, ImageDraw
connection = sqlite3.connect("school.db")
tao = Tk()
tao.title("Mayurapada Central Collage")
tao.configure(bg = '#6699ff')
canvas = Canvas(tao,width = 600,height = 400,bg = '#6699ff')
def imgs():
img = Image.open("C:\\Users\\TAO\\Desktop\\New\\02.png")
#img.show()
str01 = "Hello World"
font = ImageFont.truetype("arial.ttf",200)
w,h = font.getsize(str01)
print(str01)
draw = ImageDraw.Draw(img)
draw.text(((900-w)/2,(900-h)/2),str01,font = font,fill = "black")
img.show()
button01 = Button(tao,text = "Preview",bd = 7,padx = 5,pady = 5,command =
imgs).place(x = 50,y = 300)
canvas.pack()
tao.mainloop()
I made a graph in matplotlib, and wanted to make it in to an image and use it in my pyqt5 application. Someone suggested I use BytesIO for this. This is my code so far:
Drawing my graph:
...
plt.axis('equal')
buff = io.BytesIO()
plt.savefig(buff, format="png")
print(buff)
return buff
This is then called in another script:
def minionRatioGraphSetup(self, recentMinionRatioAvg):
image = minionRatioGraph(recentMinionRatioAvg)
label = QtWidgets.QLabel()
pixmap = QtGui.QPixmap(image)
label.setPixmap(pixmap)
label.setGeometry(QtCore.QRect(0,0,200,200))
It stops working at pixmap = QtGui.QPixmap(image) and I'm unsure why. Also: How could I place this in my MainWindow? because I doubt the code there will work lol
I'm sure there is a solution using a buffer. However, it seems rather complicated to get the byte format correct. So an alternative is to save the image to disk, and then load it from there.
import sys
from PyQt4 import QtGui
import matplotlib.pyplot as plt
import numpy as np
def minionRatioGraph():
plt.plot([1,3,2])
plt.savefig(__file__+".png", format="png")
class App(QtGui.QWidget):
def __init__(self):
super(App, self).__init__()
self.setGeometry(300, 300, 250, 150)
self.setLayout(QtGui.QVBoxLayout())
label = QtGui.QLabel()
label2 = QtGui.QLabel("Some other text label")
minionRatioGraph()
qimg = QtGui.QImage(__file__+".png")
pixmap = QtGui.QPixmap(qimg)
label.setPixmap(pixmap)
self.layout().addWidget(label)
self.layout().addWidget(label2)
self.show()
if __name__ == '__main__':
app = QtGui.QApplication([])
ex = App()
sys.exit(app.exec_())
a snippet using pillow, might help avoiding file io
im = PIL.Image.open("filename")
with BytesIO() as f:
im.save(f, format='png')
f.seek(0)
image_data = f.read()
qimg = QImage.fromData(image_data)
patch_qt = QPixmap.fromImage(qimg)
Here's a solution that only uses the buffer, you need to use PIL to create an ImageQT and then load it to the QPixap
import matplotlib.pyplot as plt
import io
from PIL.ImageQt import ImageQt
from PIL import Image
...
buff = io.BytesIO()
plt.savefig(buff, format="png")
img = Image.open(buff)
img_qt = ImageQt(img)
return img_qt
Then, in your GUI setup, you call your function to return the ImageQT and generate the QPixmap using QPixmap.fromImage()
def minionRatioGraphSetup(self, recentMinionRatioAvg):
image = minionRatioGraph(recentMinionRatioAvg)
label = QtWidgets.QLabel()
pixmap = QtGui.QPixmap.fromImage(image)
label.setPixmap(pixmap)
label.setGeometry(QtCore.QRect(0,0,200,200))