diff --git a/gruene_signale.py b/gruene_signale.py index d5c1980..b5e059b 100755 --- a/gruene_signale.py +++ b/gruene_signale.py @@ -93,7 +93,7 @@ def readConfig(configFile=None): remoteURL = None except: remoteURL = None - + #try to read energySaving Info try: val = int(config.get('energy','mode')) @@ -105,7 +105,7 @@ def readConfig(configFile=None): needsWrite=True energySavingMode = val print("energySavingMode = ", energySavingMode) - + if energySavingMode > 0: #when energy saving is disabled we do not need this stuff h = 0 @@ -126,7 +126,7 @@ def readConfig(configFile=None): config.set('energy','start',"0:00") config.set('energy','stop',"0:00") needsWrite=True - + #we only needs this for wakeup blanked screen #oh - we also need it, in case clock is not synced to NTP try: @@ -139,7 +139,7 @@ def readConfig(configFile=None): config.set('energy','mode',"0") config.set('energy','start',"0:00") config.set('energy','stop',"0:00") - needsWrite=True + needsWrite=True if needsWrite == True: with open(configFile, 'w') as file: @@ -171,11 +171,11 @@ class RemoteData: ui.showInfo("Die Daten werden geladen:",True) #print("download to %s" % self.localZIP) with open(self.localZIP, "wb") as zipped: - for chunk in r.iter_content(chunk_size = 4096): + for chunk in r.iter_content(chunk_size = 4*1024): if chunk: zipped.write(chunk) length += len(chunk) - ui.showInfo("Daten werden geladen: %d kB" % (length/1024)) + ui.showInfo("Daten werden geladen: %d kB" % (length/1024), _clear = True) #print("%d kB" % (length/1024), end="\r") ui.showInfo("Daten wurden erfolgreich geladen: %d kB" % (length/1024)) time.sleep(1) @@ -198,7 +198,7 @@ class RemoteData: except: print("Error while unzipping the downloaded data.") return False - + if localPathExists: #move orignal localPath away shutil.move(localPath, localPath+".old") @@ -219,7 +219,7 @@ class WatchTime(): self.startuptime = time.localtime() self.timer = None # reoccuring call self.enegerySaving = False # on restart we surely do not run in energy saving mode - + def setReceiver(self,receiver=None): if receiver == None: return @@ -235,6 +235,9 @@ class WatchTime(): return t['h'] * 60 + t['m'] def checkNTPClock(self): + if sys.platform.startswith('darwin') or sys.platform.startswith('win'): + # on macOS and WIndows timedatectl is not available, we assume the clock is sync'ed + self.synced = True if self.synced != True: # timedatectl show tells me, if the clock is synchronized via NTP for line in subprocess.check_output(["timedatectl","show"]).split(): @@ -242,7 +245,7 @@ class WatchTime(): self.synced=True self.startuptime = time.localtime() print("NTP clock is synced") - + def checkTimeForShutdown(self): #check if it is time to sutdown if energySavingDuration > 0: @@ -274,9 +277,9 @@ class WatchTime(): if running > shallRun: #it runs longer than expected, so we shut down self.receiver.shutdown() - + return - + def checkTimeForBlank(self): isBlanked = self.receiver.window.blanked print( "Screen is Blanked?", isBlanked) @@ -307,7 +310,7 @@ class WatchTime(): else: #nothing to do - start and end time are equal return - + if needsBlank == None: return elif needsBlank == True and isBlanked == False: @@ -326,7 +329,7 @@ class WatchTime(): if now > 195 and now < 210: self.window.updateMedia() #else: - # if running > 24*60 + # if running > 24*60 def checkTimedEvents(self): #print("checkTimedEvents - mode:",energySavingMode, " duration:",energySavingDuration," start:",energySavingStart," stop:", energySavingEnd) @@ -350,7 +353,7 @@ class WatchTime(): else: #we should never reach here abort() - + def update(self): if self.receiver == None: return @@ -364,7 +367,7 @@ class WatchTime(): if DEBUG_PREVIEW == True: delay = 1000 self.timer = self.receiver.after(delay,self.update) - + # this class creates an invisible window to catch keboard events class HiddenRoot(tk.Tk): def __init__(self): @@ -377,16 +380,16 @@ class HiddenRoot(tk.Tk): self.window = MySlideShow(self) self.window.startSlideShow() - + def nextMedia(self): self.window.nextMedia() - + def previousMedia(self): self.window.pixNum = self.window.pixNum -2 if self.window.pixNum < 0: self.window.pixNum = self.window.pixNum + len(self.window.mediaList) self.window.nextMedia() - + def shutdown(self): subprocess.check_call(["shutdown","--no-wall","+1"]) self.destroy() @@ -427,27 +430,46 @@ class Mediafile: img.save(filename) w,h = img.size img.close() + subname = os.path.basename(filename).split('.')[0] + if '_#s' in subname: + duration = int(subname.split('_#s')[-1]) + if duration > 0: + self.duration = duration*1000 self.valid = True if DEBUG_PREVIEW == True: - print("image file: "+os.path.basename(filename)+"(%d"%w+"x%d)"%h, "resized:",resize) + if self.duration == 0: + print("Bild Datei: "+os.path.basename(filename)+" (%d"%w+"x%d)"%h, + "skaliert:",int(resize), "Dauer: standard") + else: + print("Bild Datei: "+os.path.basename(filename)+" (%d"%w+"x%d)"%h, + "skaliert:",int(resize), "Dauer: %d Sekunden" % (self.duration/1000)) elif extension in movie_extensions: media = caller.instance.media_new(filename) media.parse() self.duration = media.get_duration() self.valid = True if DEBUG_PREVIEW == True: - print("movie file: "+os.path.basename(filename)+ " (%d Sekunden)"%(self.duration/1000)) + print("Film Datei: "+os.path.basename(filename)+ " (Dauer %d Sekunden)"%(self.duration/1000)) else: - print("unknwon file type: "+os.path.basename(filename)) + print("unbekannte Datei: "+os.path.basename(filename)) +if sys.platform.startswith('darwin'): + from ctypes import c_void_p, cdll + dylib = cdll.LoadLibrary('libtk8.6.dylib') + _GetNSView = dylib.TkMacOSXGetRootControl + _GetNSView.restype = c_void_p + _GetNSView.argtypes = (c_void_p,) + # print(_GetNSView) + del dylib class MySlideShow(tk.Toplevel): def __init__(self, *args, **kwargs): tk.Toplevel.__init__(self, *args, **kwargs) - #remove window decorations + #remove window decorations self.overrideredirect(True) self.info = None self.infoText = tk.StringVar() + self.infoText.set('') #by default info widget is hidden self.infoHidden = True self.bg = (0,0,0) @@ -461,25 +483,31 @@ class MySlideShow(tk.Toplevel): #set the geometry of the playback window self.scr_w, self.scr_h = self.winfo_screenwidth(), self.winfo_screenheight() if DEBUG_PREVIEW == None or DEBUG_PREVIEW == True: - self.scr_w = int(self.scr_w / 4) - self.scr_h = int(self.scr_h / 4) - self.scr_t = self.scr_h*3 - 10 - self.scr_l = self.scr_w*3 - 10 - self.font = "Courier 8" + self.scr_w = int(self.scr_w / 3.0) + self.scr_h = int(self.scr_w * 9.0 / 16.0) + self.scr_t = self.winfo_screenheight() - self.scr_h - 10 + self.scr_l = self.winfo_screenwidth() - self.scr_w - 10 + self.font = "Courier 10" else: self.scr_t = 0 self.scr_l = 0 self.font = "Courier 12" # hide the mouse cursor if not in debug mode self.config(cursor="none") + self.wm_attributes('-fullscreen',True) + self.wm_attributes('-topmost', True) + emptyMenu = tk.Menu(self) + self['menu'] = None + emptyMenu.delete(0,tk.END) #This creates the widget where files are played back self.player = None self.videopanel = tk.Frame(self, bg="black") self.videopanel.pack(side="top",fill=tk.BOTH,expand=1) + self.videopanel.update_idletasks() #VLC player init - self.instance = vlc.Instance("--no-xlib --quiet --fullscreen --") + self.instance = vlc.Instance("--quiet --fullscreen --") self.player = self.instance.media_player_new() self.player.video_set_scale(0) #self.player.video_set_aspect_ratio('16:9') @@ -489,8 +517,19 @@ class MySlideShow(tk.Toplevel): #setup the window self.wm_geometry("{}x{}+{}+{}".format(self.scr_w, self.scr_h,self.scr_l,self.scr_t)) - self.player.set_xwindow(self.GetHandle()) # this line messes up windows - + # different platforms require different assignment below + if sys.platform.startswith('linux'): + self.player.set_xwindow(self.GetHandle()) # this line messes up windows + elif sys.platform.startswith('darwin'): + # print(_GetNSView(self.GetHandle())) + self.player.set_nsobject(_GetNSView(self.GetHandle())) + elif sys.platform.startswith('win'): + self.player.set_hwnd(self.GetHandle()) # should work on windows + else: + print( "Unbekannte Platform (%s), das Skript wird abgebrochen:" % sys.platform ) + exit(-1) + + #some brief internal initializers self.mediaList = list() self.pixNum = 0 @@ -511,21 +550,31 @@ class MySlideShow(tk.Toplevel): # This creates an info widget if self.infoHidden == True or self.info == None: self.infoHidden = False - self.info = tk.Label(self, bg="#00FF44", font=self.font, height=-1, width=-1, textvariable=self.infoText, wraplength=self.scr_w-16) - self.info.place(x=8,y=6) + self.info = tk.Label(self, bg="#2F6B1A", font=self.font, height=-1, width=-1, textvariable=self.infoText, wraplength=self.scr_w-16) + self.info.place(x=16,y=32) else: self.infoHidden = True self.info.destroy() self.info = None - - - def showInfo(self,_text,_force=False): + + + def showInfo(self,_text,_force=False,_clear=False): if self.infoHidden == True and _force == True: self.toggleInfo() - self.infoText.set(_text) + if len(_text.strip()) == 0: + return + if _clear == True: + lines = [] + else: + lines = self.infoText.get().strip().split('\n') + if len(lines) > 18: + lines = lines[1:] + lines.append(_text.strip()) + self.infoText.set('\n'.join(lines).strip()) self.update() - + def hideInfo(self): + self.infoText.set('') if self.infoHidden == False: self.toggleInfo() @@ -599,7 +648,7 @@ class MySlideShow(tk.Toplevel): if self.timer != None: self.after_cancel(self.timer) self.timer = None - + def togglePlayback(self): if self.paused == True: self.resumePlayback() @@ -625,7 +674,7 @@ class MySlideShow(tk.Toplevel): self.resumePlayback() self.inited = True self.nextMedia() - + def blankScreenOn(self): #energy savings - start blank screen self.pausePlayback() @@ -635,7 +684,7 @@ class MySlideShow(tk.Toplevel): #while debugging we want screen blanking turn off right away time.sleep(5) self.blankScreenOff() - + def blankScreenOff(self): #energy savings - end blank screen self.blanked = False @@ -650,7 +699,11 @@ readConfig() try: slideShow = HiddenRoot() -except: +except Exception as e: + if hasattr(e, 'message'): + print(e.message) + else: + print(e) print("Es ist ein Tcl-Fehler aufgetreten. Das Skript muss im Desktop-Modus gestartet werden.") exit(0) timedEvents = WatchTime() diff --git a/setup.py b/setup.py index 391a65d..2ebff41 100755 --- a/setup.py +++ b/setup.py @@ -17,6 +17,16 @@ try: except: modules.append("python-vlc") +try: + import PIL +except: + modules.append('pillow') + +try: + import requests +except: + modules.append('requests') + master=None # try: # import crontab @@ -88,7 +98,7 @@ except: if "noautostart" in sys.argv: removeAutostart() print("Das Autostart-Objekt wurde entfernt.") - + else: print("Es ist ein Tcl-Fehler aufgetreten. Das Skript muss im Desktop-Modus gestartet werden.") print("""Folgende Kommandos sind erlaubt: @@ -195,7 +205,7 @@ def setDirty(action=None, ign=None): return True def setEntryDirty(action=None, ign=None): - #this gets called + #this gets called global isDirty #print("setDirty() called", sys._getframe().f_back.f_code.co_name, "###", action, "###", ign) if action != None and int(action) >= 0: @@ -241,20 +251,20 @@ def readConfig(configFile=None): except: val = None remoteURL.set("") - + #read energ - mode try: energyMode.set(int(config.get('energy','mode'))) except: energyMode.set(0) - + #read energy - start try: val = config.get('energy', 'start') except: val = "" energyStart.set(val) - + #read energy - stop try: val = config.get('energy', 'stop') @@ -317,7 +327,7 @@ def buildGUI_1(): row.pack(side=tk.TOP,padx=dx,pady=dy,expand=tk.YES,fill=tk.X) lab.pack(side=tk.LEFT,pady=dy,padx=dx) obj.pack(side=tk.RIGHT,expand=tk.YES,fill=tk.X,padx=dx) - + def buildGUI_2(): #add autostart checkbox doAutostart.set(checkAutostartfile()) @@ -390,7 +400,7 @@ def buildGUI_6(): lab.pack(side=tk.LEFT,pady=dy,padx=dx) obj2=tk.Entry(row3,name="stop",textvariable=energyStop,validate="all",validatecommand=(timeEntryCallback, '%P', '%S', "%V", "%W")) obj2.pack(side=tk.LEFT,fill=tk.X,padx=dx) - + def buildGUI_7(): #add checkbox for Debug Preview Mode row=tk.Frame(master,bd=1,relief=tk.SUNKEN) @@ -418,8 +428,8 @@ def buildGUI(): buildGUI_6() buildGUI_7() buildGUI_8() - - + + readConfig() master.minsize(600,300)