I'm creating an app that can use a button to load and display an image. I don't understand how this would work with Python Gtk3+.
I want to load the next image into the GUI location where the first image is... a simple replacement.
image = Gtk.Image()
image.set_from_file(self.image)
grid.attach(image, 0, 2, 1, 1) #grid location
button = Gtk.Button("Load next image")
button.connect("clicked", self.load_image)
grid.attach(button, 2, 1, 1, 1) #grid location
button1 = Gtk.Button("Load next image")
button1.connect("clicked", self.load_new_image)
grid.attach(button1, 2, 2, 1, 1) #grid location
def load_image(self, widget):
self.image = 'image_path'
def load_new_image:
self.image = 'image_path'
I thought of Event Boxes, or something similar, but I'm kind of at a loss. The image section is only run once on instantiation, so I don't understand how it should get updated with events. I want the image to change if the self.image path name changes in another class method. Any ideas?
Maybe I am misunderstanding the question, but should not it be that simple?
I will explain the answer as #DanD. pointed out.
You just need to set the image path (self.image.set_from_file(img)) on the load_image method (connected with the button clicked signal) with the desired image.
Current Gtk.Image will display automatically the new loaded image.
import gi
import os
import sys
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class GridWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Grid Example")
grid = Gtk.Grid()
self.add(grid)
self.button = Gtk.Button(label="Button 1")
self.image = Gtk.Image()
grid.add(self.button)
grid.add(self.image)
self.button.connect("clicked", self.load_image)
self.count = 0
for root, _, files in os.walk(sys.argv[1]):
self.images = [os.path.join(root, f) for f in files]
def load_image(self, event):
img = self.images[self.count]
print(img)
self.image.set_from_file(img)
self.count = self.count + 1
win = GridWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
Related
The code provided here is:
import tkinter as tk
from PIL import Image, ImageTk
from pathlib import Path
class App(tk.Tk):
def __init__(self):
super().__init__()
self.geometry('600x600')
self.img_path = Path(r'D:\Python\Lena.jpg')
self.img = Image.open(self.img_path)
self.img_rgb = self.img.convert('RGB')
dim_x, dim_y = self.img_rgb.size
self.img_tk = ImageTk.PhotoImage(self.img_rgb.resize((dim_x, dim_y)))
self.canvas = tk.Canvas(self)
self.canvas.create_image(dim_x // 2, dim_y // 2, image=self.img_tk)
self.canvas.pack(expand=True, fill=tk.BOTH)
self.rgb_var = tk.StringVar(self, '0 0 0')
self.rgb_label = tk.Label(self, textvariable=self.rgb_var)
self.rgb_label.pack()
self.bind('<Motion>', lambda e: self.get_rgb(e))
def get_rgb(self, event):
x, y = event.x, event.y
try:
rgb = self.img_rgb.getpixel((x, y))
self.rgb_var.set(rgb)
except IndexError:
pass # ignore errors if the cursor is outside the image
if __name__ == '__main__':
app = App()
app.mainloop()
It displays an image with the RGB value of the pixel under the mouse pointer under the image (when the mouse pointer is over the image). The image used is this.
However, only the upper left quadrant of the image is displayed on the canvas. You can see that in the screenshot below.
How can I display the whole image and still have the RGB values of the pixel under the mouse pointer displayed (when the mouse pointer is over the image)?
I can see two possible solutions:
Expand image to fit window
Wrap window around image
To expand image to fit window
dim_x, dim_y = 600, 600
self.img_tk = ImageTk.PhotoImage(self.img_rgb.resize((dim_x, dim_y)))
OR
To wrap window around image
dim_x, dim_y = self.img_rgb.size
self.img_tk = ImageTk.PhotoImage(self.img_rgb)
Both approaches will display the entire image.
Here is the complete code with both options available via select flag.
import tkinter as tk
from PIL import Image, ImageTk
from pathlib import Path
class App(tk.Tk):
def __init__(self, select = True):
super().__init__()
self.img_path = Path('D:\Lenna.jpg')
self.img = Image.open(self.img_path)
self.img_rgb = self.img.convert('RGB')
if select:
# resize image to fit window
dim_x, dim_y = 600, 600
self.img_tk = ImageTk.PhotoImage(self.img_rgb.resize((dim_x, dim_y)))
else:
# resize window to fit image
dim_x, dim_y = self.img_rgb.size
self.img_tk = ImageTk.PhotoImage(self.img_rgb)
self.geometry(f'{dim_x}x{dim_y+21}')
self.canvas = tk.Canvas(self, borderwidth = 0, highlightthickness = 0)
self.canvas.create_image(0, 0, image = self.img_tk, anchor= tk.NW)
self.canvas.pack(expand=True, fill=tk.BOTH)
self.rgb_var = tk.StringVar(self, '0 0 0')
self.rgb_label = tk.Label(self, textvariable=self.rgb_var)
self.rgb_label.pack()
self.bind('<Motion>', lambda e: self.get_rgb(e))
def get_rgb(self, event):
x, y = event.x, event.y
try:
rgb = self.img_rgb.getpixel((x, y))
self.rgb_var.set(rgb)
except IndexError:
pass # ignore errors if the cursor is outside the image
if __name__ == '__main__':
app = App(False)
app.mainloop()
Everything works as expected when borderwidth and highlightthickness are removed.
I'm trying to get the label value that is inside a Grid and that grid is inside a button after a click event.
This is my part of code:
for one_text in text_list:
label_for_button = Gtk.Label(one_text)
label_for_button.set_line_wrap(True)
image_for_button = Gtk.Image.new_from_file("img.png")
grid_in_button = Gtk.Grid()
grid_in_button.add(image_button)
grid_in_button.attach_next_to(label_for_button, image_for_button, Gtk.PositionType.BOTTOM, 1, 2)
grid_in_button.show_all()
button.add(grid_in_button)
button.connect("clicked", self.on_button_clicked)
def on_button_clicked(self, widget):
# here i wanna get the value of the label_for_button
Help.. Any idea? Thanks
hope this code helps:
import gi
gi.require_version('Gtk','3.0')
from gi.repository import Gtk,GdkPixbuf
def btn_clicked(widget):
print(widget.get_label())
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(filename="img.png", width=24, height=24, preserve_aspect_ratio=True)
img = Gtk.Image.new_from_pixbuf(pixbuf)
btn = Gtk.Button(label='some text',image=img,)
btn.connect('clicked',btn_clicked)
win = Gtk.Window()
win.connect("destroy", Gtk.main_quit)
win.add(btn)
win.show_all()
Gtk.main()
I'm writing a simple paint like application:
#!/usr/bin/env python3
from gi.repository import Gtk
class Application(Gtk.Window):
def __init__(self):
super().__init__(Gtk.WindowType.TOPLEVEL, title='paint')
self.connect('destroy', self.__on_destroy)
vbox = Gtk.VBox()
drawing_area = Gtk.DrawingArea()
drawing_area.conenct('draw', self.__on_draw)
vbox.pack_start(drawing_area, True, True, 2)
self.add(vbox)
self.show_all()
def __on_draw(self, widget, g):
g.set_source_rgb(200, 0, 0)
g.move_to(16, 0)
g.line_to(16, 32)
g.stroke()
def save_to_file(self, filename):
# How to get pixels from drawing_area?
pass
def __on_destroy(self, e):
Gtk.main_quit()
app = Application()
Gtk.main()
Now I want to save user drawing. How can I access pixels inside Gtk.DrawingArea widget?
Quote from this following SO link
Gtk.DrawingArea derives from Gtk.Window.Hence you can use
Gdk.pixbuf_get_from_window() to get the contents of the drawing area
into a GdkPixbuf.Pixbuf and then use the GdkPixbuf.Pixbuf.savev()
function to write the pixbuf as an image on disk.
You can follow the link for complete code.Also this link can also help you.
I am currently using WXPython to do a GUI which displays images. I am currently changing the images about 1 to 2 times per second:
image = image.Scale(scaledHeight, scaledWidth)
image1 = image.ConvertToBitmap()
# Center the image
self.panel.bmp1 = wx.StaticBitmap(self.panel, -1, image1, ((width / 2) - (image.GetWidth() / 2), (height / 2) - (image.GetHeight() / 2)), (image.GetWidth(),image.GetHeight()))
Only problem is periodically the image does not display. I tried out an inefficient solution and copied the image into image1, image2, etc and displayed all of them hoping for the chances for all of them to not display to be lower. Unfortunately, the image will still periodically not display. Is there some sort of buffer I need to use?
Thanks in advance!
It would be convenient if you could provide your code snippet and some more details regarding what you are trying to achieve.
How ever I have created a small example which shows how to change/update images on a panel. The code below basically creates a random number in myThread() class. The number is then sent to the gui() class using publisher-subscriber strategy. The changeImage() of the gui() class checks if the value is less than or equal to 5, then it will display green image on the panel named as self.myPanel, else if the value is greater than 5 then a blue image is displayed.
The images can be downloaded from here: green.bmp blue.bmp
Code: Please note that the image files should be in the same directory in which this script is located.
import wx
import time
from wx.lib.pubsub import setupkwargs
from wx.lib.pubsub import pub
from threading import Thread
import random
class gui(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, None, id, title, size=(100,100))
self.myPanel = wx.Panel(self, -1)
image_file1 = 'green.bmp'
image_file2 = 'blue.bmp'
self.image1 = wx.Image(image_file1, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.image2 = wx.Image(image_file2, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.bitmap = wx.StaticBitmap(self.myPanel, -1)
pub.subscribe(self.changeImage, 'Update')
def changeImage(self, value):
#To destroy any previous image on self.myPanel
if self.bitmap:
self.bitmap.Destroy()
#If the value received from myThread() class is <= 5 then display the green image on myPanel
if value <=5:
self.bitmap = wx.StaticBitmap(self.myPanel, -1, self.image1, (0, 0))
#If the value received from myThread() class is > 5 then display the green image on myPanel
else:
self.bitmap = wx.StaticBitmap(self.myPanel, -1, self.image2, (10, 10))
class myThread(Thread):
def __init__(self):
Thread.__init__(self)
self.daemon = True
self.start()
def run(self):
while True:
number = random.randrange(1,10)
wx.CallAfter(pub.sendMessage, 'Update', value=number)
time.sleep(1)
if __name__=='__main__':
app = wx.App()
frame = gui(parent=None, id=-1, title="Test")
frame.Show()
myThread()
app.MainLoop()
I am trying to display live images from my 1394 camera.
Currently my code is able to obtain images in a loop from the camera and I was looking for any quick GUI that will update dynamically (as a separate thread). I can do this in PyQt maybe using QThreads but is there any recommendation or faster way of doing this??
Here's my code
#Loop capturing frames from camera
for frame in range(1,500):
print 'frame:',frame
TIME.sleep(1) #capture frame every second
image_binary = pycam.cam.RetrieveBuffer()
#convert to PIL Image
pilimg = PIL.Image.frombuffer("L",(cimg.GetCols(),cimg.GetRows()),image_binary,'raw', "RGBA", 0, 1)
# At this point I want to send my image data to a GUI window and display it
Thank you.
Here's wxPython code that will do it...
import wx
from PIL import Image
SIZE = (640, 480)
def get_image():
# Put your code here to return a PIL image from the camera.
return Image.new('L', SIZE)
def pil_to_wx(image):
width, height = image.size
buffer = image.convert('RGB').tostring()
bitmap = wx.BitmapFromBuffer(width, height, buffer)
return bitmap
class Panel(wx.Panel):
def __init__(self, parent):
super(Panel, self).__init__(parent, -1)
self.SetSize(SIZE)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
self.Bind(wx.EVT_PAINT, self.on_paint)
self.update()
def update(self):
self.Refresh()
self.Update()
wx.CallLater(15, self.update)
def create_bitmap(self):
image = get_image()
bitmap = pil_to_wx(image)
return bitmap
def on_paint(self, event):
bitmap = self.create_bitmap()
dc = wx.AutoBufferedPaintDC(self)
dc.DrawBitmap(bitmap, 0, 0)
class Frame(wx.Frame):
def __init__(self):
style = wx.DEFAULT_FRAME_STYLE & ~wx.RESIZE_BORDER & ~wx.MAXIMIZE_BOX
super(Frame, self).__init__(None, -1, 'Camera Viewer', style=style)
panel = Panel(self)
self.Fit()
def main():
app = wx.PySimpleApp()
frame = Frame()
frame.Center()
frame.Show()
app.MainLoop()
if __name__ == '__main__':
main()
I thought I'd try PyQt4 imageviewer.py example and it worked for me.
Thanks for all your help guys.
Here's my modified code:
from PyQt4 import QtCore, QtGui
class CameraViewer(QtGui.QMainWindow):
def __init__(self):
super(CameraViewer, self).__init__()
self.imageLabel = QtGui.QLabel()
self.imageLabel.setBackgroundRole(QtGui.QPalette.Base)
self.imageLabel.setScaledContents(True)
self.scrollArea = QtGui.QScrollArea()
self.scrollArea.setWidget(self.imageLabel)
self.setCentralWidget(self.scrollArea)
self.setWindowTitle("Image Viewer")
self.resize(640, 480)
timer = QtCore.QTimer(self)
timer.timeout.connect(self.open)
timer.start(33) #30 Hz
def open(self):
#get data and display
pilimg = getMyPILImageDatFromCamera()
image = PILQT.ImageQt.ImageQt(pilimg)
if image.isNull():
QtGui.QMessageBox.information(self, "Image Viewer","Cannot load %s." % fileName)
return
self.imageLabel.setPixmap(QtGui.QPixmap.fromImage(image))
self.imageLabel.adjustSize()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
CameraViewer = CameraViewer()
CameraViewer.show()
sys.exit(app.exec_())
I recommend using Tkinter since it's already part of python. I've never used PIL but a quick google shows it's easy to use PIL images in Tk widgets (via the pil.ImageTk.PhotoImage() method).
If you already have a Tkinter widget set up to display images (a Label widget works fine) all you need to do is arrange for the image to be updated every second or so. You can do this by using the after command of tkinter.
Here's an example; I don't have PIL so it uses a static image but it illustrates how to use the event loop to fetch images every second:
import Tkinter
class App(Tkinter.Tk):
def __init__(self):
Tkinter.Tk.__init__(self)
self.label = Tkinter.Label(text="your image here", compound="top")
self.label.pack(side="top", padx=8, pady=8)
self.iteration=0
self.UpdateImage(1000)
def UpdateImage(self, delay, event=None):
# this is merely so the display changes even though the image doesn't
self.iteration += 1
self.image = self.get_image()
self.label.configure(image=self.image, text="Iteration %s" % self.iteration)
# reschedule to run again in 1 second
self.after(delay, self.UpdateImage, 1000)
def get_image(self):
# this is where you get your image and convert it to
# a Tk PhotoImage. For demonstration purposes I'll
# just return a static image
data = '''
R0lGODlhIAAgALMAAAAAAAAAgHCAkC6LV76+vvXeswD/ANzc3DLNMubm+v/6zS9PT6Ai8P8A////
/////yH5BAEAAAkALAAAAAAgACAAAAS00MlJq7046803AF3ofAYYfh8GIEvpoUZcmtOKAO5rLMva
0rYVKqX5IEq3XDAZo1GGiOhw5rtJc09cVGo7orYwYtYo3d4+DBxJWuSCAQ30+vNTGcxnOIARj3eT
YhJDQ3woDGl7foNiKBV7aYeEkHEignKFkk4ciYaImJqbkZ+PjZUjaJOElKanqJyRrJyZgSKkokOs
NYa2q7mcirC5I5FofsK6hcHHgsSgx4a9yzXK0rrV19gRADs=
'''
image = Tkinter.PhotoImage(data=data)
return image
if __name__ == "__main__":
app=App()
app.mainloop()
Since the good answers are pretty large, I feel like I should post a library I built specifically for this:
from cvpubsubs.webcam_pub import VideoHandlerThread
import numpy as np
image_np = numpy.array(pilImage)
def update_function(frame, cam_id):
frame[...] = image_np[...]
VideoHandlerThread(video_source=image_np, callbacks=update_function).display()
Actually, that's if image_binary is a new numpy array every time. If it's assigned to the same location, then just this should work:
from cvpubsubs.webcam_pub import VideoHandlerThread
VideoHandlerThread(video_source=image_np).display()
I know OpenCV barely counts as a GUI, but this is quick code wise.
Try to take a look at gstreamer. This is the first result google gave me searching for "gstreamer 1394" and this one is the first for "gstreamer pyqt".