Kodi refresh slideshow when folder content changes - slideshow

I use a Raspberry Pi to show a slideshow of pictures stored in a folder.
However, if I add pictures to the folder while the slideshow is running, the new pictures are not added to the slideshow.
How to achieve this behavior without interrupting the slideshow?

Add an autostart script to Kodi: It will run the slideshow automatically when Kodi starts and refresh the slideshow if the content of the folder changes.
This script comes from this answer: https://discourse.osmc.tv/t/refresh-picture-library/4867/13
# ~/.kodi/userdata/autoexec.py
# On Kodi startup, automatically start a Slideshow
# Refresh the slideshow if the content of the folder has been updated
import xbmc
from os import listdir
from time import sleep
xbmc.log('[refresh-slideshow] Starting')
monitor = xbmc.Monitor()
picfolder = "/PATH/TO/FOLDER"
# Generate list of images and start slideshow
l1 = listdir(picfolder)
xbmc.executebuiltin('SlideShow(%s)' %(picfolder))
xbmc.log('[refresh-slideshow] Slideshow started, monitoring')
# Wait for slideshow to start
sleep(5)
# Monitor during slideshow
while not monitor.abortRequested():
# If slideshow is still running, compare directory contents
if xbmc.getCondVisibility('Window.IsActive(slideshow)'):
l2 = listdir(picfolder)
# Restart slideshow if directory contents have changed
if l1 != l2:
xbmc.log('[refresh-slideshow] Folder contents changed, restarting slideshow')
xbmc.executebuiltin('SlideShow(%s)' %(picfolder))
l1 = l2
# If slideshow is no longer running (user has exited), exit script
else:
break
# Wait for 60 seconds, break if abort is requested
if monitor.waitForAbort(60):
break
xbmc.log('[refresh-slideshow] Finished, exiting')

Convert your screensaver script in Class based if not.
Create another class to update images using threading
import xbmc, xbmcgui, threading
class MyMonitor(xbmc.Monitor):
def __init__(self, *args, **kwargs):
self.action = kwargs['action']
# do some action here.......
def onScreensaverDeactivated(self):
self.action()
class ImgVideoUpdate(threading.Thread):
def __init__(self, *args, **kwargs):
self._get_items = kwargs['data']
threading.Thread.__init__(self)
self.stop = False
self.check_update = 50 # set thread to update images(in seconds)
self.Monitor = MyMonitor(action=self._exit)
def run(self):
while (not self.Monitor.abortRequested()) and (not self.stop):
count = 0
while count != self.check_update: # check for new images every 50seconds
xbmc.sleep(200)
count += 1
if self.Monitor.abortRequested() or self.stop:
return
self._get_items()
Now initialize ImgVideoUpdate in main ScreensaverWindow class from where screensaver is running
class ScreensaverWindow(xbmcgui.WindowXMLDialog):
def onInit(self):
xbmcgui.WindowXML.onInit(self)
self.volumeCtrl = None
# Get the videos to use as a screensaver
playlist = self.imagelist()
thread = ImgVideoUpdate(data=self.imagelist)
thread.start()
NOTE: Hope this helps.let me know if you face any issue.

Related

Why does threading not work in a Flask app when its run with gunicorn?

When files are uploaded to my flask app a thread id is generated. This is used by the frontend to later get the progress details of the file reading process (the cert_details route)
The cert_details route reads in data from a PDF using tesseract, opencv etc. This is quite a resource intensive process and can take a while to perform
While the process is running, I want to be able to update the front end with the progress (i.e how many files read in). Hence I have made a separate thread that has a value 'progress' that increases each time a file has been processed.
The app works without gunicorn i.e when the docker command is CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]
If I change it to use gunicorn CMD ["gunicorn","--timeout", “60", "--bind", "0.0.0.0:5000", "app:app"] , the progress bar doesn't get updated until the file reading process has finished (which defeats the purpose of the progress bar).
Why does the threading not work with gunicorn? Is there an alternative approach that I have to take or could it be related to thread starvation?
import threading
from flask import Flask, flash, request, redirect, url_for, jsonify, send_from_directory, send_file
from flask_cors import CORS, cross_origin
app = Flask(__name__)
CORS(app)
class ExportingThread(threading.Thread):
def __init__(self):
self.progress = 0
super().__init__()
exporting_threads = {}
#app.route('/uploads', methods=['POST'])
def upload_file():
if request.method == 'POST':
if 'files[]' not in request.files:
return {'status':'File upload unsuccesful'}
files = request.files.getlist('files[]')
for file in files:
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
else:
return {'status':'Non pdf file type uploaded'}
global thread_id
thread_id = random.randint(0, 10000)
return {'status':'sucessful'}
#app.route('/get_thread_id')
def get_thread():
global thread_id
return str(thread_id)
#app.route('/progress', methods=['POST'])
def progress():
data = request.json
thread_id = data['thread_id']
global exporting_threads
if thread_id in exporting_threads:
return str(exporting_threads[thread_id].progress)
else:
return '0'
#app.route('/cert_details', methods=['POST'])
def get_details():
data = request.json
if 'filenames' not in data:
return {'error':'filenames not found'}
else:
filenames =data['filenames']
details =[]
global exporting_threads
global thread_id
exporting_threads[thread_id] = ExportingThread()
exporting_threads[thread_id].start()
for file in filenames:
#this is the resource intensive process using ocr / opencv
details.append( get_certficate_details( UPLOAD_FOLDER_LOCATION + file, file ) )
exporting_threads[thread_id].progress += 1
return jsonify(file_details = details)

tkinter avoid GUI from freezing

I developed a simple Python application doing some stuff, then I decided to add a simple GUI using Tkinter.
The problem is that, while the I call a function called startprocess and begin doing stuff which is processor heavy and the window freezes.
I know it's a common problem and I've already read that I should use multithreads (very complicated, because the function updates the GUI too) or divide my code in different function, each one working for a little time. anyways is there any modification needed in below code to avoid GUI freezing?
import threading
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import os, datetime, sys, subprocess
import parselog_v1
# diplay messagebox window
def MessageBox(windowLable,msg):
messagebox.showinfo(windowLable, msg)
# check if Dir empty
def checkDirEmpty(work_path):
if os.path.isdir(work_path):
if not os.listdir(work_path):
print ("No Files found in directory")
MessageBox('Log Parser', 'No Files found in directory.')
else:
return True
# launch app in center of screen
def center_window(width=300, height=200):
# get screen width and height
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
# calculate position x and y coordinates
x = (screen_width/2) - (width/2)
y = (screen_height/2) - (height/2)
root.geometry('%dx%d+%d+%d' % (width, height, x, y))
# application frame
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse for logs"
self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.startProcess
self.Run_Main.pack(side='left',padx=0)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["fg"] = "red"
self.QUIT["command"] = self.quit
self.QUIT.pack(side='right',padx=5)
def startProcess(self):
global Src_foldername
Src_foldername = filedialog.askdirectory()
Src_foldername = Src_foldername.replace("/", "\\")
print("Source folder: " + Src_foldername)
if checkDirEmpty(Src_foldername):
# process logs
# multithread
print("Processing...")
self.refresh()
threading.Thread(target=parselog_v1.main(Src_foldername))
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
class IORedirector(object):
'''A general class for redirecting I/O to this Text widget.'''
def __init__(self, text_area):
self.text_area = text_area
class StdoutRedirector(IORedirector):
'''A class for redirecting stdout to this Text widget.'''
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=21, width=68)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
self.text.insert("end", "Begin by selecting log folder..." + "\n")
self.text.configure(state='disabled') # disable text editing
sys.stdout = (self) # to begin logging stdio to GUI
return
def write(self, txt):
self.text.configure(state='normal')
self.text.insert('end', txt)
self.text.configure(state='disabled')
root = tk.Tk()
root.resizable(width=False, height=False)
center_window(500, 300) # launch in center of screen
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
root.destroy()
You use thread in wrong way.
First: target= needs function name without () and arguments.
You can assign arguments to args= (it have to be tuple even if you have only one argument)
threading.Thread(target=parselog_v1.main, args=(Src_foldername,) )
Now your code runs parselog_v1.main as normal function, waits for result and it will assign this result as function name to taget= - so you have something like this:
result = parselog_v1.main(Src_foldername)
threading.Thread(target=result)
It stops mainloop so it can't get mouse/keyboard events, refresh window, etc. so it looks like window freeze.
Second: after you create thread correctly you have to start it
my_thread = threading.Thread(target=parselog_v1.main, args=(Src_foldername,) )
my_thread.start()

How to synchronize wxpython Gauge widget with threads in python?

My python project has multiple threads running. I want to add a gauge widget from the wxpython library to show the progress. I want the gauge to fill until my first thread completes. How do I achieve this? I am using Python 2.7 on Windows.
Use wx.CallAfter
def add_to_the_gauge(value):
your_gauge.Value += value
...
#in some thread
def some_thread_running():
wx.CallAfter(add_to_the_gauge, 2)
You need to post events from your worker thread to the main thread asking it to update the gauge, see this overview.
You can use some simple things
remember to import the module:
import os
and then put this on the frame
def __init__(self, *a, **k):
filename = 'game.exe'
self.timer = wx.Timer()
self.Bind(wx.EVT_TIMER, self.OnUpdateGauge, self.timer)
self.timer.Start()
self.proc = os.popen(filename) # start the process
def OnUpdateGauge(self, event):
if self.proc.poll() == None: # return None if still running and 0 if stopped
self.timer.Stop()
return
else:
'''do something with the gauge'''
hope those help

wxPython: How do you start a wxPython application from a wxPython application?

I am attempting to start a wxPython application but I want a banner to be displayed before it is started.
One way to do this is to start a wxPython application which in turn starts another wxPython application, the reason for doing it this way is since the App part of the second wxPython application needs to do some processing before starting and may take some time.
The issue is how do I start the other application and know that it has started?
Currently I do this which blocks for the entire GUI session:
subprocess.check_output(["python", "src/gui.py"], stderr=subprocess.STDOUT, shell=True)
I have attempted to do the following but the frame of the first application does not seem to close:
loadCompleted, EVT_LOAD_COMPLETED = wx.lib.newevent.NewEvent()
class MyRegion(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title="My Region")
self.label = wx.StaticText(self, label="Hello, World!")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.label, 0, wx.ALL, 5)
self.SetSizer(sizer)
self.startThread = Thread(target=self.Start)
self.startThread.start()
self.Bind(EVT_LOAD_COMPLETED, self.OnClose)
def OnClose(self, result):
self.Close()
def Start(self):
try:
subprocess.check_output(["python", "src/gui.py"], stderr=subprocess.STDOUT, shell=True)
except:
pass
wx.PostEvent(self, loadCompleted(result=(None)))
if __name__ == "__main__":
app = wx.App()
frame = MyRegion(None)
frame.Show()
app.MainLoop()
I don't think starting a second wxPython application is the way to go. Instead, I would just load the banner inside your frame's __init__ method, then do your processing. When the processing finishes, you can destroy the banner and show your main app. Here's some psuedo-code:
#
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Test")
banner = MyBanner()
# do a bunch of stuff
banner.Destroy() # or banner.Close()
self.Show()
Now if the processing takes a really long time, you can put that into a thread and have the thread send a message back to the UI that tells it that the thread is finished. When the app receives the message, it can close the banner in the handler and show the App at that point. Please note that you need to use a thread-safe method, such as wx.CallAfter or wx.PostEvent.
Check out the following articles for ideas:
http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
http://www.blog.pythonlibrary.org/2013/09/05/wxpython-2-9-and-the-newer-pubsub-api-a-simple-tutorial/

How to add url to bookmark?

I am using PyQt4 for creating a custom browser using QtWebKit, but I am stuck on saving bookmarks from the browser. Does anyone know how to achieve that?
You're a little vague on how you want this done, so I'll say we wanted to use a button imported from a UI file called bookmarks_Btn. You'll need to use the pickle module.
Here's the example code...
from PyQt4 import QtCore, QtGui, QtWebKit, uic
import pickle
class window(QtGui.QWidget):
def __init__(self, parent=None):
super(httpWidget, self).__init__(parent)
self.ui = uic.loadUi('mybrowser.ui')
self.ui.setupUi(self)
def bookmarksLoad(self):
print 'Loading bookmarks'
try:
bookOpen = open('bookmarks.txt', 'rb')
bookmarks = pickle.load(bookOpen)
bookOpen.close()
print bookmarks # Not necessary, but for example purposes
# Here you decide how "bookmarks" variable is displayed.
except:
bookOpen = open('bookmarks.txt', 'wb')
bookmarks = 'http://www.stackoverflow.com'
bookWrite = pickle.dump(bookmarks, bookOpen)
bookOpen.close()
print bookmarks # Not necessary, but for example purposes
# Here you decide how "bookmarks" variable is displayed.
QtCore.QObject.connect(self.ui.bookmarks_Btn, QtCore.SIGNAL('clicked()'), self.bookmarksLoad)
self.ui.show()
def bookmarks():
url = input 'Enter a URL: '
bookOpen = open('bookmarks.txt', 'wb')
bookOpen.write(url)
bookOpen.close()
print 'Website bookmarked!'
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
run = window()
bookmarks()
sys.exit(app.exec_())
# You add on here, for example, deleting bookmarks.
However, if you wanted it to be retrieved from an address bar (named address, make the following changes...
# In the bookmarks function...
global url # Add at beginning
# Remove the input line.
# Add at end of __init__ in window class:
url = self.ui.address.text()
global url
That's pretty much the basics. Please note I normally program in Python 3 and PyQt5 so if there are any errors let me know :)