Version 1.0

This commit is contained in:
Rinaldus 2016-02-29 18:05:54 +03:00
commit 7043c22c95
14 changed files with 9698 additions and 0 deletions

27
README.md Normal file
View File

@ -0,0 +1,27 @@
## About
This is Light weight desktop widget, that shows random picture from given directory. It's similar to the same plasmoid from KDE 4.x. I very liked it and was very upset when KDE 4 became obsolete and there's no alternative plasmoid in KDE 5. That's why I decided to write it myself. It was written in Python 3 and PyQt 5 and it's compatible with all known desktop environments.
## Screenshots
![Main window in MATE](https://raw.github.com/rinaldus/photoframe/master/screenshots/screen1.jpg)
## Install
You need to install PyQt5 as dependency. Then just clone this git repository or download release and copy all files where you want to store this program. No additional installation is required.
## Launch
Give execute permission to photoframe.py
```sh
$ chmod +x photoframe.py
```
After program start, you have to set directory with many pictures or photos in "Settings" window (this window will appear automatically at first program start).
## Features
* JPG and PNG only formats are supported.
* When you set directory, the program will search for JPG and PNG files in this directory and also in subdirectories recursively.
* Click on picture to choose another random picture immediately. You can also set update interval for automatic change.
## Changelog
### Version 1.0 (release date: 29.02.16)
* Initial version

BIN
icons/dummy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
icons/lock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
icons/menu_quit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
icons/settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
icons/unlock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

210
photoframe.py Executable file
View File

@ -0,0 +1,210 @@
#!/usr/bin/env python3
from PyQt5.QtGui import QIcon, QMouseEvent
from PyQt5.QtWidgets import (QAction, QApplication, QCheckBox, QComboBox,
QDialog, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLineEdit,
QMessageBox, QMenu, QPushButton, QSpinBox, QStyle, QSystemTrayIcon,
QTextEdit, QVBoxLayout, QSizeGrip, QFileDialog)
from PyQt5.QtCore import (QThread, QTimer, QFile, QSettings,Qt, QPoint )
import resources_rc
from ui_main import Ui_PhotoFrame
from ui_settings import Ui_Settings
from PyQt5 import QtCore, QtGui, QtWidgets
import os
import random
import fnmatch
programSettings = QSettings(os.path.expanduser("~")+"/.config/photoframe/settings.conf", QSettings.NativeFormat)
MainWindowType = QtCore.Qt.Tool
def SettingsExist():
if (programSettings.contains("SizeX") and
programSettings.contains("SizeY") and
programSettings.contains("directory") and
programSettings.contains("PosX") and
programSettings.contains("PosY") and
programSettings.contains("UpdateInterval")):
return True
else:
return False
def SettingsSave():
programSettings.setValue("SizeX",window.geometry().width())
programSettings.setValue("SizeY",window.geometry().height())
programSettings.setValue("directory",settings.ui.txtPath.text())
programSettings.setValue("PosX",window.x())
programSettings.setValue("PosY",window.y())
programSettings.setValue("UpdateInterval",settings.ui.spinUpdateInterval.value())
#Settings load
if (SettingsExist()):
SizeX = int(programSettings.value("SizeX"))
SizeY = int(programSettings.value("SizeY"))
directory = programSettings.value("directory")
PosX = int(programSettings.value("PosX"))
PosY = int(programSettings.value("PosY"))
UpdateInterval = int(programSettings.value("UpdateInterval"))
else:
SizeX = 800
SizeY = 600
directory = ""
PosX = 50
PosY = 50
UpdateInterval = 60
class Window(QDialog):
def __init__(self):
super(Window, self).__init__()
# Init
self.ui = Ui_PhotoFrame()
self.ui.setupUi(self)
self.setWindowFlags(MainWindowType | Qt.WindowStaysOnBottomHint | Qt.FramelessWindowHint)
self.locked = True
self.resize(SizeX, SizeY)
self.move(PosX,PosY)
# Viewer configuration
if (directory != ""):
picture = QtGui.QPixmap(getRandomPicture(directory)).scaled(SizeX,SizeY,QtCore.Qt.KeepAspectRatio)
self.ui.Viewer.setPixmap(picture)
self.ui.Viewer.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.ui.Viewer.customContextMenuRequested.connect(self.showMenu)
# Timer
self.timer = QTimer(self)
self.timer.timeout.connect(self.timerTimeout)
# Functions
def showMenu(self,point):
menu = QMenu(self)
if self.locked:
menu.addAction(QAction(QIcon(':icons/unlock.png'),"&Unlock", self, triggered=self.Unlock))
else:
menu.addAction(QAction(QIcon(':icons/lock.png'),"&Lock", self, triggered=self.Lock))
menu.addAction(QAction(QIcon(':icons/settings.png'),"&Settings", self, triggered=settings.show))
menu.addAction(QAction(QIcon(':icons/menu_quit.png'),"&Quit", self, triggered=QApplication.instance().quit))
menu.popup(self.mapToGlobal(point))
def Unlock(self):
window.setWindowFlags(QtCore.Qt.Window)
self.locked = False
window.show()
def Lock(self):
window.setWindowFlags(MainWindowType | Qt.FramelessWindowHint | Qt.WindowStaysOnBottomHint)
self.locked = True
window.show()
def closeEvent(self, event):
SettingsSave()
QApplication.instance().quit()
def mousePressEvent(self,event):
if ((event.buttons() == QtCore.Qt.LeftButton) and (SettingsExist())):
setPicture(programSettings.value("directory"))
def resizeEvent(self,event):
programSettings.setValue("SizeX",window.geometry().width())
programSettings.setValue("SizeY",window.geometry().height())
def moveEvent(self,event):
programSettings.setValue("PosX",window.x())
programSettings.setValue("PosY",window.y())
if (window.x() > 1920 or window.y() > 1000):
PosX = 1700
PosY = 1000
window.move(PosX,PosY)
if (window.x() < 0):
PosX = 0
window.move(PosX,window.y())
def start(self):
self.timer.setInterval(settings.ui.spinUpdateInterval.value()*1000*60)
self.timer.start()
def stop(self):
self.timer.stop()
def timerTimeout(self):
self.ui.Viewer.setPixmap(QtGui.QPixmap(getRandomPicture(settings.ui.txtPath.text())).scaled(SizeX,SizeY,QtCore.Qt.KeepAspectRatio))
class Settings(QDialog):
def __init__(self):
super(Settings, self).__init__()
# Init
self.ui = Ui_Settings()
self.ui.setupUi(self)
self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint)
self.ui.txtPath.setText(directory)
if SettingsExist():
self.ui.spinUpdateInterval.setValue(int(programSettings.value("UpdateInterval")))
self.ui.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self.btnOK_clicked)
self.ui.buttonBox.button(QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self.btnCancel_clicked)
self.ui.btnOpen.clicked.connect(self.btnOpen_clicked)
# Functions
def btnCancel_clicked(self):
self.hide()
def closeEvent(self, event):
self.hide()
event.ignore()
def btnOpen_clicked(self):
directory = QFileDialog.getExistingDirectory(self, 'Open file', os.path.expanduser("~"), QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks | QFileDialog.ReadOnly)
self.ui.txtPath.setText(directory)
def btnOK_clicked(self):
SettingsSave()
directory = self.ui.txtPath.text()
setPicture(directory)
window.stop()
window.start()
self.hide()
def getRandomPicture(path):
files = os.listdir(path)
files_jpg = searchFilesRecursively(path,"*.jpg")
files_png = searchFilesRecursively(path,"*.png")
pictures = files_jpg+files_png
index = random.randrange(0, len(pictures))
return str(pictures[index])
def searchFilesRecursively(path,mask):
matches = []
for root, dirnames, filenames in os.walk(path):
for filename in fnmatch.filter(filenames, mask):
matches.append(os.path.join(root, filename))
return (matches)
def setPicture(directory):
picture = QtGui.QPixmap(getRandomPicture(directory)).scaled(SizeX,SizeY,QtCore.Qt.KeepAspectRatio)
window.ui.Viewer.setPixmap(picture)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
QApplication.setQuitOnLastWindowClosed(True)
window = Window()
window.show()
settings = Settings()
if (SettingsExist()):
settings.hide()
else:
settings.show()
window.start()
sys.exit(app.exec_())

9
resources.qrc Normal file
View File

@ -0,0 +1,9 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/">
<file>icons/dummy.png</file>
<file>icons/menu_quit.png</file>
<file>icons/settings.png</file>
<file>icons/lock.png</file>
<file>icons/unlock.png</file>
</qresource>
</RCC>

9159
resources_rc.py Normal file

File diff suppressed because it is too large Load Diff

BIN
screenshots/screen1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

62
ui/main.ui Normal file
View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PhotoFrame</class>
<widget class="QDialog" name="PhotoFrame">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>670</width>
<height>481</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Photo Frame</string>
</property>
<property name="windowIcon">
<iconset resource="../resources.qrc">
<normaloff>:/icons/dummy.png</normaloff>:/icons/dummy.png</iconset>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="Viewer">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../resources.qrc">:/icons/dummy.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../resources.qrc"/>
</resources>
<connections/>
</ui>

125
ui/settings.ui Normal file
View File

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Settings</class>
<widget class="QDialog" name="Settings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>487</width>
<height>120</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<property name="windowIcon">
<iconset resource="../resources.qrc">
<normaloff>:/icons/settings.png</normaloff>:/icons/settings.png</iconset>
</property>
<widget class="QLineEdit" name="txtPath">
<property name="geometry">
<rect>
<x>110</x>
<y>10</y>
<width>341</width>
<height>22</height>
</rect>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
<widget class="QPushButton" name="btnOpen">
<property name="geometry">
<rect>
<x>460</x>
<y>10</y>
<width>21</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>30</x>
<y>10</y>
<width>71</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Source path</string>
</property>
</widget>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>160</x>
<y>90</y>
<width>165</width>
<height>21</height>
</rect>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>30</x>
<y>50</y>
<width>141</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Change picture every</string>
</property>
</widget>
<widget class="QSpinBox" name="spinUpdateInterval">
<property name="geometry">
<rect>
<x>160</x>
<y>50</y>
<width>51</width>
<height>22</height>
</rect>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>60</number>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>220</x>
<y>50</y>
<width>56</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>minutes</string>
</property>
</widget>
</widget>
<resources>
<include location="../resources.qrc"/>
</resources>
<connections/>
</ui>

49
ui_main.py Normal file
View File

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ui/main.ui'
#
# Created by: PyQt5 UI code generator 5.5.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_PhotoFrame(object):
def setupUi(self, PhotoFrame):
PhotoFrame.setObjectName("PhotoFrame")
PhotoFrame.resize(670, 481)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(PhotoFrame.sizePolicy().hasHeightForWidth())
PhotoFrame.setSizePolicy(sizePolicy)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/icons/dummy.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
PhotoFrame.setWindowIcon(icon)
self.horizontalLayout = QtWidgets.QHBoxLayout(PhotoFrame)
self.horizontalLayout.setObjectName("horizontalLayout")
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
self.Viewer = QtWidgets.QLabel(PhotoFrame)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.Viewer.sizePolicy().hasHeightForWidth())
self.Viewer.setSizePolicy(sizePolicy)
self.Viewer.setAutoFillBackground(False)
self.Viewer.setText("")
self.Viewer.setPixmap(QtGui.QPixmap(":/icons/dummy.png"))
self.Viewer.setScaledContents(True)
self.Viewer.setAlignment(QtCore.Qt.AlignCenter)
self.Viewer.setObjectName("Viewer")
self.gridLayout.addWidget(self.Viewer, 0, 0, 1, 1)
self.horizontalLayout.addLayout(self.gridLayout)
self.retranslateUi(PhotoFrame)
QtCore.QMetaObject.connectSlotsByName(PhotoFrame)
def retranslateUi(self, PhotoFrame):
_translate = QtCore.QCoreApplication.translate
PhotoFrame.setWindowTitle(_translate("PhotoFrame", "Photo Frame"))
import resources_rc

57
ui_settings.py Normal file
View File

@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ui/settings.ui'
#
# Created by: PyQt5 UI code generator 5.5.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Settings(object):
def setupUi(self, Settings):
Settings.setObjectName("Settings")
Settings.resize(487, 120)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/icons/settings.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
Settings.setWindowIcon(icon)
self.txtPath = QtWidgets.QLineEdit(Settings)
self.txtPath.setGeometry(QtCore.QRect(110, 10, 341, 22))
self.txtPath.setAutoFillBackground(False)
self.txtPath.setReadOnly(True)
self.txtPath.setObjectName("txtPath")
self.btnOpen = QtWidgets.QPushButton(Settings)
self.btnOpen.setGeometry(QtCore.QRect(460, 10, 21, 21))
self.btnOpen.setObjectName("btnOpen")
self.label = QtWidgets.QLabel(Settings)
self.label.setGeometry(QtCore.QRect(30, 10, 71, 21))
self.label.setObjectName("label")
self.buttonBox = QtWidgets.QDialogButtonBox(Settings)
self.buttonBox.setGeometry(QtCore.QRect(160, 90, 165, 21))
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.label_2 = QtWidgets.QLabel(Settings)
self.label_2.setGeometry(QtCore.QRect(30, 50, 141, 21))
self.label_2.setObjectName("label_2")
self.spinUpdateInterval = QtWidgets.QSpinBox(Settings)
self.spinUpdateInterval.setGeometry(QtCore.QRect(160, 50, 51, 22))
self.spinUpdateInterval.setMinimum(1)
self.spinUpdateInterval.setMaximum(1000)
self.spinUpdateInterval.setProperty("value", 60)
self.spinUpdateInterval.setObjectName("spinUpdateInterval")
self.label_3 = QtWidgets.QLabel(Settings)
self.label_3.setGeometry(QtCore.QRect(220, 50, 56, 21))
self.label_3.setObjectName("label_3")
self.retranslateUi(Settings)
QtCore.QMetaObject.connectSlotsByName(Settings)
def retranslateUi(self, Settings):
_translate = QtCore.QCoreApplication.translate
Settings.setWindowTitle(_translate("Settings", "Settings"))
self.btnOpen.setText(_translate("Settings", "..."))
self.label.setText(_translate("Settings", "Source path"))
self.label_2.setText(_translate("Settings", "Change picture every"))
self.label_3.setText(_translate("Settings", "minutes"))
import resources_rc