Index: st2.py ================================================================== --- st2.py +++ st2.py @@ -14,11 +14,11 @@ # { type: env, name: http_proxy, description: proxy for HTTP access } # { type: env, name: XDG_CONFIG_HOME, description: relocates user .config subdirectory } # category: sound # depends: pygtk | gi, threading, requests, pyquery, lxml # id: streamtuner2 -# pack: *.py, gtk*.xml, st2.py=/usr/bin/streamtuner2, channels/__init__.py, bundle/*.py, +# pack: *.py, gtk*.xml, bin=/usr/bin/streamtuner2, channels/__init__.py, bundle/*.py, # streamtuner2.desktop=/usr/share/applications/, README=/usr/share/doc/streamtuner2/, # NEWS.gz=/usr/share/doc/streamtuner2/changelog.gz, help/streamtuner2.1=/usr/share/man/man1/, # help/*page=/usr/share/doc/streamtuner2/help/, help/img/*=/usr/share/doc/streamtuner2/help/img/, # logo.png=/usr/share/pixmaps/streamtuner2.png, # architecture: all @@ -31,12 +31,10 @@ # can be more easily extended and fixed. The mix of JSON APIs, regex # or PyQuery extraction makes list generation simpler and more robust. # # Primarily radio stations are displayed, some channels however are music # collections. Commercial and sign-up services are not an objective. -# - # standard modules import sys import os @@ -44,19 +42,21 @@ from copy import copy import inspect import traceback from threading import Thread -# add library path -sys.path.insert(0, "/usr/share/streamtuner2") # pre-defined directory for modules -sys.path.insert(0, ".") # development module path +# add library path (either global setup, or pyzip basename) +if not os.path.dirname(__file__) in sys.path: + sys.path.insert(0, os.path.dirname(__file__)) + +# initializes itself, so all conf.vars are available right away +from config import * # gtk modules from uikit import pygtk, gtk, gobject, uikit, ui_xml, gui_startup, AboutStreamtuner2 # custom modules -from config import * # initializes itself, so all conf.vars are available right away import ahttp import action import logo import favicon import channels @@ -65,13 +65,12 @@ import channels.streamedit import channels.search -# this represents the main window -# and also contains most application behaviour -main = None +# This represents the main window, dispatches Gtk events, +# and shares most application behaviour with the channel modules. class StreamTunerTwo(gtk.Builder): # object containers widgets = {} # non-glade widgets (the manually instantiated ones) channels = {} # channel modules @@ -83,10 +82,11 @@ "init": [], "config_load": [], "config_save": [], } meta = plugin_meta() + # status variables channel_names = ["bookmarks"] # order of channel notebook tabs current_channel = "bookmarks" # currently selected channel name (as index in self.channels{}) @@ -110,10 +110,16 @@ "search": channels.search.search(self), "configwin": channels.configwin.configwin(self), "streamedit": channels.streamedit.streamedit(self), } gui_startup(4/20.0) + + # early module coupling + action.main = self # action (play/record) module needs a reference to main window for gtk interaction and some URL/URI callbacks + self.action = action.action # shorter name (could also become a features. entry...) + ahttp.feedback = self.status # http module gives status feedbacks too + # append other channel modules and plugins self.load_plugin_channels() # load application state (widget sizes, selections, etc.) @@ -182,14 +188,14 @@ "update_favicons": self.update_favicons, "app_state": self.app_state, "bookmark": self.bookmark, "save_as": self.save_as, "menu_about": lambda w: AboutStreamtuner2(self), - "menu_help": action.action.help, - "menu_onlineforum": lambda w: action.browser("http://sourceforge.net/projects/streamtuner2/forums/forum/1173108"), - "menu_fossilwiki": lambda w: action.browser("http://fossil.include-once.org/streamtuner2/"), - "menu_projhomepage": lambda w: action.browser("http://milki.include-once.org/streamtuner2/"), + "menu_help": self.action.help, + "menu_onlineforum": lambda w: self.action.browser("http://sourceforge.net/projects/streamtuner2/forums/forum/1173108"), + "menu_fossilwiki": lambda w: self.action.browser("http://fossil.include-once.org/streamtuner2/"), + "menu_projhomepage": lambda w: self.action.browser("http://milki.include-once.org/streamtuner2/"), # "menu_bugreport": lambda w: BugReport(), "menu_copy": self.menu_copy, "delete_entry": self.delete_entry, # search dialog "quicksearch_set": self.search.quicksearch_set, @@ -204,11 +210,11 @@ "streamedit_new": self.streamedit.new, "streamedit_cancel": self.streamedit.cancel, }, **self.add_signals)) # actually display main window - self.win_streamtuner2.show() + self.win_streamtuner2.show_all() gui_startup(1.0) #-- Shortcut for glade.get_widget() # Allows access to widgets as direct attributes instead of using .get_widget() @@ -284,22 +290,22 @@ [callback(row) for callback in self.hooks["play"]] # Recording: invoke streamripper for current stream URL def on_record_clicked(self, widget): row = self.row() - action.record(row.get("url"), row.get("format", "audio/mpeg"), "url/direct", row=row) + self.action.record(row.get("url"), row.get("format", "audio/mpeg"), "url/direct", row=row) # Open stream homepage in web browser def on_homepage_stream_clicked(self, widget): url = self.selected("homepage") - action.browser(url) + self.action.browser(url) # Browse to channel homepage (double click on notebook tab) def on_homepage_channel_clicked(self, widget, event=2): if event == 2 or event.type == gtk.gdk._2BUTTON_PRESS: __print__(dbg.UI, "dblclick") - action.browser(self.channel().homepage) + self.action.browser(self.channel().homepage) # Reload stream list in current channel-category def on_reload_clicked(self, widget=None, reload=1): __print__(dbg.UI, "reload", reload, self.current_channel, self.channels[self.current_channel], self.channel().current) category = self.channel().current @@ -352,11 +358,11 @@ def save_as(self, widget): row = self.row() default_fn = row["title"] + ".m3u" fn = uikit.save_file("Save Stream", None, default_fn, [(".m3u","*m3u"),(".pls","*pls"),(".xspf","*xspf"),(".smil","*smil"),(".asx","*asx"),("all files","*")]) if fn: - action.save(row, fn) + self.action.save(row, fn) pass # Save current stream URL into clipboard def menu_copy(self, w): gtk.clipboard_get().set_text(self.selected("url")) @@ -388,11 +394,11 @@ # progressbar if (type(text)==float): if (text >= 999.0/1000): # completed uikit.do(lambda:self.progress.hide()) else: # show percentage - uikit.do(lambda:self.progress.show() or self.progress.set_fraction(text)) + uikit.do(lambda:self.progress.show_all() or self.progress.set_fraction(text)) if (text <= 0): # unknown state uikit.do(lambda:self.progress.pulse()) # add text elif (type(text)==str): sbar_msg.append(1) @@ -472,11 +478,11 @@ def station_context_menu(self, treeview, event): if event.button >= 3: path = treeview.get_path_at_pos(int(event.x), int(event.y))[0] treeview.grab_focus() treeview.set_cursor(path, None, False) - main.streamactions.popup( + self.streamactions.popup( parent_menu_shell=None, parent_menu_item=None, func=None, button=event.button, activate_time=event.time, data=None ) return None @@ -486,13 +492,12 @@ - -#-- run main -if __name__ == "__main__": +# startup procedure +def main(): # graphical if len(sys.argv) < 2 or "--gtk3" in sys.argv: # prepare for threading in Gtk+ callbacks @@ -499,15 +504,10 @@ gobject.threads_init() # prepare main window main = StreamTunerTwo() - # module coupling - action.main = main # action (play/record) module needs a reference to main window for gtk interaction and some URL/URI callbacks - action = action.action # shorter name - ahttp.feedback = main.status # http module gives status feedbacks too - # first invocation if (conf.get("firstrun")): main.configwin.open(None) del conf.firstrun @@ -518,6 +518,9 @@ # invoke command-line interface else: import cli cli.StreamTunerCLI() +# run +if __name__ == "__main__": + main()