Skip to content

Instantly share code, notes, and snippets.

@nbergont
Last active March 12, 2019 10:01
Show Gist options
  • Save nbergont/8963892 to your computer and use it in GitHub Desktop.
Save nbergont/8963892 to your computer and use it in GitHub Desktop.
QML slideshow with KenBurns effect
import QtQuick 2.0
import Qt.labs.folderlistmodel 2.0
/**
--- QML Ken Burns slideshow ---
Work well on Raspberry Pi (resize image 1920*1600 max before ...)
call :
qmlscene slideshow.qml path="/home/img"
qmlscene.exe slideshow.qml path="/C:/test/img"
**/
Rectangle {
id: root
width: 800
height: 600
color: "black"
focus: true
Keys.onEscapePressed :{
Qt.quit();
}
//Slideshow options :
property int slide_duration: 7000 //ms
property int fade_duration: 800
//Load 2 slides
Loader {id:img1; transformOrigin: Item.TopLeft; sourceComponent: slide;}
Loader {id:img2; transformOrigin: Item.TopLeft; sourceComponent: slide;}
property variant current_img: img1
//Input images files
FolderListModel{
id: img_files
folder : "file://" + Qt.application.arguments[2].split("=")[1]
nameFilters: ["*.jpg", "*.jpeg", "*.png"]
showDirs : false
property int index: 0
property variant rlist: []
function getNextUrl(){
if(index >= rlist.length)
shuffleList();
return img_files.get(rlist[index++], "fileURL"); //filePath
}
//Fisher-Yates shuffle algorithm.
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
function shuffleList()
{
console.log("Shuffle...");
var list = [];
for(var i=0; i<img_files.count; i++)
list.push(i);
shuffleArray(list);
rlist = list;
index = 0;
}
//Initialisation
onDataChanged: {
console.log(img_files.count+ " images found");
shuffleList();
//Force la lecture de la 1ere image
img1.item.asynchronous = false
img1.item.visible = true;
img1.item.load_next_slide();
img2.item.load_next_slide();
img1.item.asynchronous = true;
mtimer.start();
}
}
//Main timer
Timer{
id:mtimer
interval: slide_duration-fade_duration
repeat: true
triggeredOnStart : true
onTriggered: {
current_img.item.fadein();
current_img = (current_img == img1 ? img2 : img1); //Swap img
current_img.item.fadeout();
}
}
//Slide component
Component {
id: slide
Image {
id: img
asynchronous : true
cache: false
fillMode: Image.PreserveAspectFit
//visible: true
opacity: 0
width: root.width
height: root.height
//Max painted size (RPI limitations)
sourceSize.width: 1920
sourceSize.height: 1600
property real from_scale: 1
property real to_scale: 1
property real from_x: 0
property real to_x: 0
property real from_y: 0
property real to_y: 0
function randRange(a, b){return Math.random()*Math.abs(a-b) + Math.min(a,b);}
function randChoice(n){return Math.round(Math.random()*(n-1));}
function randDirection(){return (Math.random() >= 0.5) ? 1 : -1;}
function fadein(){
//Check image loading...
if(status != Image.Ready){
console.log("LOAD ERROR", source);
return;
}
//Fit in view
var img_ratio = paintedWidth/paintedHeight;
var scale = (height == paintedHeight) ? width/paintedWidth : height/paintedHeight;
//Find random directions
if(img_ratio < 1){ //Rotated
from_scale = scale*0.8;//Un-zoom on 16/9 viewer...
to_scale = from_scale;
from_y = 0;
to_y = 0;
from_x = randDirection()*(paintedHeight*from_scale-height)/2;
to_x = 0;
}
else if(img_ratio > 2){ //Panorama
from_scale = scale;
to_scale = from_scale;
from_y = randDirection()*(paintedWidth*from_scale-width)/2;
to_y = -from_y;
from_x = 0;
to_x = 0;
}
else { //Normal
var type = randChoice(3);
switch(type)
{
case 0: //Zoom in
from_scale = scale;
to_scale = scale*1.4;
from_y = 0;
to_y = 0;
from_x = 0;
to_x = 0;
break;
case 1: //Zoom out
from_scale = scale*1.4;
to_scale = scale;
from_y = 0;
to_y = 0;
from_x = 0;
to_x = 0;
break;
default: //Fixed zoom
from_scale = scale*1.2;
to_scale = from_scale;
break;
}
//Random x,y
var from_max_y = (paintedWidth*from_scale-width)/2;
var to_max_y = (paintedWidth*to_scale-width)/2;
from_y = randRange(-from_max_y, from_max_y);
to_y = randRange(-to_max_y, to_max_y);
var from_max_x = (paintedHeight*from_scale-height)/2;
var to_max_x = (paintedHeight*to_scale-height)/2;
from_x = randRange(-from_max_x, from_max_x);
to_x = randRange(-to_max_x, to_max_x);
}
visible = true;
afadein.start();
}
function fadeout(){
afadeout.start();
}
function load_next_slide(){
visible = false;
source = img_files.getNextUrl();
console.log(source);
}
ParallelAnimation{
id: afadein
NumberAnimation {target: img; property: "opacity"; from: 0; to: 1; duration: fade_duration; easing.type: Easing.InOutQuad;}
NumberAnimation {target: img; property: "y"; from: from_x; to: to_x; duration: slide_duration; }
NumberAnimation {target: img; property: "x"; from: from_y; to: to_y; duration: slide_duration; }
NumberAnimation {target: img; property: "scale"; from: from_scale; to: to_scale; duration: slide_duration; }
}
SequentialAnimation {
id: afadeout;
NumberAnimation{ target: img; property: "opacity"; from: 1; to: 0; duration: fade_duration; easing.type: Easing.InOutQuad;}
ScriptAction { script: img.load_next_slide(); }
}
}
}
}
@ben80
Copy link

ben80 commented Aug 30, 2014

Hi,

can someone explain to me, where the following limitation for the RPI comes from ( code lines 111-113)?

      //Max painted size (RPI limitations)
       sourceSize.width: 1920
       sourceSize.height: 1600

Thanks a lot,
Ben

@nbergont
Copy link
Author

nbergont commented Oct 2, 2014

It is the limitation of Raspberry Pi texture size ! (exact limit is 2048*2048)

@svenss2
Copy link

svenss2 commented May 20, 2015

Hi!

I am quite new to Qt/QML but your source is a good inspiration - I wonder, if somebody could tell me, how I could include "QtGraphicalEffects" such as "HueSaturation" into the QML? I just wanted to use them to apply some "filters" to adjust brightness, contrast etc.

Thanks a lot,
Sven

@putridp
Copy link

putridp commented Jun 2, 2015

Thank you, this works really well.

I'm completely new to Qt/QML and struggled to get qmlscene working initially. For some reason I couldn't get the hardware acceleration to work in Jessie. However, I found a prebuilt version of Qt5 for Raspbian Wheezy and some instructions at the following page https://www.raspberrypi.org/forums/viewtopic.php?t=70784&p=526245

Thanks again for posting this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment