For a few months now I've been trying to have a random slideshow of images. I
used to do this either with
kscreensaver, which for completely different
reasons I can't use now, or
glslideshow, which, even when I
compiled it by hand, I can't find the way to give it the root dir of the images.
So, based on OMIT, I developed my own.
The differences with OMIT are minimal. It has to scan the whole tree for finding the appropriate files (its definition of "appropriate" could be improved, it's true); it goes into full screen mode with black background; and it (more) properly handles EXIF rotation. All that in 176 LOCs, including proper licensing (GPLv3), and developed in one day and refined the next one.
So, there you are. Like OMIT, it's in
PyQt4, but this time in Python3 (that's
why I used
includes porting it to
PyQt5 and a few other things. You can grab it
here. I plan to do a proper
release soon, but for the moment just drop it in your
PATH and be happy with
A couple of days ago Marcelo Fernández wrote a simple image viewer in
less than 200 lines long, and I thought that it would be nice to compare how
the same app would be written in
PyKDE4. But then I though that it would not
be fair, as
KDE is a whole desktop environment and
GTK is 'only' a widget
library, so I did it in
To make this even more fair, I hadn't had a good look at the code itself, I only run it to see what it looks like: a window with only the shown image in it, both scrollbars, no menu or statusbar, and no external file, so I assume he builds the ui 'by hand'. He mentions these features:
- Pan the image with the mouse.
- F1 to F5 handle the zoom from 'fit to window', 25%, 50%, 75% and 100%.
- Zooming with the mouse wheel doesn't work.
Here's my take:
#! /usr/bin/python # -*- coding: utf-8 -*- # - Example image viewer in # Marcos Dione <firstname.lastname@example.org> - http://grulicueva.homelinux.net/~mdione/glob/ # TODO: # * add licence! (GPLv2 or later) from .QtGui import QApplication, QMainWindow, QGraphicsView, QGraphicsScene from .QtGui import QPixmap, QGraphicsPixmapItem, QAction, QKeySequence import sys class OMITGraphicsView (QGraphicsView): def __init__ (self, pixmap, scene, parent, *args): QGraphicsView.__init__ (self, scene) self.zoomLevel= 1.0 self.win= parent self.img= pixmap self.setupActions () def setupActions (self): # a factory to the right! zoomfit= QAction (self) zoomfit.setShortcuts ([QKeySequence.fromString ('F1')]) zoomfit.triggered.connect (self.zoomfit) self.addAction (zoomfit) zoom25= QAction (self) zoom25.setShortcuts ([QKeySequence.fromString ('F2')]) zoom25.triggered.connect (self.zoom25) self.addAction (zoom25) zoom50= QAction (self) zoom50.setShortcuts ([QKeySequence.fromString ('F3')]) zoom50.triggered.connect (self.zoom50) self.addAction (zoom50) zoom75= QAction (self) zoom75.setShortcuts ([QKeySequence.fromString ('F4')]) zoom75.triggered.connect (self.zoom75) self.addAction (zoom75) zoom100= QAction (self) zoom100.setShortcuts ([QKeySequence.fromString ('F5')]) zoom100.triggered.connect (self.zoom100) self.addAction (zoom100) def zoomfit (self, *ignore): winSize= self.size () imgSize= self.img.size () print winSize, imgSize hZoom= 1.0*winSize.width ()/imgSize.width () vZoom= 1.0*winSize.height ()/imgSize.height () zoomLevel= min (hZoom, vZoom) print zoomLevel self.zoomTo (zoomLevel) def zoom25 (self, *ignore): self.zoomTo (0.25) def zoom50 (self, *ignore): self.zoomTo (0.5) def zoom75 (self, *ignore): self.zoomTo (0.75) def zoom100 (self, *ignore): self.zoomTo (1.0) def zoomTo (self, zoomLevel): scale= zoomLevel/self.zoomLevel print "scaling", scale self.scale (scale, scale) self.zoomLevel= zoomLevel if __name__=='__main__': # this code is enough for loading an image and show it! app= QApplication (sys.argv) win= QMainWindow () pixmap= QPixmap (sys.argv) qgpi= QGraphicsPixmapItem (pixmap) scene= QGraphicsScene () scene.addItem (qgpi) view= OMITGraphicsView (pixmap, scene, win) view.setDragMode (QGraphicsView.ScrollHandDrag) view.show() app.exec_ () # up to here! # end
Things to note:
- The code for loading, showing the image and pan support is only 13 lines of
Pythoncode, including 3 imports. The resulting app is also able to handle vector graphics, but of course I didn't exploit that, I just added a
- Zooming is implemented via
QGraphicsView.scale(), which is accumulative (scaling twice to 0.5 actually scales to 0.25 of the original size), so I have to keep the zoom level all the time. There should be a
- The code for calculating the scale level is not very good: scaling between 75% and 50% or 25% produces scales of 0.666 and 0.333, which I think at the end of the day will accumulate a lot of error.
- For the same reason,
zoomToFit()has to do some magic. I also got hit by the integer division of
Python(I was getting zoom factors of 0) so I had to add
1.0*to the claculations. It's good that this is fixed in
- The size reported by the
QMainWindowwas any vegetable (it said 640x480 when it actually was 960x600), so I used the
- For some strange reason
zoomToFit()scales the image a little bigger than it should, so a scrollbar appears in the direction of the constraining dimension.
- Less that 100 lines! Even if
setupActions()could surely be improved.
- In Marcelo's favor I should mention that he writes docstrings for most of his methods both in english and spanish (yes, of course I read his code after I finished mine). I barely put a couple of comments, but doing the same should add 10 more lines, tops. Also, I don't want to convert this into a who-has-it-smaller contest (the code, I mean :).
- It took me approx 3 hours, with no previous knowledge of how to do it and no internet connection, so no asking around. I just used the «Qt Reference Documentation», going to the «Gropued Classes» page and to the «Graphics View Classes» from there.
- It doesn't zoom with the mouse wheel either.
- The default colors of
formatplugin are at most sucky, but better than nothing.
 Unluckly he didn't declared which license it has, so I'm not sure if I really can do this. I GPL'ed mine.