def __init__(self): self.pipresents_issue="1.2" self.nonfull_window_width = 0.6 # proportion of width self.nonfull_window_height= 0.6 # proportion of height self.nonfull_window_x = 0 # position of top left corner self.nonfull_window_y=0 # position of top left corner StopWatch.global_enable=False #**************************************** # Initialisation # *************************************** # get command line options self.options=command_options() # get pi presents code directory pp_dir=sys.path[0] self.pp_dir=pp_dir if not os.path.exists(pp_dir+"/pipresents.py"): tkMessageBox.showwarning("Pi Presents","Bad Application Directory") exit() #Initialise logging Monitor.log_path=pp_dir self.mon=Monitor() self.mon.on() if self.options['debug']==True: Monitor.global_enable=True else: Monitor.global_enable=False self.mon.log (self, "Pi Presents is starting") self.mon.log (self," OS and separator:" + os.name +' ' + os.sep) self.mon.log(self,"sys.path[0] - location of code: "+sys.path[0]) # self.mon.log(self,"os.getenv('HOME') - user home directory (not used): " + os.getenv('HOME')) # self.mon.log(self,"os.path.expanduser('~') - user home directory: " + os.path.expanduser('~')) # optional other classes used self.ppio=None self.tod=None #get profile path from -p option if self.options['profile']<>"": self.pp_profile_path="/pp_profiles/"+self.options['profile'] else: self.pp_profile_path = "/pp_profiles/pp_profile" #get directory containing pp_home from the command, if self.options['home'] =="": home = os.path.expanduser('~')+ os.sep+"pp_home" else: home = self.options['home'] + os.sep+ "pp_home" self.mon.log(self,"pp_home directory is: " + home) #check if pp_home exists. # try for 10 seconds to allow usb stick to automount # fall back to pipresents/pp_home self.pp_home=pp_dir+"/pp_home" found=False for i in range (1, 10): self.mon.log(self,"Trying pp_home at: " + home + " (" + str(i)+')') if os.path.exists(home): found=True self.pp_home=home break time.sleep (1) if found==True: self.mon.log(self,"Found Requested Home Directory, using pp_home at: " + home) else: self.mon.log(self,"FAILED to find requested home directory, using default to display error message: " + self.pp_home) #check profile exists, if not default to error profile inside pipresents self.pp_profile=self.pp_home+self.pp_profile_path if os.path.exists(self.pp_profile): self.mon.log(self,"Found Requested profile - pp_profile directory is: " + self.pp_profile) else: self.pp_profile=pp_dir+"/pp_home/pp_profiles/pp_profile" self.mon.log(self,"FAILED to find requested profile, using default to display error message: pp_profile") if self.options['verify']==True: val =Validator() if val.validate_profile(None,pp_dir,self.pp_home,self.pp_profile,self.pipresents_issue,False) == False: tkMessageBox.showwarning("Pi Presents","Validation Failed") exit() # open the resources self.rr=ResourceReader() # read the file, done once for all the other classes to use. if self.rr.read(pp_dir,self.pp_home,self.pp_profile)==False: self.end('error','cannot find resources.cfg') #initialise and read the showlist in the profile self.showlist=ShowList() self.showlist_file= self.pp_profile+ "/pp_showlist.json" if os.path.exists(self.showlist_file): self.showlist.open_json(self.showlist_file) else: self.mon.err(self,"showlist not found at "+self.showlist_file) self.end('error','showlist not found') # check profile and Pi Presents issues are compatible if float(self.showlist.sissue())<>float(self.pipresents_issue): self.mon.err(self,"Version of profile " + self.showlist.sissue() + " is not same as Pi Presents, must exit") self.end('error','wrong version of profile') # get the 'start' show from the showlist index = self.showlist.index_of_show('start') if index >=0: self.showlist.select(index) self.starter_show=self.showlist.selected_show() else: self.mon.err(self,"Show [start] not found in showlist") self.end('error','start show not found') # ******************** # SET UP THE GUI # ******************** #turn off the screenblanking and saver if self.options['noblank']==True: call(["xset","s", "off"]) call(["xset","s", "-dpms"]) self.root=Tk() self.title='Pi Presents - '+ self.pp_profile self.icon_text= 'Pi Presents' self.root.title(self.title) self.root.iconname(self.icon_text) self.root.config(bg='black') # get size of the screen self.screen_width = self.root.winfo_screenwidth() self.screen_height = self.root.winfo_screenheight() # set window dimensions and decorations if self.options['fullscreen']==True: self.root.attributes('-fullscreen', True) os.system('unclutter &') self.window_width=self.screen_width self.window_height=self.screen_height self.window_x=0 self.window_y=0 self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y)) self.root.attributes('-zoomed','1') else: self.window_width=int(self.screen_width*self.nonfull_window_width) self.window_height=int(self.screen_height*self.nonfull_window_height) self.window_x=self.nonfull_window_x self.window_y=self.nonfull_window_y self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y)) #canvas covers the whole window self.canvas_height=self.screen_height self.canvas_width=self.screen_width # make sure focus is set. self.root.focus_set() #define response to main window closing. self.root.protocol ("WM_DELETE_WINDOW", self.exit_pressed) #setup a canvas onto which will be drawn the images or text self.canvas = Canvas(self.root, bg='black') self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=0) # self.canvas.pack() self.canvas.place(x=0,y=0) self.canvas.focus_set() # **************************************** # INITIALISE THE INPUT DRIVERS # **************************************** # looks after bindings between symbolic names and internal operations controlsmanager=ControlsManager() if controlsmanager.read(pp_dir,self.pp_home,self.pp_profile)==False: self.end('error','cannot find or error in controls.cfg.cfg') else: controlsmanager.parse_defaults() # each driver takes a set of inputs, binds them to symboic names # and sets up a callback which returns the symbolic name when an input event occurs/ # use keyboard driver to bind keys to symbolic names and to set up callback kbd=KbdDriver() if kbd.read(pp_dir,self.pp_home,self.pp_profile)==False: self.end('error','cannot find or error in keys.cfg') kbd.bind_keys(self.root,self.input_pressed) self.sr=ScreenDriver() # read the screen click area config file if self.sr.read(pp_dir,self.pp_home,self.pp_profile)==False: self.end('error','cannot find screen.cfg') # create click areas on the canvas, must be polygon as outline rectangles are not filled as far as find_closest goes reason,message = self.sr.make_click_areas(self.canvas,self.input_pressed) if reason=='error': self.mon.err(self,message) self.end('error',message) # **************************************** # INITIALISE THE APPLICATION AND START # **************************************** self.shutdown_required=False #kick off GPIO if enabled by command line option if self.options['gpio']==True: from pp_gpio import PPIO # initialise the GPIO self.ppio=PPIO() # PPIO.gpio_enabled=False if self.ppio.init(pp_dir,self.pp_home,self.pp_profile,self.canvas,50,self.gpio_pressed)==False: self.end('error','gpio error') # and start polling gpio self.ppio.poll() #kick off the time of day scheduler self.tod=TimeOfDay() self.tod.init(pp_dir,self.pp_home,self.canvas,500) self.tod.poll() # Create list of start shows initialise them and then run them self.run_start_shows() #start tkinter self.root.mainloop( )
def __init__(self): gc.set_debug(gc.DEBUG_UNCOLLECTABLE|gc.DEBUG_INSTANCES|gc.DEBUG_OBJECTS|gc.DEBUG_SAVEALL) self.pipresents_issue="1.3.2" self.pipresents_minorissue = '1.3.2a' # position and size of window without -f command line option self.nonfull_window_width = 0.45 # proportion of width self.nonfull_window_height= 0.7 # proportion of height self.nonfull_window_x = 0 # position of top left corner self.nonfull_window_y=0 # position of top left corner StopWatch.global_enable=False # set up the handler for SIGTERM signal.signal(signal.SIGTERM,self.handle_sigterm) # **************************************** # Initialisation # *************************************** # get command line options self.options=command_options() # get Pi Presents code directory pp_dir=sys.path[0] self.pp_dir=pp_dir if not os.path.exists(pp_dir+"/pipresents.py"): if self.options['manager'] is False: tkMessageBox.showwarning("Pi Presents","Bad Application Directory") exit(102) # Initialise logging and tracing Monitor.log_path=pp_dir self.mon=Monitor() # Init in PiPresents only self.mon.init() # uncomment to enable control of logging from within a class # Monitor.enable_in_code = True # enables control of log level in the code for a class - self.mon.set_log_level() # make a shorter list to log/trace only some classes without using enable_in_code. Monitor.classes = ['PiPresents', 'HyperlinkShow','RadioButtonShow','ArtLiveShow','ArtMediaShow','MediaShow','LiveShow','MenuShow', 'GapShow','Show','ArtShow', 'AudioPlayer','BrowserPlayer','ImagePlayer','MenuPlayer','MessagePlayer','VideoPlayer','Player', 'MediaList','LiveList','ShowList', 'PathManager','ControlsManager','ShowManager','PluginManager', 'MplayerDriver','OMXDriver','UZBLDriver', 'KbdDriver','GPIODriver','TimeOfDay','ScreenDriver','Animate','OSCDriver', 'Network','Mailer' ] # Monitor.classes=['PiPresents','MediaShow','GapShow','Show','VideoPlayer','Player','OMXDriver'] # get global log level from command line Monitor.log_level = int(self.options['debug']) Monitor.manager = self.options['manager'] # print self.options['manager'] self.mon.newline(3) self.mon.sched (self, "Pi Presents is starting, Version:"+self.pipresents_minorissue + ' at '+time.strftime("%Y-%m-%d %H:%M.%S")) self.mon.log (self, "Pi Presents is starting, Version:"+self.pipresents_minorissue+ ' at '+time.strftime("%Y-%m-%d %H:%M.%S")) # self.mon.log (self," OS and separator:" + os.name +' ' + os.sep) self.mon.log(self,"sys.path[0] - location of code: "+sys.path[0]) # log versions of Raspbian and omxplayer, and GPU Memory with open("/boot/issue.txt") as file: self.mon.log(self,'\nRaspbian: '+file.read()) self.mon.log(self,'\n'+check_output(["omxplayer", "-v"])) self.mon.log(self,'\nGPU Memory: '+check_output(["vcgencmd", "get_mem", "gpu"])) if "DESKTOP_SESSION" not in os.environ: print 'Pi Presents must be run from the Desktop' self.mon.log(self,'Pi Presents must be run from the Desktop') self.mon.finish() sys.exit(102) else: self.mon.log(self,'Desktop is '+ os.environ['DESKTOP_SESSION']) # optional other classes used self.root=None self.ppio=None self.tod=None self.animate=None self.gpiodriver=None self.oscdriver=None self.osc_enabled=False self.gpio_enabled=False self.tod_enabled=False self.email_enabled=False if os.geteuid() == 0: self.mon.err(self,'Do not run Pi Presents with sudo') self.end('error','Do not run Pi Presents with sudo') user=os.getenv('USER') self.mon.log(self,'User is: '+ user) # self.mon.log(self,"os.getenv('HOME') - user home directory (not used): " + os.getenv('HOME')) # does not work # self.mon.log(self,"os.path.expanduser('~') - user home directory: " + os.path.expanduser('~')) # does not work # check network is available self.network_connected=False self.network_details=False self.interface='' self.ip='' self.unit='' # sets self.network_connected and self.network_details self.init_network() # start the mailer and send email when PP starts self.email_enabled=False if self.network_connected is True: self.init_mailer() if self.email_enabled is True and self.mailer.email_at_start is True: subject= '[Pi Presents] ' + self.unit + ': PP Started on ' + time.strftime("%Y-%m-%d %H:%M") message = time.strftime("%Y-%m-%d %H:%M") + '\nUnit: ' + self.unit + ' Profile: '+ self.options['profile']+ '\n ' + self.interface + '\n ' + self.ip self.send_email('start',subject,message) # get profile path from -p option if self.options['profile'] != '': self.pp_profile_path="/pp_profiles/"+self.options['profile'] else: self.mon.err(self,"Profile not specified in command ") self.end('error','Profile not specified with the commands -p option') # get directory containing pp_home from the command, if self.options['home'] == "": home = os.sep+ 'home' + os.sep + user + os.sep+"pp_home" else: home = self.options['home'] + os.sep+ "pp_home" self.mon.log(self,"pp_home directory is: " + home) # check if pp_home exists. # try for 10 seconds to allow usb stick to automount found=False for i in range (1, 10): self.mon.log(self,"Trying pp_home at: " + home + " (" + str(i)+')') if os.path.exists(home): found=True self.pp_home=home break time.sleep (1) if found is True: self.mon.log(self,"Found Requested Home Directory, using pp_home at: " + home) else: self.mon.err(self,"Failed to find pp_home directory at " + home) self.end('error',"Failed to find pp_home directory at " + home) # check profile exists self.pp_profile=self.pp_home+self.pp_profile_path if os.path.exists(self.pp_profile): self.mon.sched(self,"Running profile: " + self.pp_profile_path) self.mon.log(self,"Found Requested profile - pp_profile directory is: " + self.pp_profile) else: self.mon.err(self,"Failed to find requested profile: "+ self.pp_profile) self.end('error',"Failed to find requested profile: "+ self.pp_profile) self.mon.start_stats(self.options['profile']) if self.options['verify'] is True: val =Validator() if val.validate_profile(None,pp_dir,self.pp_home,self.pp_profile,self.pipresents_issue,False) is False: self.mon.err(self,"Validation Failed") self.end('error','Validation Failed') # initialise and read the showlist in the profile self.showlist=ShowList() self.showlist_file= self.pp_profile+ "/pp_showlist.json" if os.path.exists(self.showlist_file): self.showlist.open_json(self.showlist_file) else: self.mon.err(self,"showlist not found at "+self.showlist_file) self.end('error',"showlist not found at "+self.showlist_file) # check profile and Pi Presents issues are compatible if self.showlist.profile_version() != self.pipresents_version(): self.mon.err(self,"Version of showlist " + self.showlist.profile_version_string + " is not same as Pi Presents") self.end('error',"Version of showlist " + self.showlist.profile_version_string + " is not same as Pi Presents") # get the 'start' show from the showlist index = self.showlist.index_of_show('start') if index >=0: self.showlist.select(index) self.starter_show=self.showlist.selected_show() else: self.mon.err(self,"Show [start] not found in showlist") self.end('error',"Show [start] not found in showlist") # ******************** # SET UP THE GUI # ******************** # turn off the screenblanking and saver if self.options['noblank'] is True: call(["xset","s", "off"]) call(["xset","s", "-dpms"]) self.root=Tk() self.title='Pi Presents - '+ self.pp_profile self.icon_text= 'Pi Presents' self.root.title(self.title) self.root.iconname(self.icon_text) self.root.config(bg=self.starter_show['background-colour']) self.mon.log(self, 'monitor screen dimensions are ' + str(self.root.winfo_screenwidth()) + ' x ' + str(self.root.winfo_screenheight()) + ' pixels') if self.options['screensize'] =='': self.screen_width = self.root.winfo_screenwidth() self.screen_height = self.root.winfo_screenheight() else: reason,message,self.screen_width,self.screen_height=self.parse_screen(self.options['screensize']) if reason =='error': self.mon.err(self,message) self.end('error',message) self.mon.log(self, 'forced screen dimensions (--screensize) are ' + str(self.screen_width) + ' x ' + str(self.screen_height) + ' pixels') # set window dimensions and decorations if self.options['fullscreen'] is False: self.window_width=int(self.root.winfo_screenwidth()*self.nonfull_window_width) self.window_height=int(self.root.winfo_screenheight()*self.nonfull_window_height) self.window_x=self.nonfull_window_x self.window_y=self.nonfull_window_y self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y)) else: self.window_width=self.screen_width self.window_height=self.screen_height self.root.attributes('-fullscreen', True) os.system('unclutter &') self.window_x=0 self.window_y=0 self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y)) self.root.attributes('-zoomed','1') # canvas cover the whole screen whatever the size of the window. self.canvas_height=self.screen_height self.canvas_width=self.screen_width # make sure focus is set. self.root.focus_set() # define response to main window closing. self.root.protocol ("WM_DELETE_WINDOW", self.handle_user_abort) # setup a canvas onto which will be drawn the images or text self.canvas = Canvas(self.root, bg=self.starter_show['background-colour']) if self.options['fullscreen'] is True: self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=0) else: self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=1, highlightcolor='yellow') self.canvas.place(x=0,y=0) # self.canvas.config(bg='black') self.canvas.focus_set() # **************************************** # INITIALISE THE INPUT DRIVERS # **************************************** # each driver takes a set of inputs, binds them to symboic names # and sets up a callback which returns the symbolic name when an input event occurs/ # use keyboard driver to bind keys to symbolic names and to set up callback kbd=KbdDriver() if kbd.read(pp_dir,self.pp_home,self.pp_profile) is False: self.end('error','cannot find, or error in keys.cfg') kbd.bind_keys(self.root,self.handle_input_event) self.sr=ScreenDriver() # read the screen click area config file reason,message = self.sr.read(pp_dir,self.pp_home,self.pp_profile) if reason == 'error': self.end('error','cannot find, or error in screen.cfg') # create click areas on the canvas, must be polygon as outline rectangles are not filled as far as find_closest goes # click areas are made on the Pi Presents canvas not the show canvases. reason,message = self.sr.make_click_areas(self.canvas,self.handle_input_event) if reason == 'error': self.mon.err(self,message) self.end('error',message) # **************************************** # INITIALISE THE APPLICATION AND START # **************************************** self.shutdown_required=False self.terminate_required=False self.exitpipresents_required=False # delete omxplayer dbus files # if os.path.exists("/tmp/omxplayerdbus.{}".format(user)): # os.remove("/tmp/omxplayerdbus.{}".format(user)) # if os.path.exists("/tmp/omxplayerdbus.{}.pid".format(user)): # os.remove("/tmp/omxplayerdbus.{}.pid".format(user)) # kick off GPIO if enabled by command line option self.gpio_enabled=False if os.path.exists(self.pp_profile + os.sep + 'pp_io_config'+os.sep+ 'gpio.cfg'): # initialise the GPIO self.gpiodriver=GPIODriver() reason,message=self.gpiodriver.init(pp_dir,self.pp_home,self.pp_profile,self.canvas,50,self.handle_input_event) if reason == 'error': self.end('error',message) else: self.gpio_enabled=True # and start polling gpio self.gpiodriver.poll() # kick off animation sequencer self.animate = Animate() self.animate.init(pp_dir,self.pp_home,self.pp_profile,self.canvas,200,self.handle_output_event) self.animate.poll() #create a showmanager ready for time of day scheduler and osc server show_id=-1 self.show_manager=ShowManager(show_id,self.showlist,self.starter_show,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home) # first time through set callback to terminate Pi Presents if all shows have ended. self.show_manager.init(self.canvas,self.all_shows_ended_callback,self.handle_command,self.showlist) # Register all the shows in the showlist reason,message=self.show_manager.register_shows() if reason == 'error': self.mon.err(self,message) self.end('error',message) # Init OSCDriver, read config and start OSC server self.osc_enabled=False if self.network_connected is True: if os.path.exists(self.pp_profile + os.sep + 'pp_io_config'+ os.sep + 'osc.cfg'): self.oscdriver=OSCDriver() reason,message=self.oscdriver.init(self.pp_profile,self.handle_command,self.handle_input_event,self.e_osc_handle_output_event) if reason == 'error': self.mon.err(self,message) self.end('error',message) else: self.osc_enabled=True self.root.after(1000,self.oscdriver.start_server()) # enable ToD scheduler if schedule exists if os.path.exists(self.pp_profile + os.sep + 'schedule.json'): self.tod_enabled = True else: self.tod_enabled=False # warn if the network not available when ToD required if self.tod_enabled is True and self.network_connected is False: self.mon.warn(self,'Network not connected so Time of Day scheduler may be using the internal clock') # warn about start shows and scheduler if self.starter_show['start-show']=='' and self.tod_enabled is False: self.mon.sched(self,"No Start Shows in Start Show and no shows scheduled") self.mon.warn(self,"No Start Shows in Start Show and no shows scheduled") if self.starter_show['start-show'] !='' and self.tod_enabled is True: self.mon.sched(self,"Start Shows in Start Show and shows scheduled - conflict?") self.mon.warn(self,"Start Shows in Start Show and shows scheduled - conflict?") # run the start shows self.run_start_shows() # kick off the time of day scheduler which may run additional shows if self.tod_enabled is True: self.tod=TimeOfDay() self.tod.init(pp_dir,self.pp_home,self.pp_profile,self.root,self.handle_command) self.tod.poll() # start Tkinters event loop self.root.mainloop( )
def __init__(self): self.pipresents_issue = "1.2" self.pipresents_minorissue = '1.2.3f' self.nonfull_window_width = 0.5 # proportion of width self.nonfull_window_height = 0.6 # proportion of height self.nonfull_window_x = 0 # position of top left corner self.nonfull_window_y = 0 # position of top left corner StopWatch.global_enable = False #**************************************** # Initialisation # *************************************** # get command line options self.options = command_options() # get pi presents code directory pp_dir = sys.path[0] self.pp_dir = pp_dir if not os.path.exists(pp_dir + "/pipresents.py"): tkMessageBox.showwarning("Pi Presents", "Bad Application Directory") exit() #Initialise logging Monitor.log_path = pp_dir self.mon = Monitor() self.mon.on() # 0 - errors only # 1 - errors and warnings # 2 - everything if self.options['debug'] == True: Monitor.global_enable = 2 else: Monitor.global_enable = 0 # UNCOMMENT THIS TO LOG WARNINGS AND ERRORS ONLY # Monitor.global_enable=1 self.mon.log( self, "\n\n\n\n\n*****************\nPi Presents is starting, Version:" + self.pipresents_minorissue) self.mon.log(self, "Version: " + self.pipresents_minorissue) self.mon.log(self, " OS and separator:" + os.name + ' ' + os.sep) self.mon.log(self, "sys.path[0] - location of code: " + sys.path[0]) # self.mon.log(self,"os.getenv('HOME') - user home directory (not used): " + os.getenv('HOME')) # self.mon.log(self,"os.path.expanduser('~') - user home directory: " + os.path.expanduser('~')) # optional other classes used self.ppio = None self.tod = None #get profile path from -p option if self.options['profile'] <> "": self.pp_profile_path = "/pp_profiles/" + self.options['profile'] else: self.pp_profile_path = "/pp_profiles/pp_profile" #get directory containing pp_home from the command, if self.options['home'] == "": home = os.path.expanduser('~') + os.sep + "pp_home" else: home = self.options['home'] + os.sep + "pp_home" self.mon.log(self, "pp_home directory is: " + home) #check if pp_home exists. # try for 10 seconds to allow usb stick to automount # fall back to pipresents/pp_home self.pp_home = pp_dir + "/pp_home" found = False for i in range(1, 10): self.mon.log(self, "Trying pp_home at: " + home + " (" + str(i) + ')') if os.path.exists(home): found = True self.pp_home = home break time.sleep(1) if found == True: self.mon.log( self, "Found Requested Home Directory, using pp_home at: " + home) else: self.mon.log( self, "FAILED to find requested home directory, using default to display error message: " + self.pp_home) #check profile exists, if not default to error profile inside pipresents self.pp_profile = self.pp_home + self.pp_profile_path if os.path.exists(self.pp_profile): self.mon.log( self, "Found Requested profile - pp_profile directory is: " + self.pp_profile) else: self.pp_profile = pp_dir + "/pp_home/pp_profiles/pp_profile" self.mon.log( self, "FAILED to find requested profile, using default to display error message: pp_profile" ) if self.options['verify'] == True: val = Validator() if val.validate_profile(None, pp_dir, self.pp_home, self.pp_profile, self.pipresents_issue, False) == False: tkMessageBox.showwarning("Pi Presents", "Validation Failed") exit() # open the resources self.rr = ResourceReader() # read the file, done once for all the other classes to use. if self.rr.read(pp_dir, self.pp_home, self.pp_profile) == False: self.end('error', 'cannot find resources.cfg') #initialise and read the showlist in the profile self.showlist = ShowList() self.showlist_file = self.pp_profile + "/pp_showlist.json" if os.path.exists(self.showlist_file): self.showlist.open_json(self.showlist_file) else: self.mon.err(self, "showlist not found at " + self.showlist_file) self.end('error', 'showlist not found') # check profile and Pi Presents issues are compatible if float(self.showlist.sissue()) <> float(self.pipresents_issue): self.mon.err( self, "Version of profile " + self.showlist.sissue() + " is not same as Pi Presents, must exit") self.end('error', 'wrong version of profile') # get the 'start' show from the showlist index = self.showlist.index_of_show('start') if index >= 0: self.showlist.select(index) self.starter_show = self.showlist.selected_show() else: self.mon.err(self, "Show [start] not found in showlist") self.end('error', 'start show not found') # ******************** # SET UP THE GUI # ******************** #turn off the screenblanking and saver if self.options['noblank'] == True: call(["xset", "s", "off"]) call(["xset", "s", "-dpms"]) self.root = Tk() self.title = 'Pi Presents - ' + self.pp_profile self.icon_text = 'Pi Presents' self.root.title(self.title) self.root.iconname(self.icon_text) self.root.config(bg='black') # get size of the screen self.screen_width = self.root.winfo_screenwidth() self.screen_height = self.root.winfo_screenheight() # set window dimensions and decorations if self.options['fullscreen'] == True: self.root.attributes('-fullscreen', True) os.system('unclutter &') self.window_width = self.screen_width self.window_height = self.screen_height self.window_x = 0 self.window_y = 0 self.root.geometry("%dx%d%+d%+d" % (self.window_width, self.window_height, self.window_x, self.window_y)) self.root.attributes('-zoomed', '1') else: self.window_width = int(self.screen_width * self.nonfull_window_width) self.window_height = int(self.screen_height * self.nonfull_window_height) self.window_x = self.nonfull_window_x self.window_y = self.nonfull_window_y self.root.geometry("%dx%d%+d%+d" % (self.window_width, self.window_height, self.window_x, self.window_y)) #canvas covers the whole window self.canvas_height = self.screen_height self.canvas_width = self.screen_width # make sure focus is set. self.root.focus_set() #define response to main window closing. self.root.protocol("WM_DELETE_WINDOW", self.exit_pressed) #setup a canvas onto which will be drawn the images or text self.canvas = Canvas(self.root, bg='black') self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=0) # self.canvas.pack() self.canvas.place(x=0, y=0) self.canvas.focus_set() # **************************************** # INITIALISE THE INPUT DRIVERS # **************************************** # looks after bindings between symbolic names and internal operations controlsmanager = ControlsManager() if controlsmanager.read(pp_dir, self.pp_home, self.pp_profile) == False: self.end('error', 'cannot find or error in controls.cfg.cfg') else: controlsmanager.parse_defaults() # each driver takes a set of inputs, binds them to symboic names # and sets up a callback which returns the symbolic name when an input event occurs/ # use keyboard driver to bind keys to symbolic names and to set up callback kbd = KbdDriver() if kbd.read(pp_dir, self.pp_home, self.pp_profile) == False: self.end('error', 'cannot find or error in keys.cfg') kbd.bind_keys(self.root, self.input_pressed) self.sr = ScreenDriver() # read the screen click area config file if self.sr.read(pp_dir, self.pp_home, self.pp_profile) == False: self.end('error', 'cannot find screen.cfg') # create click areas on the canvas, must be polygon as outline rectangles are not filled as far as find_closest goes reason, message = self.sr.make_click_areas(self.canvas, self.input_pressed) if reason == 'error': self.mon.err(self, message) self.end('error', message) # **************************************** # INITIALISE THE APPLICATION AND START # **************************************** self.shutdown_required = False #kick off GPIO if enabled by command line option if self.options['gpio'] == True: from pp_gpio import PPIO # initialise the GPIO self.ppio = PPIO() # PPIO.gpio_enabled=False if self.ppio.init(pp_dir, self.pp_home, self.pp_profile, self.canvas, 50, self.gpio_pressed) == False: self.end('error', 'gpio error') # and start polling gpio self.ppio.poll() #kick off the time of day scheduler self.tod = TimeOfDay() self.tod.init(pp_dir, self.pp_home, self.canvas, 500) self.tod.poll() # Create list of start shows initialise them and then run them self.run_start_shows() #start tkinter self.root.mainloop()
def __init__(self): gc.set_debug(gc.DEBUG_UNCOLLECTABLE|gc.DEBUG_INSTANCES|gc.DEBUG_OBJECTS|gc.DEBUG_SAVEALL) self.pipresents_issue="1.3" self.pipresents_minorissue = '1.3.1g' # position and size of window without -f command line option self.nonfull_window_width = 0.45 # proportion of width self.nonfull_window_height= 0.7 # proportion of height self.nonfull_window_x = 0 # position of top left corner self.nonfull_window_y=0 # position of top left corner self.pp_background='black' StopWatch.global_enable=False # set up the handler for SIGTERM signal.signal(signal.SIGTERM,self.handle_sigterm) # **************************************** # Initialisation # *************************************** # get command line options self.options=command_options() # get Pi Presents code directory pp_dir=sys.path[0] self.pp_dir=pp_dir if not os.path.exists(pp_dir+"/pipresents.py"): if self.options['manager'] is False: tkMessageBox.showwarning("Pi Presents","Bad Application Directory:\n{0}".format(pp_dir)) exit(103) # Initialise logging and tracing Monitor.log_path=pp_dir self.mon=Monitor() # Init in PiPresents only self.mon.init() # uncomment to enable control of logging from within a class # Monitor.enable_in_code = True # enables control of log level in the code for a class - self.mon.set_log_level() # make a shorter list to log/trace only some classes without using enable_in_code. Monitor.classes = ['PiPresents', 'pp_paths', 'HyperlinkShow','RadioButtonShow','ArtLiveShow','ArtMediaShow','MediaShow','LiveShow','MenuShow', 'PathManager','ControlsManager','ShowManager','PluginManager', 'MplayerDriver','OMXDriver','UZBLDriver', 'KbdDriver','GPIODriver','TimeOfDay','ScreenDriver','Animate','OSCDriver' ] # Monitor.classes=['PiPresents','ArtMediaShow','VideoPlayer','OMXDriver'] # get global log level from command line Monitor.log_level = int(self.options['debug']) Monitor.manager = self.options['manager'] # print self.options['manager'] self.mon.newline(3) self.mon.log (self, "Pi Presents is starting, Version:"+self.pipresents_minorissue) # self.mon.log (self," OS and separator:" + os.name +' ' + os.sep) self.mon.log(self,"sys.path[0] - location of code: "+sys.path[0]) if os.geteuid() !=0: user=os.getenv('USER') else: user = os.getenv('SUDO_USER') self.mon.log(self,'User is: '+ user) # self.mon.log(self,"os.getenv('HOME') - user home directory (not used): " + os.getenv('HOME')) # does not work # self.mon.log(self,"os.path.expanduser('~') - user home directory: " + os.path.expanduser('~')) # does not work # optional other classes used self.root=None self.ppio=None self.tod=None self.animate=None self.gpiodriver=None self.oscdriver=None self.osc_enabled=False self.gpio_enabled=False self.tod_enabled=False # get home path from -o option self.pp_home = pp_paths.get_home(self.options['home']) if self.pp_home is None: self.end('error','Failed to find pp_home') # get profile path from -p option # pp_profile is the full path to the directory that contains # pp_showlist.json and other files for the profile self.pp_profile = pp_paths.get_profile_dir(self.pp_home, self.options['profile']) if self.pp_profile is None: self.end('error','Failed to find profile') # check profile exists if os.path.exists(self.pp_profile): self.mon.log(self,"Found Requested profile - pp_profile directory is: " + self.pp_profile) else: self.mon.err(self,"Failed to find requested profile: "+ self.pp_profile) self.end('error','Failed to find profile') self.mon.start_stats(self.options['profile']) # check 'verify' option if self.options['verify'] is True: val =Validator() if val.validate_profile(None,pp_dir,self.pp_home,self.pp_profile,self.pipresents_issue,False) is False: self.mon.err(self,"Validation Failed") self.end('error','Validation Failed') # initialise and read the showlist in the profile self.showlist=ShowList() self.showlist_file= self.pp_profile+ "/pp_showlist.json" if os.path.exists(self.showlist_file): self.showlist.open_json(self.showlist_file) else: self.mon.err(self,"showlist not found at "+self.showlist_file) self.end('error','showlist not found') # check profile and Pi Presents issues are compatible if float(self.showlist.sissue()) != float(self.pipresents_issue): self.mon.err(self,"Version of profile " + self.showlist.sissue() + " is not same as Pi Presents, must exit") self.end('error','wrong version of profile') # get the 'start' show from the showlist index = self.showlist.index_of_show('start') if index >=0: self.showlist.select(index) self.starter_show=self.showlist.selected_show() else: self.mon.err(self,"Show [start] not found in showlist") self.end('error','start show not found') if self.starter_show['start-show']=='': self.mon.warn(self,"No Start Shows in Start Show") # ******************** # SET UP THE GUI # ******************** # turn off the screenblanking and saver if self.options['noblank'] is True: call(["xset","s", "off"]) call(["xset","s", "-dpms"]) self.root=Tk() self.title='Pi Presents - '+ self.pp_profile self.icon_text= 'Pi Presents' self.root.title(self.title) self.root.iconname(self.icon_text) self.root.config(bg=self.pp_background) self.mon.log(self, 'native screen dimensions are ' + str(self.root.winfo_screenwidth()) + ' x ' + str(self.root.winfo_screenheight()) + ' pixcels') if self.options['screensize'] =='': self.screen_width = self.root.winfo_screenwidth() self.screen_height = self.root.winfo_screenheight() else: reason,message,self.screen_width,self.screen_height=self.parse_screen(self.options['screensize']) if reason =='error': self.mon.err(self,message) self.end('error',message) self.mon.log(self, 'commanded screen dimensions are ' + str(self.screen_width) + ' x ' + str(self.screen_height) + ' pixcels') # set window dimensions and decorations if self.options['fullscreen'] is False: self.window_width=int(self.root.winfo_screenwidth()*self.nonfull_window_width) self.window_height=int(self.root.winfo_screenheight()*self.nonfull_window_height) self.window_x=self.nonfull_window_x self.window_y=self.nonfull_window_y self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y)) else: self.window_width=self.screen_width self.window_height=self.screen_height self.root.attributes('-fullscreen', True) os.system('unclutter 1>&- 2>&- &') # Suppress 'someone created a subwindow' complaints from unclutter self.window_x=0 self.window_y=0 self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y)) self.root.attributes('-zoomed','1') # canvs cover the whole screen whatever the size of the window. self.canvas_height=self.screen_height self.canvas_width=self.screen_width # make sure focus is set. self.root.focus_set() # define response to main window closing. self.root.protocol ("WM_DELETE_WINDOW", self.handle_user_abort) # setup a canvas onto which will be drawn the images or text self.canvas = Canvas(self.root, bg=self.pp_background) if self.options['fullscreen'] is True: self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=0) else: self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=1, highlightcolor='yellow') self.canvas.place(x=0,y=0) # self.canvas.config(bg='black') self.canvas.focus_set() # **************************************** # INITIALISE THE INPUT DRIVERS # **************************************** # each driver takes a set of inputs, binds them to symboic names # and sets up a callback which returns the symbolic name when an input event occurs/ # use keyboard driver to bind keys to symbolic names and to set up callback kbd=KbdDriver() if kbd.read(pp_dir,self.pp_home,self.pp_profile) is False: self.end('error','cannot find or error in keys.cfg') kbd.bind_keys(self.root,self.handle_input_event) self.sr=ScreenDriver() # read the screen click area config file reason,message = self.sr.read(pp_dir,self.pp_home,self.pp_profile) if reason == 'error': self.end('error','cannot find screen.cfg') # create click areas on the canvas, must be polygon as outline rectangles are not filled as far as find_closest goes # click areas are made on the Pi Presents canvas not the show canvases. reason,message = self.sr.make_click_areas(self.canvas,self.handle_input_event) if reason == 'error': self.mon.err(self,message) self.end('error',message) # **************************************** # INITIALISE THE APPLICATION AND START # **************************************** self.shutdown_required=False self.exitpipresents_required=False # kick off GPIO if enabled by command line option self.gpio_enabled=False if os.path.exists(self.pp_profile + os.sep + 'pp_io_config'+os.sep+ 'gpio.cfg'): # initialise the GPIO self.gpiodriver=GPIODriver() reason,message=self.gpiodriver.init(pp_dir,self.pp_home,self.pp_profile,self.canvas,50,self.handle_input_event) if reason == 'error': self.end('error',message) else: self.gpio_enabled=True # and start polling gpio self.gpiodriver.poll() # kick off animation sequencer self.animate = Animate() self.animate.init(pp_dir,self.pp_home,self.pp_profile,self.canvas,200,self.handle_output_event) self.animate.poll() #create a showmanager ready for time of day scheduler and osc server show_id=-1 self.show_manager=ShowManager(show_id,self.showlist,self.starter_show,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home) # first time through set callback to terminate Pi Presents if all shows have ended. self.show_manager.init(self.canvas,self.all_shows_ended_callback,self.handle_command,self.showlist) # Register all the shows in the showlist reason,message=self.show_manager.register_shows() if reason == 'error': self.mon.err(self,message) self.end('error',message) # Init OSCDriver, read config and start OSC server self.osc_enabled=False if os.path.exists(self.pp_profile + os.sep + 'pp_io_config'+ os.sep + 'osc.cfg'): self.oscdriver=OSCDriver() reason,message=self.oscdriver.init(self.pp_profile,self.handle_command,self.handle_input_event,self.e_osc_handle_output_event) if reason == 'error': self.end('error',message) else: self.osc_enabled=True self.root.after(1000,self.oscdriver.start_server()) # and run the start shows self.run_start_shows() # set up the time of day scheduler including catchup self.tod_enabled=False if os.path.exists(self.pp_profile + os.sep + 'schedule.json'): # kick off the time of day scheduler which may run additional shows self.tod=TimeOfDay() self.tod.init(pp_dir,self.pp_home,self.pp_profile,self.root,self.handle_command) self.tod_enabled = True # then start the time of day scheduler if self.tod_enabled is True: self.tod.poll() # start Tkinters event loop self.root.mainloop( )
def __init__(self): gc.set_debug(gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_INSTANCES | gc.DEBUG_OBJECTS | gc.DEBUG_SAVEALL) self.pipresents_issue = "1.3" self.pipresents_minorissue = '1.3.1g' # position and size of window without -f command line option self.nonfull_window_width = 0.45 # proportion of width self.nonfull_window_height = 0.7 # proportion of height self.nonfull_window_x = 0 # position of top left corner self.nonfull_window_y = 0 # position of top left corner self.pp_background = 'black' StopWatch.global_enable = False # set up the handler for SIGTERM signal.signal(signal.SIGTERM, self.handle_sigterm) # **************************************** # Initialisation # *************************************** # get command line options self.options = command_options() # get Pi Presents code directory pp_dir = sys.path[0] self.pp_dir = pp_dir if not os.path.exists(pp_dir + "/pipresents.py"): if self.options['manager'] is False: tkMessageBox.showwarning( "Pi Presents", "Bad Application Directory:\n{0}".format(pp_dir)) exit(103) # Initialise logging and tracing Monitor.log_path = pp_dir self.mon = Monitor() # Init in PiPresents only self.mon.init() # uncomment to enable control of logging from within a class # Monitor.enable_in_code = True # enables control of log level in the code for a class - self.mon.set_log_level() # make a shorter list to log/trace only some classes without using enable_in_code. Monitor.classes = [ 'PiPresents', 'pp_paths', 'HyperlinkShow', 'RadioButtonShow', 'ArtLiveShow', 'ArtMediaShow', 'MediaShow', 'LiveShow', 'MenuShow', 'PathManager', 'ControlsManager', 'ShowManager', 'PluginManager', 'MplayerDriver', 'OMXDriver', 'UZBLDriver', 'KbdDriver', 'GPIODriver', 'TimeOfDay', 'ScreenDriver', 'Animate', 'OSCDriver' ] # Monitor.classes=['PiPresents','ArtMediaShow','VideoPlayer','OMXDriver'] # get global log level from command line Monitor.log_level = int(self.options['debug']) Monitor.manager = self.options['manager'] # print self.options['manager'] self.mon.newline(3) self.mon.log( self, "Pi Presents is starting, Version:" + self.pipresents_minorissue) # self.mon.log (self," OS and separator:" + os.name +' ' + os.sep) self.mon.log(self, "sys.path[0] - location of code: " + sys.path[0]) if os.geteuid() != 0: user = os.getenv('USER') else: user = os.getenv('SUDO_USER') self.mon.log(self, 'User is: ' + user) # self.mon.log(self,"os.getenv('HOME') - user home directory (not used): " + os.getenv('HOME')) # does not work # self.mon.log(self,"os.path.expanduser('~') - user home directory: " + os.path.expanduser('~')) # does not work # optional other classes used self.root = None self.ppio = None self.tod = None self.animate = None self.gpiodriver = None self.oscdriver = None self.osc_enabled = False self.gpio_enabled = False self.tod_enabled = False # get home path from -o option self.pp_home = pp_paths.get_home(self.options['home']) if self.pp_home is None: self.end('error', 'Failed to find pp_home') # get profile path from -p option # pp_profile is the full path to the directory that contains # pp_showlist.json and other files for the profile self.pp_profile = pp_paths.get_profile_dir(self.pp_home, self.options['profile']) if self.pp_profile is None: self.end('error', 'Failed to find profile') # check profile exists if os.path.exists(self.pp_profile): self.mon.log( self, "Found Requested profile - pp_profile directory is: " + self.pp_profile) else: self.mon.err( self, "Failed to find requested profile: " + self.pp_profile) self.end('error', 'Failed to find profile') self.mon.start_stats(self.options['profile']) # check 'verify' option if self.options['verify'] is True: val = Validator() if val.validate_profile(None, pp_dir, self.pp_home, self.pp_profile, self.pipresents_issue, False) is False: self.mon.err(self, "Validation Failed") self.end('error', 'Validation Failed') # initialise and read the showlist in the profile self.showlist = ShowList() self.showlist_file = self.pp_profile + "/pp_showlist.json" if os.path.exists(self.showlist_file): self.showlist.open_json(self.showlist_file) else: self.mon.err(self, "showlist not found at " + self.showlist_file) self.end('error', 'showlist not found') # check profile and Pi Presents issues are compatible if float(self.showlist.sissue()) != float(self.pipresents_issue): self.mon.err( self, "Version of profile " + self.showlist.sissue() + " is not same as Pi Presents, must exit") self.end('error', 'wrong version of profile') # get the 'start' show from the showlist index = self.showlist.index_of_show('start') if index >= 0: self.showlist.select(index) self.starter_show = self.showlist.selected_show() else: self.mon.err(self, "Show [start] not found in showlist") self.end('error', 'start show not found') if self.starter_show['start-show'] == '': self.mon.warn(self, "No Start Shows in Start Show") # ******************** # SET UP THE GUI # ******************** # turn off the screenblanking and saver if self.options['noblank'] is True: call(["xset", "s", "off"]) call(["xset", "s", "-dpms"]) self.root = Tk() self.title = 'Pi Presents - ' + self.pp_profile self.icon_text = 'Pi Presents' self.root.title(self.title) self.root.iconname(self.icon_text) self.root.config(bg=self.pp_background) self.mon.log( self, 'native screen dimensions are ' + str(self.root.winfo_screenwidth()) + ' x ' + str(self.root.winfo_screenheight()) + ' pixcels') if self.options['screensize'] == '': self.screen_width = self.root.winfo_screenwidth() self.screen_height = self.root.winfo_screenheight() else: reason, message, self.screen_width, self.screen_height = self.parse_screen( self.options['screensize']) if reason == 'error': self.mon.err(self, message) self.end('error', message) self.mon.log( self, 'commanded screen dimensions are ' + str(self.screen_width) + ' x ' + str(self.screen_height) + ' pixcels') # set window dimensions and decorations if self.options['fullscreen'] is False: self.window_width = int(self.root.winfo_screenwidth() * self.nonfull_window_width) self.window_height = int(self.root.winfo_screenheight() * self.nonfull_window_height) self.window_x = self.nonfull_window_x self.window_y = self.nonfull_window_y self.root.geometry("%dx%d%+d%+d" % (self.window_width, self.window_height, self.window_x, self.window_y)) else: self.window_width = self.screen_width self.window_height = self.screen_height self.root.attributes('-fullscreen', True) os.system( 'unclutter 1>&- 2>&- &' ) # Suppress 'someone created a subwindow' complaints from unclutter self.window_x = 0 self.window_y = 0 self.root.geometry("%dx%d%+d%+d" % (self.window_width, self.window_height, self.window_x, self.window_y)) self.root.attributes('-zoomed', '1') # canvs cover the whole screen whatever the size of the window. self.canvas_height = self.screen_height self.canvas_width = self.screen_width # make sure focus is set. self.root.focus_set() # define response to main window closing. self.root.protocol("WM_DELETE_WINDOW", self.handle_user_abort) # setup a canvas onto which will be drawn the images or text self.canvas = Canvas(self.root, bg=self.pp_background) if self.options['fullscreen'] is True: self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=0) else: self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=1, highlightcolor='yellow') self.canvas.place(x=0, y=0) # self.canvas.config(bg='black') self.canvas.focus_set() # **************************************** # INITIALISE THE INPUT DRIVERS # **************************************** # each driver takes a set of inputs, binds them to symboic names # and sets up a callback which returns the symbolic name when an input event occurs/ # use keyboard driver to bind keys to symbolic names and to set up callback kbd = KbdDriver() if kbd.read(pp_dir, self.pp_home, self.pp_profile) is False: self.end('error', 'cannot find or error in keys.cfg') kbd.bind_keys(self.root, self.handle_input_event) self.sr = ScreenDriver() # read the screen click area config file reason, message = self.sr.read(pp_dir, self.pp_home, self.pp_profile) if reason == 'error': self.end('error', 'cannot find screen.cfg') # create click areas on the canvas, must be polygon as outline rectangles are not filled as far as find_closest goes # click areas are made on the Pi Presents canvas not the show canvases. reason, message = self.sr.make_click_areas(self.canvas, self.handle_input_event) if reason == 'error': self.mon.err(self, message) self.end('error', message) # **************************************** # INITIALISE THE APPLICATION AND START # **************************************** self.shutdown_required = False self.exitpipresents_required = False # kick off GPIO if enabled by command line option self.gpio_enabled = False if os.path.exists(self.pp_profile + os.sep + 'pp_io_config' + os.sep + 'gpio.cfg'): # initialise the GPIO self.gpiodriver = GPIODriver() reason, message = self.gpiodriver.init(pp_dir, self.pp_home, self.pp_profile, self.canvas, 50, self.handle_input_event) if reason == 'error': self.end('error', message) else: self.gpio_enabled = True # and start polling gpio self.gpiodriver.poll() # kick off animation sequencer self.animate = Animate() self.animate.init(pp_dir, self.pp_home, self.pp_profile, self.canvas, 200, self.handle_output_event) self.animate.poll() #create a showmanager ready for time of day scheduler and osc server show_id = -1 self.show_manager = ShowManager(show_id, self.showlist, self.starter_show, self.root, self.canvas, self.pp_dir, self.pp_profile, self.pp_home) # first time through set callback to terminate Pi Presents if all shows have ended. self.show_manager.init(self.canvas, self.all_shows_ended_callback, self.handle_command, self.showlist) # Register all the shows in the showlist reason, message = self.show_manager.register_shows() if reason == 'error': self.mon.err(self, message) self.end('error', message) # Init OSCDriver, read config and start OSC server self.osc_enabled = False if os.path.exists(self.pp_profile + os.sep + 'pp_io_config' + os.sep + 'osc.cfg'): self.oscdriver = OSCDriver() reason, message = self.oscdriver.init( self.pp_profile, self.handle_command, self.handle_input_event, self.e_osc_handle_output_event) if reason == 'error': self.end('error', message) else: self.osc_enabled = True self.root.after(1000, self.oscdriver.start_server()) # and run the start shows self.run_start_shows() # set up the time of day scheduler including catchup self.tod_enabled = False if os.path.exists(self.pp_profile + os.sep + 'schedule.json'): # kick off the time of day scheduler which may run additional shows self.tod = TimeOfDay() self.tod.init(pp_dir, self.pp_home, self.pp_profile, self.root, self.handle_command) self.tod_enabled = True # then start the time of day scheduler if self.tod_enabled is True: self.tod.poll() # start Tkinters event loop self.root.mainloop()