Check-in [b973f0e385]
Overview
Comment: | Slim down initialization (wrapper script for /usr/bin and pyzip will be used). Move module coupling into ST2 window constructor. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
b973f0e3851cf87583589c00d650e5ed |
User & Date: | mario on 2015-04-01 11:16:39 |
Other Links: | manifest | tags |
Context
2015-04-01
| ||
11:17 | Tried SVG for logo, but Gtk refuses to play along; given up. Set progressbar to no-show-all. check-in: ef90440dbf user: mario tags: trunk | |
11:16 | Slim down initialization (wrapper script for /usr/bin and pyzip will be used). Move module coupling into ST2 window constructor. check-in: b973f0e385 user: mario tags: trunk | |
11:15 | Remove gtk/visibility setting in favour of show_all(). Fix pixbuf creation, b64decode ignoring non-base64 data. check-in: 1786e24701 user: mario tags: trunk | |
Changes
Modified st2.py from [df1a1ff19b] to [15304419e8].
︙ | ︙ | |||
12 13 14 15 16 17 18 | # url: http://freshcode.club/projects/streamtuner2 # config: # { 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 | | < < | > | | > > < | | < > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | # url: http://freshcode.club/projects/streamtuner2 # config: # { 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, 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 # # Streamtuner2 is a GUI for browsing internet radio directories, music # collections, and video services - grouped by genres or categories. # It runs your preferred audio player, and streamripper for recording. # # It's an independent rewrite of streamtuner1. Being written in Python, # 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 import re from copy import copy import inspect import traceback from threading import Thread # 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 import ahttp import action import logo import favicon import channels import channels.bookmarks import channels.configwin import channels.streamedit import channels.search # 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 features = {} # non-channel plugins working = [] # threads add_signals = {} # channel gtk-handler signals hooks = { "play": [favicon.download_playing], # observers queue here "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{}) # constructor |
︙ | ︙ | |||
108 109 110 111 112 113 114 115 116 117 118 119 120 121 | # dialogs that are connected to main self.features = { "search": channels.search.search(self), "configwin": channels.configwin.configwin(self), "streamedit": channels.streamedit.streamedit(self), } gui_startup(4/20.0) # append other channel modules and plugins self.load_plugin_channels() # load application state (widget sizes, selections, etc.) try: winlayout = conf.load("window") | > > > > > > | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | # dialogs that are connected to main self.features = { "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.) try: winlayout = conf.load("window") |
︙ | ︙ | |||
180 181 182 183 184 185 186 | # else "update_categories": self.update_categories, "update_favicons": self.update_favicons, "app_state": self.app_state, "bookmark": self.bookmark, "save_as": self.save_as, "menu_about": lambda w: AboutStreamtuner2(self), | | | | | | | 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | # else "update_categories": self.update_categories, "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": 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, "search_open": self.search.menu_search, "search_go": self.search.cache_search, "search_srv": self.search.server_search, "search_cancel": self.search.cancel, "true": lambda w,*args: True, # win_streamedit "streamedit_open": self.streamedit.open, "streamedit_save": self.streamedit.save, "streamedit_new": self.streamedit.new, "streamedit_cancel": self.streamedit.cancel, }, **self.add_signals)) # actually display main window 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() # Also looks in self.channels[] for the named channel plugins def __getattr__(self, name): |
︙ | ︙ | |||
282 283 284 285 286 287 288 | if row: self.channel().play(row) [callback(row) for callback in self.hooks["play"]] # Recording: invoke streamripper for current stream URL def on_record_clicked(self, widget): row = self.row() | | | | | 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | if row: self.channel().play(row) [callback(row) for callback in self.hooks["play"]] # Recording: invoke streamripper for current stream URL def on_record_clicked(self, widget): row = self.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") 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") 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 self.thread( lambda: ( self.channel().load(category,reload), reload and self.bookmarks.heuristic_update(self.current_channel,category) ) |
︙ | ︙ | |||
350 351 352 353 354 355 356 | # Save stream to file (.m3u) 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: | | | 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | # Save stream to file (.m3u) 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: 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")) # Remove a stream entry |
︙ | ︙ | |||
386 387 388 389 390 391 392 | sbar_msg.pop() uikit.do(lambda:self.statusbar.pop(sbar_cid)) # progressbar if (type(text)==float): if (text >= 999.0/1000): # completed uikit.do(lambda:self.progress.hide()) else: # show percentage | | | 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 | sbar_msg.pop() uikit.do(lambda:self.statusbar.pop(sbar_cid)) # 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_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) uikit.do(lambda:self.statusbar.push(sbar_cid, text)) pass |
︙ | ︙ | |||
470 471 472 473 474 475 476 | # Right clicking a stream/station in the treeview to make context menu pop out. 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) | | | | < < < < < < > > > | 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 | # Right clicking a stream/station in the treeview to make context menu pop out. 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) self.streamactions.popup( parent_menu_shell=None, parent_menu_item=None, func=None, button=event.button, activate_time=event.time, data=None ) return None # else pass on to normal left-button signal handler else: return False # startup procedure def main(): # graphical if len(sys.argv) < 2 or "--gtk3" in sys.argv: # prepare for threading in Gtk+ callbacks gobject.threads_init() # prepare main window main = StreamTunerTwo() # first invocation if (conf.get("firstrun")): main.configwin.open(None) del conf.firstrun # run gtk.main() __print__(dbg.PROC, r"[31m gtk_main_quit [0m") # invoke command-line interface else: import cli cli.StreamTunerCLI() # run if __name__ == "__main__": main() |