Using pygtk how can I make a full screen slide show that rotates through all the images in a directory switching them every x seconds
In the past I wrote exactly the same thing. Then I deleted it. This version uses a prepared code for exposing an image while scaling it. It was taken from Jack Valmadre’s Blog
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Taken and customed from Jack Valmadre's Blog:
# http://jackvalmadre.wordpress.com/2008/09/21/resizable-image-control/
#
# Put together and created the time switching by Izidor Matusov <izidor.matusov#gmail.com>
import os
import pygtk
pygtk.require('2.0')
import gtk
import glib
def is_image(filename):
""" File is image if it has a common suffix and it is a regular file """
if not os.path.isfile(filename):
return False
for suffix in ['.jpg', '.png', '.bmp']:
if filename.lower().endswith(suffix):
return True
return False
def resizeToFit(image, frame, aspect=True, enlarge=False):
"""Resizes a rectangle to fit within another.
Parameters:
image -- A tuple of the original dimensions (width, height).
frame -- A tuple of the target dimensions (width, height).
aspect -- Maintain aspect ratio?
enlarge -- Allow image to be scaled up?
"""
if aspect:
return scaleToFit(image, frame, enlarge)
else:
return stretchToFit(image, frame, enlarge)
def scaleToFit(image, frame, enlarge=False):
image_width, image_height = image
frame_width, frame_height = frame
image_aspect = float(image_width) / image_height
frame_aspect = float(frame_width) / frame_height
# Determine maximum width/height (prevent up-scaling).
if not enlarge:
max_width = min(frame_width, image_width)
max_height = min(frame_height, image_height)
else:
max_width = frame_width
max_height = frame_height
# Frame is wider than image.
if frame_aspect > image_aspect:
height = max_height
width = int(height * image_aspect)
# Frame is taller than image.
else:
width = max_width
height = int(width / image_aspect)
return (width, height)
def stretchToFit(image, frame, enlarge=False):
image_width, image_height = image
frame_width, frame_height = frame
# Stop image from being blown up.
if not enlarge:
width = min(frame_width, image_width)
height = min(frame_height, image_height)
else:
width = frame_width
height = frame_height
return (width, height)
class ResizableImage(gtk.DrawingArea):
def __init__(self, aspect=True, enlarge=False,
interp=gtk.gdk.INTERP_NEAREST, backcolor=None, max=(1600,1200)):
"""Construct a ResizableImage control.
Parameters:
aspect -- Maintain aspect ratio?
enlarge -- Allow image to be scaled up?
interp -- Method of interpolation to be used.
backcolor -- Tuple (R, G, B) with values ranging from 0 to 1,
or None for transparent.
max -- Max dimensions for internal image (width, height).
"""
super(ResizableImage, self).__init__()
self.pixbuf = None
self.aspect = aspect
self.enlarge = enlarge
self.interp = interp
self.backcolor = backcolor
self.max = max
self.connect('expose_event', self.expose)
self.connect('realize', self.on_realize)
def on_realize(self, widget):
if self.backcolor is None:
color = gtk.gdk.Color()
else:
color = gtk.gdk.Color(*self.backcolor)
self.window.set_background(color)
def expose(self, widget, event):
# Load Cairo drawing context.
self.context = self.window.cairo_create()
# Set a clip region.
self.context.rectangle(
event.area.x, event.area.y,
event.area.width, event.area.height)
self.context.clip()
# Render image.
self.draw(self.context)
return False
def draw(self, context):
# Get dimensions.
rect = self.get_allocation()
x, y = rect.x, rect.y
# Remove parent offset, if any.
parent = self.get_parent()
if parent:
offset = parent.get_allocation()
x -= offset.x
y -= offset.y
# Fill background color.
if self.backcolor:
context.rectangle(x, y, rect.width, rect.height)
context.set_source_rgb(*self.backcolor)
context.fill_preserve()
# Check if there is an image.
if not self.pixbuf:
return
width, height = resizeToFit(
(self.pixbuf.get_width(), self.pixbuf.get_height()),
(rect.width, rect.height),
self.aspect,
self.enlarge)
x = x + (rect.width - width) / 2
y = y + (rect.height - height) / 2
context.set_source_pixbuf(
self.pixbuf.scale_simple(width, height, self.interp), x, y)
context.paint()
def set_from_pixbuf(self, pixbuf):
width, height = pixbuf.get_width(), pixbuf.get_height()
# Limit size of internal pixbuf to increase speed.
if not self.max or (width < self.max[0] and height < self.max[1]):
self.pixbuf = pixbuf
else:
width, height = resizeToFit((width, height), self.max)
self.pixbuf = pixbuf.scale_simple(
width, height,
gtk.gdk.INTERP_BILINEAR)
self.invalidate()
def set_from_file(self, filename):
self.set_from_pixbuf(gtk.gdk.pixbuf_new_from_file(filename))
def invalidate(self):
self.queue_draw()
class DemoGtk:
SECONDS_BETWEEN_PICTURES = 3
FULLSCREEN = True
WALK_INSTEAD_LISTDIR = True
def __init__(self):
self.window = gtk.Window()
self.window.connect('destroy', gtk.main_quit)
self.window.set_title('Slideshow')
self.image = ResizableImage( True, True, gtk.gdk.INTERP_BILINEAR)
self.image.show()
self.window.add(self.image)
self.load_file_list()
self.window.show_all()
if self.FULLSCREEN:
self.window.fullscreen()
glib.timeout_add_seconds(self.SECONDS_BETWEEN_PICTURES, self.on_tick)
self.display()
def load_file_list(self):
""" Find all images """
self.files = []
self.index = 0
if self.WALK_INSTEAD_LISTDIR:
for directory, sub_directories, files in os.walk('.'):
for filename in files:
if is_image(filename):
filepath = os.path.join(directory, filename)
self.files.append(filepath)
else:
for filename in os.listdir('.'):
if is_image(filename):
self.files.append(filename)
print "Images:", self.files
def display(self):
""" Sent a request to change picture if it is possible """
if 0 <= self.index < len(self.files):
self.image.set_from_file(self.files[self.index])
return True
else:
return False
def on_tick(self):
""" Skip to another picture.
If this picture is last, go to the first one. """
self.index += 1
if self.index >= len(self.files):
self.index = 0
return self.display()
if __name__ == "__main__":
gui = DemoGtk()
gtk.main()
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
Use gtk.Image and gtk.Window.fullscreen().
Related
I'm very new to programming pytho my 3rd month. I'm making a desktop program that is supposed to show appointments and sayings. I already have a function process that opens a window in full screen and displays the slide show.
But I want to place the slide show in a tkinter window so that I can add other labels next to the show.
this is my code i want to call the function in a tkinter window so that i can assign it to a button.
import cv2
import numpy as np
import glob
import os
import random
class Image:
def __init__(self, filename, time=200, size=800):
self.size = size
self.time = time
self.shifted = 1.0
self.img = cv2.imread(filename)
self.height, self.width, _ = self.img.shape
if self.width < self.height:
self.height = int(self.height*size/self.width)
self.width = size
self.img = cv2.resize(self.img, (self.width, self.height))
self.shift = self.height - size
self.shift_height = True
else:
self.width = int(self.width*size/self.height)
self.height = size
self.shift = self.width - size
self.img = cv2.resize(self.img, (self.width, self.height))
self.shift_height = False
self.delta_shift = self.shift/self.time
def reset(self):
if random.randint(0, 1) == 0:
self.shifted = 0.0
self.delta_shift = abs(self.delta_shift)
else:
self.shifted = self.shift
self.delta_shift = -abs(self.delta_shift)
def get_frame(self):
if self.shift_height:
roi = self.img[int(self.shifted):int(self.shifted) + self.size, :, :]
else:
roi = self.img[:, int(self.shifted):int(self.shifted) + self.size, :]
self.shifted += self.delta_shift
if self.shifted > self.shift:
self.shifted = self.shift
if self.shifted < 0:
self.shifted = 0
return roi
def process():
text = f'xXxxxxxXXXXXxxxxxXx'
coordinates = (650, 1100)
font = cv2.FONT_HERSHEY_SIMPLEX
fontScale = 2
color = (255, 0, 255)
thickness = 3
filenames = glob.glob(os.path.join(path, "*"))
cnt = 0
images = []
for filename in filenames:
img = Image(filename)
images.append(img)
if cnt > len(images):
break
cnt += 1
prev_image = images[random.randrange(0, len(images))]
prev_image.reset()
while True:
while True:
img = images[random.randrange(0, len(images))]
if img != prev_image:
break
img.reset()
for i in range(100):
alpha = i/100
beta = 1.0 - alpha
dst = cv2.addWeighted(img.get_frame(), alpha, prev_image.get_frame(), beta, 0.0)
dst = cv2.putText(dst, text, coordinates, font, fontScale, color, thickness,cv2.LINE_AA)
cv2.imshow('Slideshow', dst)
if cv2.waitKey(10) == ord('q'):
cv2.destroyWindow('Slideshow')
return
prev_image = img
for _ in range(100):
cv2.imshow('Slideshow', img.get_frame())
if cv2.waitKey(10) == ord('q'):
cv2.destroyWindow('Slideshow')
return
def start():
cnt = 0
images = []
path = 'pictures'
filenames = glob.glob(os.path.join(path, "*"))
showWindow = tk.Tk()
showWindow.attributes('-fullscreen', True)
showWindow.mainloop()
I tried to display the text in opencv in the pictures but I had problems keeping the text in the exercises. That's why I want to display the whole thing in a Tkinter window so I can do the classification with grid. because I don't just want to display a single image but a slide show
Does the method not work here(**) or I just don't understand it could someone help me.
(**)=
#Import the tkinter library
from tkinter import *
import numpy as np
import cv2
from PIL import Image, ImageTk
#Create an instance of tkinter frame
show_Winow = Tk()
win.geometry("700x550")
#Load the image
img = cv2.imread('tutorialspoint.png')
#Rearrange colors
blue,green,red = cv2.split(img)
img = cv2.merge((red,green,blue))
im = Image.fromarray(img)
imgtk = ImageTk.PhotoImage(image=im)
#Create a Label to display the image
Label(show_Window, image= imgtk).pack()
show_Window.mainloop()
I'm trying to improve the speed of my object detection. I'm using OpenCV and ORB brute force keypoint matching.
I've got 30 seconds for keypoint_detection to run and at the moment it's taking about 23 seconds. This is fine for now with 74 images, but that number is likely to increase.
So far I've;
Reduced my search area down to the absolute minimum
Refined my 74 images down to the absolute minimum size possible
I tried pre-processing to remove some of the clutter from the matching but it impacted my ability to find a successful match dramatically.
I've also tried;
SIF, SURF, MatchTemplate, Canny and a few other methods. ORB brute force is my best match by a significant margin.
This is my exact working code, not pseudo-code and not an MVP as I didn't think it would make sense presenting a cut down version of the code I'm looking to speed up.
Is there any way to improve the efficiency of my code?
import cv2 as cv
import os
import glob
import pyautogui as py
from time import sleep
from windowcapture import WindowCapture
from vision import Vision
# Change the working directory to the folder this script is in.
os.chdir(r'C:\test')
avoid = glob.glob(r"C:\Users\test\*.png")
def loadImages(directory):
# Intialise empty array
image_list = []
# Add images to array
for i in directory:
img = cv.imread(i, cv.IMREAD_UNCHANGED)
image_list.append((img, i))
return image_list
# initialize the WindowCapture class
wincap = WindowCapture()
def keypoint_detection(image_list):
counter = 0
for i in image_list:
counter += 1
needle_img = i[0]
# load image to find
objectToFind = Vision(needle_img)
# get an updated image of the screen
keypoint_haystack = wincap.get_haystack()
# crop the image
x, w, y, h = [600,700,20,50]
keypoint_haystack = keypoint_haystack[y:y+h, x:x+w]
kp1, kp2, matches, match_points = objectToFind.match_keypoints(keypoint_haystack, sliced_name, min_match_count=30)
match_image = cv.drawMatches(objectToFind.needle_img, kp1, keypoint_haystack, kp2, matches, None)
if match_points:
# find the center point of all the matched features
center_point = objectToFind.centeroid(match_points)
# account for the width of the needle image that appears on the left
center_point[0] += objectToFind.needle_w
# drawn the found center point on the output image
match_image = objectToFind.draw_crosshairs(match_image, [center_point])
sleep(3)
break
while(True):
ships_to_avoid = loadImages(avoid)
keypoint_detection(ships_to_avoid)
WindowCapture Class
import numpy as np
import win32gui, win32ui, win32con
class WindowCapture:
# properties
w = 0
h = 0
hwnd = None
cropped_x = 0
cropped_y = 0
offset_x = 0
offset_y = 0
# constructor
def __init__(self, window_name=None):
# find the handle for the window we want to capture.
# if no window name is given, capture the entire screen
if window_name is None:
self.hwnd = win32gui.GetDesktopWindow()
else:
self.hwnd = win32gui.FindWindow(None, window_name)
if not self.hwnd:
raise Exception('Window not found: {}'.format(window_name))
# get the window size
window_rect = win32gui.GetWindowRect(self.hwnd)
self.w = window_rect[2] - window_rect[0]
self.h = window_rect[3] - window_rect[1]
# account for the window border and titlebar and cut them off
border_pixels = 0
titlebar_pixels = 5
self.w = self.w - border_pixels
self.h = self.h - titlebar_pixels - border_pixels
self.cropped_x = border_pixels
self.cropped_y = titlebar_pixels
# set the cropped coordinates offset so we can translate screenshot
# images into actual screen positions
self.offset_x = window_rect[0] + self.cropped_x
self.offset_y = window_rect[1] + self.cropped_y
def get_haystack(self):
# get the window image data
wDC = win32gui.GetWindowDC(self.hwnd)
dcObj = win32ui.CreateDCFromHandle(wDC)
cDC = dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, self.w, self.h)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0, 0), (self.w, self.h), dcObj, (self.cropped_x, self.cropped_y), win32con.SRCCOPY)
# convert the raw data into a format opencv can read
# dataBitMap.SaveBitmapFile(cDC, 'debug.bmp')
signedIntsArray = dataBitMap.GetBitmapBits(True)
img = np.fromstring(signedIntsArray, dtype='uint8')
img.shape = (self.h, self.w, 4)
# free resources
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(self.hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())
img = img[...,:3]
img = np.ascontiguousarray(img)
return img
#staticmethod
def list_window_names():
def winEnumHandler(hwnd, ctx):
if win32gui.IsWindowVisible(hwnd):
print(hex(hwnd), win32gui.GetWindowText(hwnd))
win32gui.EnumWindows(winEnumHandler, None)
# translate a pixel position on a screenshot image to a pixel position on the screen.
# pos = (x, y)
def get_screen_position(self, pos):
return (pos[0] + self.offset_x, pos[1] + self.offset_y)
Vision Class
import cv2 as cv
import numpy as np
class Vision:
# properties
needle_img = None
needle_w = 0
needle_h = 0
# constructor
def __init__(self, needle_img_path):
self.needle_img = needle_img_path
# Save the dimensions of the needle image
self.needle_w = self.needle_img.shape[1]
self.needle_h = self.needle_img.shape[0]
def match_keypoints(self, haystack_screenshot, name, min_match_count, patch_size=32):
orb = cv.ORB_create(edgeThreshold=0, patchSize=patch_size)
keypoints_needle, descriptors_needle = orb.detectAndCompute(self.needle_img, None)
orb2 = cv.ORB_create(edgeThreshold=0, patchSize=patch_size, nfeatures=2000)
keypoints_haystack, descriptors_haystack = orb2.detectAndCompute(haystack_screenshot, None)
FLANN_INDEX_LSH = 6
index_params = dict(algorithm=FLANN_INDEX_LSH, table_number=6, key_size=12, multi_probe_level=1)
search_params = dict(checks=50)
try:
flann = cv.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(descriptors_needle, descriptors_haystack, k=2)
except cv.error:
return None, None, [], []
# store all the good matches as per Lowe's ratio test.
good = []
points = []
for pair in matches:
if len(pair) == 2:
if pair[0].distance < 0.7*pair[1].distance:
good.append(pair[0])
if len(good) > min_match_count:
print(str(name) + ' - ' + '%03d keypoints matched - %03d' % (len(good), len(keypoints_needle)))
for match in good:
points.append(keypoints_haystack[match.trainIdx].pt)
return keypoints_needle, keypoints_haystack, good, points
I'm trying to detect these chevrons from four instances of bluestacks running the same game:
To make it a little easier at a glance to see the differences. These are the same chevrons zoomed. For some reason, bluestacks render them marginally different every time.
I've tried to use thresholding and different methods with OpenCV but I need to drop it so low I get too many false positives and the different methods don't seem to make a difference.
I'm using win32gui for my template:
class WindowCapture:
# properties
w = 0
h = 0
hwnd = None
cropped_x = 0
cropped_y = 0
offset_x = 0
offset_y = 0
# constructor
def __init__(self, window_name=None):
# find the handle for the window we want to capture.
# if no window name is given, capture the entire screen
if window_name is None:
self.hwnd = win32gui.GetDesktopWindow()
else:
self.hwnd = win32gui.FindWindow(None, window_name)
if not self.hwnd:
raise Exception('Window not found: {}'.format(window_name))
# get the window size
window_rect = win32gui.GetWindowRect(self.hwnd)
self.w = window_rect[2] - window_rect[0]
self.h = window_rect[3] - window_rect[1]
# account for the window border and titlebar and cut them off
border_pixels = 8
titlebar_pixels = 40
self.w = self.w - (border_pixels * 2)
self.h = self.h - titlebar_pixels - border_pixels
self.cropped_x = border_pixels
self.cropped_y = titlebar_pixels
# set the cropped coordinates offset so we can translate screenshot
# images into actual screen positions
self.offset_x = window_rect[0] + self.cropped_x
self.offset_y = window_rect[1] + self.cropped_y
def get_screenshot(self):
# get the window image data
wDC = win32gui.GetWindowDC(self.hwnd)
dcObj = win32ui.CreateDCFromHandle(wDC)
cDC = dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, self.w, self.h)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0, 0), (self.w, self.h), dcObj, (self.cropped_x, self.cropped_y), win32con.SRCCOPY)
# convert the raw data into a format opencv can read
# dataBitMap.SaveBitmapFile(cDC, 'debug.bmp')
signedIntsArray = dataBitMap.GetBitmapBits(True)
img = np.fromstring(signedIntsArray, dtype='uint8')
img.shape = (self.h, self.w, 4)
# free resources
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(self.hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())
img = img[...,:3]
img = np.ascontiguousarray(img)
return img
#staticmethod
def list_window_names():
def winEnumHandler(hwnd, ctx):
if win32gui.IsWindowVisible(hwnd):
print(hex(hwnd), win32gui.GetWindowText(hwnd))
win32gui.EnumWindows(winEnumHandler, None)
# translate a pixel position on a screenshot image to a pixel position on the screen.
# pos = (x, y)
def get_screen_position(self, pos):
return (pos[0] + self.offset_x, pos[1] + self.offset_y)
And OpenCV and Numpy for my object detection:
class Vision:
# properties
needle_img = None
needle_w = 0
needle_h = 0
method = None
# constructor
def __init__(self, needle_img_path, method=cv.TM_CCOEFF_NORMED):
self.needle_img = cv.imread(needle_img_path, cv.IMREAD_UNCHANGED)
# Save the dimensions of the needle image
self.needle_w = self.needle_img.shape[1]
self.needle_h = self.needle_img.shape[0]
# There are 6 methods to choose from:
# TM_CCOEFF, TM_CCOEFF_NORMED, TM_CCORR, TM_CCORR_NORMED, TM_SQDIFF, TM_SQDIFF_NORMED
self.method = method
def find(self, haystack_img, threshold=0.5, debug_mode=None):
# run the OpenCV algorithm
result = cv.matchTemplate(haystack_img, self.needle_img, self.method)
# Get the all the positions from the match result that exceed our threshold
locations = np.where(result >= threshold)
locations = list(zip(*locations[::-1]))
rectangles = []
for loc in locations:
rect = [int(loc[0]), int(loc[1]), self.needle_w, self.needle_h]
# Add every box to the list twice in order to retain single (non-overlapping) boxes
rectangles.append(rect)
rectangles.append(rect)
# Apply group rectangles
rectangles, weights = cv.groupRectangles(rectangles, groupThreshold=1, eps=0.5)
points = []
if len(rectangles):
line_color = (0, 255, 0)
line_type = cv.LINE_4
marker_color = (255, 0, 255)
marker_type = cv.MARKER_CROSS
# Loop over all the rectangles
for (x, y, w, h) in rectangles:
# Determine the center position
center_x = x + int(w/2)
center_y = y + int(h/2)
# Save the points
points.append((center_x, center_y))
if debug_mode == 'rectangles':
# Determine the box position
top_left = (x, y)
bottom_right = (x + w, y + h)
# Draw the box
cv.rectangle(haystack_img, top_left, bottom_right, color=line_color,
lineType=line_type, thickness=2)
elif debug_mode == 'points':
# Draw the center point
cv.drawMarker(haystack_img, (center_x, center_y),
color=marker_color, markerType=marker_type,
markerSize=40, thickness=2)
return points
And then I'm calling the match like this:
chevronsicon = Vision('chevrons.jpg')
# checks for chevrons
chevron = chevronsicon.find(screenshotCropped, 0.86, 'points')
# mouse controls
if chevrons :
py.moveTo(chevrons[0])
Thresholding and different OpenCV methods have worked every single time I've come across this issue previously. I was considering using a gaussian function to blur the edges and then canny to see if I could get a consistent match but I'm not sure if that's overworking what might be a very simple answer I'm simply not aware of.
Is there an easier way to detect these?
Why does grabwindow return black screen? Is a valid window and I do not receive any errors.
pix = QPixmap.grabWindow(hwnd)
And that way it works perfectly!
pix = QPixmap.grabWindow(QApplication.desktop().winId())
Below the complete source code:
def __init__(self):
serverProtocol.RDCServerProtocol.__init__(self)
self._array = QByteArray()
self._buffer = QBuffer(self._array)
self._buffer.open(QIODevice.WriteOnly)
def _makeFramebuffer(self, width, height):
pix = QPixmap.grabWindow(197526) # Google Chrome Window
pix = pix.scaled(width, height)
if width >= self._maxWidth or height >= self._maxHeight:
width = self._maxWidth
height = self._maxHeight
pix.save(self._buffer, 'jpeg')
pixData = self._buffer.data()
self._array.clear()
self._buffer.close()
return "%s" % pixData
I need to capture only the google window, but a black screen appears =/
Thank!
i want to add a transparent label over an image using pygtk, it does not seem to work with me, the label over comes the background pictures (slider) and i can only see my label. i had the idea to make a label transparent in order to see the background picture,, any suggestions please?
#!/usr/bin/python
# -*- coding: utf-8 -*-
import RPi.GPIO as GPIO
from time import sleep
import urllib2
import os
import xml.dom
import xml.dom.minidom
import pygtk
pygtk.require('2.0')
import gtk
import glib
#Configure the inputs/outputs
GPIO.setmode(GPIO.BCM)
GPIO.setup(4, GPIO.IN, pull_up_down = GPIO.PUD_UP) # The emplyee button
xmldoc = xml.dom.minidom.parse('/var/www/delay.xml')
delay = xmldoc.getElementsByTagName('delay')
def _begin():
if (GPIO.input(4)==0):
response=urllib2.urlopen("http://localhost/num.php?id_service=1",timeout=5) **#retrieve the next ticket number from MYSQL DB through an url**
number = response.read()
label = gtk.Label("9999 ") # for testing else it would be label= gtk.Label(number)**
response.close()
sleep(1);
return _begin()
def is_image(filename):
""" File is image if it has a common suffix and it is a regular file """
if not os.path.isfile(filename):
return False
for suffix in ['.jpg', '.png', '.bmp']:
if filename.lower().endswith(suffix):
return True
return False
def resizeToFit(image, frame, aspect=True, enlarge=False):
"""Resizes a rectangle to fit within another.
Parameters:
image -- A tuple of the original dimensions (width, height).
frame -- A tuple of the target dimensions (width, height).
aspect -- Maintain aspect ratio?
enlarge -- Allow image to be scaled up?
"""
if aspect:
return scaleToFit(image, frame, enlarge)
else:
return stretchToFit(image, frame, enlarge)
def scaleToFit(image, frame, enlarge=False):
image_width, image_height = image
frame_width, frame_height = frame
image_aspect = float(image_width) / image_height
frame_aspect = float(frame_width) / frame_height
# Determine maximum width/height (prevent up-scaling).
if not enlarge:
max_width = min(frame_width, image_width)
max_height = min(frame_height, image_height)
else:
max_width = frame_width
max_height = frame_height
# Frame is wider than image.
if frame_aspect > image_aspect:
height = max_height
width = int(height * image_aspect)
# Frame is taller than image.
else:
width = max_width
height = int(width / image_aspect)
return (width, height)
def stretchToFit(image, frame, enlarge=False):
image_width, image_height = image
frame_width, frame_height = frame
# Stop image from being blown up.
if not enlarge:
width = min(frame_width, image_width)
height = min(frame_height, image_height)
else:
width = frame_width
height = frame_height
return (width, height)
class ResizableImage(gtk.DrawingArea):
def __init__(self, aspect=True, enlarge=False,
interp=gtk.gdk.INTERP_NEAREST, backcolor=None, max=(1600,1200)):
"""Construct a ResizableImage control.
Parameters:
aspect -- Maintain aspect ratio?
enlarge -- Allow image to be scaled up?
interp -- Method of interpolation to be used.
backcolor -- Tuple (R, G, B) with values ranging from 0 to 1,
or None for transparent.
max -- Max dimensions for internal image (width, height).
"""
super(ResizableImage, self).__init__()
self.pixbuf = None
self.aspect = aspect
self.enlarge = enlarge
self.interp = interp
self.backcolor = backcolor
self.max = max
self.connect('expose_event', self.expose)
self.connect('realize', self.on_realize)
def on_realize(self, widget):
if self.backcolor is None:
color = gtk.gdk.Color()
else:
color = gtk.gdk.Color(*self.backcolor)
self.window.set_background(color)
def expose(self, widget, event):
# Load Cairo drawing context.
self.context = self.window.cairo_create()
# Set a clip region.
self.context.rectangle(
event.area.x, event.area.y,
event.area.width, event.area.height)
self.context.clip()
# Render image.
self.draw(self.context)
return False
def draw(self, context):
# Get dimensions.
rect = self.get_allocation()
x, y = rect.x, rect.y
# Remove parent offset, if any.
parent = self.get_parent()
if parent:
offset = parent.get_allocation()
x -= offset.x
y -= offset.y
# Fill background color.
if self.backcolor:
context.rectangle(x, y, rect.width, rect.height)
context.set_source_rgb(*self.backcolor)
context.fill_preserve()
# Check if there is an image.
if not self.pixbuf:
return
width, height = resizeToFit(
(self.pixbuf.get_width(), self.pixbuf.get_height()),
(rect.width, rect.height),
self.aspect,
self.enlarge)
x = x + (rect.width - width) / 2
y = y + (rect.height - height) / 2
context.set_source_pixbuf(
self.pixbuf.scale_simple(width, height, self.interp), x, y)
context.paint()
def set_from_pixbuf(self, pixbuf):
width, height = pixbuf.get_width(), pixbuf.get_height()
# Limit size of internal pixbuf to increase speed.
if not self.max or (width < self.max[0] and height < self.max[1]):
self.pixbuf = pixbuf
else:
width, height = resizeToFit((width, height), self.max)
self.pixbuf = pixbuf.scale_simple(
width, height,
gtk.gdk.INTERP_BILINEAR)
self.invalidate()
def set_from_file(self, filename):
self.set_from_pixbuf(gtk.gdk.pixbuf_new_from_file(filename))
def invalidate(self):
self.queue_draw()
class DemoGtk:
SECONDS_BETWEEN_PICTURES = int(delay[0].firstChild.nodeValue)
FULLSCREEN = True
WALK_INSTEAD_LISTDIR = True
def __init__(self):
self.window = gtk.Window()
self.window.connect('destroy', gtk.main_quit)
self.window.set_title('Slideshow')
self.image = ResizableImage( True, True, gtk.gdk.INTERP_BILINEAR)
self.image.show()
self.window.add(self.image)
self.window.add(label) # GO to def _begin there i declared the label with its value**
self.load_file_list()
self.window.show_all()
if self.FULLSCREEN:
self.window.fullscreen()
glib.timeout_add_seconds(self.SECONDS_BETWEEN_PICTURES, self.on_tick)
self.display()
def load_file_list(self):
""" Find all images """
self.files = []
self.index = 0
if self.WALK_INSTEAD_LISTDIR:
for directory, sub_directories, files in os.walk('.'):
for filename in files:
if is_image(filename):
filepath = os.path.join(directory, filename)
self.files.append(filepath)
else:
for filename in os.listdir('.'):
if is_image(filename):
self.files.append(filename)
print "Images:", self.files
def display(self):
""" Sent a request to change picture if it is possible """
if 0 <= self.index < len(self.files):
self.image.set_from_file(self.files[self.index])
return True
else:
return False
def on_tick(self):
""" Skip to another picture.
If this picture is last, go to the first one. """
self.index += 1
if self.index >= len(self.files):
self.index = 0
return self.display()
if __name__ == "__main__":
while 1:
gui = DemoGtk()
gtk.main()
_begin()
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4