def load_options(self): if '--defaults' in sys.argv: self.options = AttrDict() return try: with io.open(os.path.expanduser(u'~/.config/variety/variety_slideshow.json'), encoding='utf8') as f: self.options = AttrDict(json.loads(f.read())) except: self.options = AttrDict()
async def previous_track(query: types.CallbackQuery): await query_answer(query) token = await spotify_api.get_token(query.from_user.id) if not token: return SendMessage(query.from_user.id, 'Please authorize', reply_markup=auth_keyboard(query.from_user.id)) await request_post('https://api.spotify.com/v1/me/player/previous', headers={'Authorization': f'Bearer {token}'}) await asyncio.sleep(.7) req = await request_get( 'https://api.spotify.com/v1/me/player/currently-playing', headers={'Authorization': f'Bearer {token}'}) try: json = await req.json() track = AttrDict(json['item']) except Exception as e: print_traceback(e) return SendMessage(query.message.chat.id, f'Play something in Spotify and try again', reply_to_message_id=query.message.message_id) await bot.edit_message_text( text='Currently playing track:\n' + f'{track.artists[0].name} - {track.name}' f'<a href="{track.album.images[0].url}">​</a>', chat_id=query.message.chat.id, message_id=query.message.message_id, reply_markup=current_track_keyboard(track), parse_mode='HTML')
async def search(query, obj_type='track', limit=5): if not var.spotify_token or time() > var.spotify_token_expires: await authorize() data = {'type': obj_type, 'limit': limit, 'q': query} headers = {'Authorization': f'Bearer {var.spotify_token}'} r = await request_get(encode_url( 'https://api.spotify.com/v1/search', data=data), headers=headers) json = await r.json() return [AttrDict(track) for track in json['tracks']['items']]
async def request(method, id, **params): r = request_get(f'https://api.genius.com/{method}/{id}', params={ **params, 'access_token': genius_token }) j = await r.json() response_code = j['meta']['status'] if response_code != 200: raise ValueError(f'request error code: {response_code}') return AttrDict(j)
async def api_request(request_method, method, need_sign=True, **args): params = {'method': method, **args, 'api_key': lastfm_api} if need_sign: params['api_sig'] = sign(params) params['format'] = 'json' if request_method == 'POST': req = await request_post(api_url, params=params) elif request_method == 'GET': req = await request_get(api_url, params=params) resp = await req.json() return AttrDict(resp)
async def get_track(track_id): if not var.spotify_token or time() > var.spotify_token_expires: await authorize() r = await request_get( f'https://api.spotify.com/v1/tracks/{track_id}', headers={'Authorization': f'Bearer {var.spotify_token}'}) json = await r.json() if not json.get('error'): return AttrDict(json) else: raise ValueError('Cannot get track')
async def get_playlist(playlist_id): if not var.spotify_token or time() > var.spotify_token_expires: await authorize() r = await request_get( f'https://api.spotify.com/v1/playlists/{playlist_id}/tracks', headers={'Authorization': f'Bearer {var.spotify_token}'}) print(r.url) json = await r.json() if not json.get('error'): return [AttrDict(track['track']) for track in json['items']] else: raise ValueError('Error getting playlist: ' + json.get('error'))
async def get_artist(artist_id): if not var.spotify_token or time() > var.spotify_token_expires: await authorize() r = await request_get( f'https://api.spotify.com/v1/artists/{artist_id}', headers={'Authorization': f'Bearer {var.spotify_token}'}) print(r.url) json = await r.json() if not json.get('error'): return AttrDict(json) else: raise ValueError('Error getting artist: ' + json.get('error'))
async def api_call(obj_name, obj_id, method='', errcount=0, **params): r = await request_get(f'{api_url}/{obj_name}/{obj_id}/{method}', params=params) obj = AttrDict(await r.json()) if obj.error: if errcount > 2: raise ValueError(f'error getting object {obj}_{obj_id}' f'\n{r.url}') url = f'https://deezer.com/{obj_name}/{obj_id}/' r = await request_get(url) new_id = str(r.url).split('/')[-1] return await api_call(obj_name, new_id, method, errcount + 1, **params) if obj.data: obj = obj.data if not len(obj): return [] if isinstance(obj, list): return obj return AttrDict(obj)
async def search(obj='track', q=''): encoded_url = utils.encode_url(f'{api_url}/search/{obj}/', {'q': q, 'limit': 50}) results = await (await var.session.get(encoded_url)).json() try: if obj == 'artist': result = [Artist(result) for result in results['data']] elif obj == 'album': result = [Album(result) for result in results['data']] else: result = [AttrDict(result) for result in results['data']] return result or [] except KeyError: print(obj)
async def search(self, query, obj_type='track', limit=5): if self.expires_in < time(): self.restart() data = {'type': obj_type, 'limit': limit, 'q': query} headers = {'Authorization': f'Bearer {self.token}'} r = await var.session.get(encode_url( 'https://api.spotify.com/v1/search', data=data), headers=headers) json = await r.json(content_type=None) result = [] if json['tracks']['total'] != 0: for item in json['tracks']['items']: result.append(AttrDict(item)) return result
async def get_playlist(self, url): if self.expires_in < time(): self.restart() url = re.findall(spotify_playlist, url)[0] playlist_id = url.split('/')[-1] r = await request_get( f'https://api.spotify.com/v1/playlists/{playlist_id}/tracks', headers={'Authorization': f'Bearer {self.token}'}) print(r.url) json = await r.json(content_type=None) if not json.get('error'): return [AttrDict(track['track']) for track in json['items']] else: raise ValueError('Error getting playlist: ' + json.get('error'))
async def get_album(self, url): if self.expires_in < time(): self.restart() url = re.findall(spotify_album, url)[0] album_id = url.split('/')[-1] r = await request_get( f'https://api.spotify.com/v1/albums/{album_id}', headers={'Authorization': f'Bearer {self.token}'}) print(r.url) json = await r.json(content_type=None) if not json.get('error'): return AttrDict(json) else: raise ValueError('Error getting albums: ' + json.get('error'))
async def now_playing(message: types.Message): if message.from_user.id not in admins \ and message.from_user.id not in donated_users: trial_mode = True times = int(await trial_mode_times(message.from_user.id, 'spotify')) if times > 10: return SendMessage( message.chat.id, 'This feature works only for donated users\n' 'please /donate and help developer') else: trial_mode = False token = await get_token(message.from_user.id) if not token: return SendMessage(message.chat.id, 'Please authorize', reply_markup=auth_keyboard(message.from_user.id)) req = await request_get( 'https://api.spotify.com/v1/me/player/currently-playing', headers={'Authorization': f'Bearer {token}'}) try: json = await req.json() track = AttrDict(json['item']) except Exception as e: print_traceback(e) return SendMessage(message.chat.id, f'Play something in Spotify and try again', reply_to_message_id=message.message_id) if trial_mode: await bot.send_message( message.chat.id, f'You can use this feature {10-times} more times' 'to enable this and many other features ' 'permanently, please /donate') return SendMessage(message.chat.id, 'Currently playing track:\n' + f'{track.artists[0].name} - {track.name}' f'<a href="{track.album.images[0].url}">​</a>', reply_markup=current_track_keyboard(track), parse_mode='HTML', reply_to_message_id=message.message_id)
async def get_track(self, url, retries=0): if self.expires_in < time(): self.restart() if retries > 3: raise ValueError('Cannot get track') url = re.findall(spotify_track, url)[0] url = 'https://' + url track_id = url.split('/')[-1] r = await request_get( f'https://api.spotify.com/v1/tracks/{track_id}', headers={'Authorization': f'Bearer {self.token}'}) print(r.url) json = await r.json(content_type=None) try: json['error'] except KeyError: return AttrDict(json) else: self.restart() return await self.get_track(url, retries + 1)
from __future__ import unicode_literals import copy import six import os.path as osp from ast import literal_eval import numpy as np import yaml import torch import torch.nn as nn from torch.nn import init from AttrDict import AttrDict __C = AttrDict() # Consumers can get config by: # from fast_rcnn_config import cfg cfg = __C __C.EPOCH = 0 __C.CLASS_UNIFORM_PCT = 0.0 __C.BATCH_WEIGHTING = False __C.BORDER_WINDOW = 1 __C.REDUCE_BORDER_EPOCH = -1 __C.STRICTBORDERCLASS = None __C.DATASET = AttrDict() __C.DATASET.CITYSCAPES_DIR = '/home/username/data/cityscapes' __C.DATASET.CV_SPLITS = 3 __C.MODEL = AttrDict()
class VarietySlideshow(Gtk.Window): def __init__(self): super(Gtk.Window, self).__init__() def current_monitors_help(self): result = 'Your current monitors are: ' screen = Gdk.Screen.get_default() for i in range(0, screen.get_n_monitors()): geo = screen.get_monitor_geometry(i) result += (', ' if i > 0 else '') + '%d - %s, %dx%d' % ( i + 1, screen.get_monitor_plug_name(i), geo.width, geo.height) return result def load_options(self): if '--defaults' in sys.argv: self.options = AttrDict() return try: with io.open(os.path.expanduser( u'~/.config/variety/variety_slideshow.json'), encoding='utf8') as f: self.options = AttrDict(json.loads(f.read())) except: self.options = AttrDict() def save_options(self): try: try: os.makedirs(os.path.expanduser(u'~/.config/variety/')) except: pass with io.open(os.path.expanduser( u'~/.config/variety/variety_slideshow.json'), 'w', encoding='utf8') as f: f.write( json.dumps(self.options, indent=4, ensure_ascii=False, encoding='utf8')) except: logging.exception(u'Could not save options:') def parse_options(self): """Support for command line options""" usage = """%prog [options] [list of images and/or image folders] Starts a slideshow using the given images and/or image folders. Options are automatically saved, and reused next time you start the slideshow.""" parser = optparse.OptionParser(usage=usage) parser.add_option("-s", "--seconds", action="store", type="float", dest="seconds", default=self.options.get("seconds", SECONDS), help="Interval in seconds between image changes.\n" "Default is %s.\n" "Float, at least 0.1." % SECONDS) parser.add_option( "--fade", action="store", type="float", dest="fade", default=self.options.get("fade", FADE), help="Fade duration, as a fraction of the interval.\n" "Default is 0.4, i.e. 0.4 * 6 = 2.4 seconds.\n" "Float, between 0 and 1.\n" "0 disables fade.") parser.add_option( "--zoom", action="store", type="float", dest="zoom", default=self.options.get("zoom", ZOOM), help="How much to zoom in or out images, as a ratio of their size.\n" "Default is %s.\n" "Float, at least 0.\n" "0 disables zoom." % ZOOM) parser.add_option( "--pan", action="store", type="float", dest="pan", default=self.options.get("pan", PAN), help="How much to pan images sideways, as a ratio of screen size.\n" "Default is %s.\n" "Float, at least 0.\n" "0 disables pan." % PAN) parser.add_option("--sort", action="store", type="string", dest="sort", default=self.options.get("sort", "random"), help=""" In what order to cycle the files. Possible values are: random - random order (Default); keep - keep order, specified on the commandline (only useful when specifying files, not folders); name - sort by folder name, then by filename; date - sort by file date;""") parser.add_option( "--order", action="store", dest="sort_order", default=self.options.get("sort_order", "asc"), help= "Sort order: asc/ascending (this is the default), or desc/descending" ) parser.add_option( "--monitor", action="store", type="int", dest="monitor", default=self.options.get("monitor", 1), help= "On which monitor to run - 1, 2, etc. up to the number of monitors.\n" + self.current_monitors_help()) parser.add_option( "--mode", action="store", dest="mode", default=self.options.get("mode", "fullscreen"), help= "Window mode: possible values are 'fullscreen', 'maximized', 'desktop', 'window' and 'undecorated'. " "Default is fullscreen.") parser.add_option("--title", action="store", type="string", dest="title", default=self.options.get("title", "Variety Slideshow"), help="Window title") parser.add_option( "--defaults", action="store_true", dest="defaults", help="Do not load saved options, use defaults instead. " "You can still specify commandline parameters to override them.") parser.add_option( "--quit-on-motion", action="store_true", dest="quit_on_motion", help="Should mouse motion stop the slideshow, like a screensaver?") cmd_options, args = parser.parse_args(sys.argv) self.options.update(vars(cmd_options)) if 'defaults' in self.options: del self.options['defaults'] if len(args) > 1: self.options.files_and_folders = args[1:] if 'files_and_folders' not in self.options: self.options.files_and_folders = ['/usr/share/backgrounds/'] if self.options.seconds < 0.1: parser.error("Seconds should be at least 0.1") self.interval = self.options.seconds * 1000 if self.options.fade < 0 or self.options.fade > 1: parser.error("Fade should be between 0 and 1") self.fade_time = self.interval * self.options.fade if self.options.zoom < 0: parser.error("Zoom should be at least 0") if self.options.pan < 0: parser.error("Pan should be at least 0") self.options.mode = self.options.mode.lower() if self.options.mode not in ('fullscreen', 'maximized', 'desktop', 'window', 'undecorated', 'desktop'): parser.error( "Window mode: possible values are " "'fullscreen', 'maximized', 'desktop', 'window' and 'undecorated'" ) self.parser = parser def prepare_file_queues(self): self.queued = [] self.files = [] self.error_files = set() self.cursor = 0 for arg in self.options.files_and_folders: path = os.path.abspath(os.path.expanduser(arg)) if is_image(path): self.files.append(path) elif os.path.isdir(path): for root, dirnames, filenames in os.walk(path): for filename in filenames: full_path = os.path.join(root, filename) if is_image(full_path): self.files.append(full_path) if len(self.files) > 2000: break else: continue break else: continue break if not self.files: self.parser.error('You should specify some files or folders') sort = self.options.sort.lower() if sort == 'keep': pass elif sort == 'name': self.files.sort() elif sort == 'date': self.files.sort(key=os.path.getmtime) else: random.shuffle(self.files) if self.options.sort_order.lower().startswith('desc'): self.files.reverse() def get_next_file(self): if not self.running: return None if len(self.queued): return self.queued.pop(0) else: if self.error_files == set(self.files): logging.error( 'Could not find any non-corrupt images, exiting.') self.quit() return None f = self.files[self.cursor] self.cursor = (self.cursor + 1) % len(self.files) if f in self.error_files: return self.get_next_file() else: return f def queue(self, filename): self.queued.append(filename) def connect_signals(self): # Connect signals def on_button_press(*args): if self.current_mode == 'fullscreen' and not self.mode_was_changed: self.quit() def on_motion(*args): if self.options.quit_on_motion and self.current_mode == 'fullscreen' and not self.mode_was_changed: self.quit() def on_key_press(widget, event): if self.current_mode == 'fullscreen' and not self.mode_was_changed: self.quit() return key = Gdk.keyval_name(event.keyval) if key == 'Escape': self.quit() elif key in ('f', 'F', 'F11'): if self.current_mode == 'desktop': return if self.current_mode == 'fullscreen': self.current_mode = 'window' self.unfullscreen() else: self.current_mode = 'fullscreen' self.fullscreen() self.mode_was_changed = True GObject.timeout_add(200, self.next) elif key in ('d', 'D'): if self.current_mode == 'undecorated': self.current_mode = 'window' self.set_decorated(True) else: self.current_mode = 'undecorated' self.set_decorated(False) self.connect("delete-event", self.quit) self.stage.connect('destroy', self.quit) self.stage.connect('key-press-event', on_key_press) self.stage.connect('button-press-event', on_button_press) self.stage.connect('motion-event', on_motion) def run(self): self.running = True self.load_options() # loads from config file self.parse_options( ) # parses the command-line arguments, these take precedence over the saved config self.save_options() self.prepare_file_queues() self.set_title(self.options.title) self.screen = self.get_screen() self.embed = GtkClutter.Embed() self.add(self.embed) self.embed.set_visible(True) self.stage = self.embed.get_stage() self.stage.set_color( Clutter.Color.get_static(Clutter.StaticColor.BLACK)) if self.options.mode == 'fullscreen': self.stage.hide_cursor() self.texture = Clutter.Texture.new() self.next_texture = None self.prev_texture = None self.data_queue = Queue() self.connect_signals() self.will_enlarge = random.choice((True, False)) self.resize(600, 400) self.move_to_monitor(self.options.monitor) self.current_mode = self.options.mode self.mode_was_changed = False if self.options.mode == 'fullscreen': self.fullscreen() self.set_skip_taskbar_hint(True) elif self.options.mode == 'maximized': self.maximize() elif self.options.mode == 'desktop': self.maximize() self.set_decorated(False) self.set_keep_below(True) elif self.options.mode == 'undecorated': self.set_decorated(False) def after_show(*args): def f(): self.move_to_monitor(self.options.monitor) self.prepare_next_data() self.next() GObject.timeout_add(200, f) self.connect('show', lambda *args: GObject.idle_add(after_show)) self.show() Gtk.main() def quit(self, *args): logging.info('Exiting...') self.running = False Gtk.main_quit() def move_to_monitor(self, i): i = max(1, min(i, self.screen.get_n_monitors())) rect = self.screen.get_monitor_geometry(i - 1) self.move(rect.x + (rect.width - self.get_size()[0]) / 2, rect.y + (rect.height - self.get_size()[1]) / 2) def next(self, *args): if not self.running: return try: if hasattr(self, 'next_timeout'): GObject.source_remove(self.next_timeout) delattr(self, 'next_timeout') image_data = self.data_queue.get(True, timeout=1) if not isinstance(image_data, tuple): if image_data: logging.info('Error in %s, skipping it' % image_data) self.error_files.add(image_data) self.prepare_next_data() return self.next() self.next_texture = self.create_texture(image_data) target_size, target_position = self.initialize_pan_and_zoom( self.next_texture) self.stage.add_actor(self.next_texture) self.toggle(self.texture, False) self.toggle(self.next_texture, True) self.start_pan_and_zoom(self.next_texture, target_size, target_position) if self.prev_texture: self.prev_texture.destroy() self.prev_texture = self.texture self.texture = self.next_texture self.next_timeout = GObject.timeout_add( int(self.interval), self.next, priority=GLib.PRIORITY_HIGH) self.prepare_next_data() except: logging.exception('Oops, exception in next, rescheduling:') self.next_timeout = GObject.timeout_add( 100, self.next, priority=GLib.PRIORITY_HIGH) def get_ratio_to_screen(self, texture): return max(self.stage.get_width() / texture.get_width(), self.stage.get_height() / texture.get_height()) def prepare_next_data(self): filename = self.get_next_file() if not filename: self.data_queue.put(None) return def _prepare(filename): os.nice(20) max_w = self.stage.get_width() * (1 + 2 * self.options.zoom) max_h = self.stage.get_height() * (1 + 2 * self.options.zoom) try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( filename, max_w, max_h, True) data = (pixbuf.get_pixels(), pixbuf.get_has_alpha(), pixbuf.get_width(), pixbuf.get_height(), pixbuf.get_rowstride(), 4 if pixbuf.get_has_alpha() else 3) self.data_queue.put(data) except: logging.exception('Could not open file %s' % filename) self.data_queue.put(filename) p = Process(target=_prepare, args=(filename, )) p.daemon = True p.start() def create_texture(self, image_data): data = tuple(image_data) + (Clutter.TextureFlags.NONE, ) texture = Clutter.Texture.new() texture.set_from_rgb_data(*data) texture.set_opacity(0) texture.set_keep_aspect_ratio(True) return texture def initialize_pan_and_zoom(self, texture): self.will_enlarge = not self.will_enlarge pan_px = max(self.stage.get_width(), self.stage.get_height()) * self.options.pan rand_pan = lambda: random.choice( (-1, 1)) * (pan_px + pan_px * random.random()) zoom_factor = (1 + self.options.zoom) * ( 1 + self.options.zoom * random.random()) scale = self.get_ratio_to_screen(texture) base_w, base_h = texture.get_width() * scale, texture.get_height( ) * scale safety_zoom = 1 + self.options.pan / 2 if self.options.zoom > 0 else 1 small_size = base_w * safety_zoom, base_h * safety_zoom big_size = base_w * safety_zoom * zoom_factor, base_h * safety_zoom * zoom_factor small_position = (-(small_size[0] - self.stage.get_width()) / 2, -(small_size[1] - self.stage.get_height()) / 2) big_position = (-(big_size[0] - self.stage.get_width()) / 2 + rand_pan(), -(big_size[1] - self.stage.get_height()) / 2 + rand_pan()) if self.will_enlarge: initial_size, initial_position = small_size, small_position target_size, target_position = big_size, big_position else: initial_size, initial_position = big_size, big_position target_size, target_position = small_size, small_position # set initial size texture.set_size(*initial_size) texture.set_position(*initial_position) return target_size, target_position def start_pan_and_zoom(self, texture, target_size, target_position): # start animating to target size texture.save_easing_state() texture.set_easing_mode(Clutter.AnimationMode.LINEAR) texture.set_easing_duration(self.interval + self.fade_time) texture.set_size(*target_size) texture.set_position(*target_position) def toggle(self, texture, visible): texture.set_reactive(visible) texture.save_easing_state() texture.set_easing_mode(Clutter.AnimationMode.EASE_OUT_SINE if visible else Clutter.AnimationMode.EASE_IN_SINE) texture.set_easing_duration(self.fade_time) texture.set_opacity(255 if visible else 0) if visible: self.stage.raise_child(texture, None)
if plot4 == True: import pickle as pkl figname = 'Paper/figures/Spix_Class.eps' figname = 'AAS_2015/Spix_Class.eps' savefig = True # Load Radio Data data = pkl.Unpickler(open('Radio_Data.pkl','rb')).load() for i in data: try: data[i] = np.array(data[i],float) except ValueError: pass kern = AttrDict(data) kern.Lbol_sources = np.array(kern.Lbol_sources,int) # Plot class0,class1,class2,extragal = [],[],[],[] for i in range(len(kern.classes)): if 'Class 0' in kern.classes[i]: class0.append(i) if 'Class I' in kern.classes[i] and 'Class II' not in kern.classes[i]: class1.append(i) if 'Class II' in kern.classes[i]: class2.append(i) if 'Extragal' in kern.classes[i] and 'Extragal.?' not in kern.classes[i]: extragal.append(i) class0,class1,class2,extragal = np.array(class0),np.array(class1),np.array(class2),np.array(extragal) trust_int_spix = np.where(kern.cm_spix_trust == 'int')[0] trust_peak_spix = np.where(kern.cm_spix_trust == 'peak')[0]
from scipy import stats import pickle as pkl from matplotlib import rc ## Flags ## ## Constants ## dist1 = 0.429 # kpc dist2 = 0.260 # kpc c = 2.99e8 # m/s ## Load In Data ## # Load Shirley07 Data shirley = AttrDict({}) shirley.Lbol_36, shirley.flux_36 = np.loadtxt('Shirley07Data/3.6cmcont.dat',unpack=True) shirley.Lbol_60, shirley.flux_60 = np.loadtxt('Shirley07Data/6cmcont.dat',unpack=True) # Load Scaife11a Data scaifea = AttrDict({}) data = np.loadtxt('Scaife11a_Data.tab',dtype='str',delimiter='\t',usecols=(0,1,2,3,4,5,6,7,8,9)) match = np.where(data.T[4]=='y')[0] match = np.delete(match,[1,11,15,27]) scaifea.classes = data.T[2][match] scaifea.flux_18 = np.array(data.T[5][match],float) scaifea.Menv = data.T[6][match] scaifea.Tbol = data.T[7][match] scaifea.Lbol = np.array(data.T[8][match],float) scaifea.Fout = data.T[9][match]
async def getplaylist(playlist_id): playlist = await api_call('playlist', playlist_id) return AttrDict(playlist)
async def top(self, limit=5): r = await api_call('artist', self.id, 'top', limit=limit) return [AttrDict(track) for track in r]
from AttrDict import AttrDict dictionary = { "entry": "entry_1", "another_dictionary": { "entry_2": "entry_2" }, "a_list": [], "a_list_with_a_dictionary": [{ "entry_3": "entry_3" }] } x = AttrDict(dictionary) # try to get the entry print(x.entry) print(x["entry"]) # try to get the dictionary in the entry another_dictionary print(x.another_dictionary) # try to get the entry 'entry 2' from the dictionary entry another_dictionary print(x.another_dictionary.entry_2) # try to get the list a_list print(x.a_list) # try to get the dictionary in the entry a_list_with_a_dictionary print(x.a_list_with_a_dictionary[0].entry_3) # get the dictionaries using the index print(x["entry"]) print(x["another_dictionary"]) print(x["another_dictionary"]["entry_2"]) print(x["a_list"])
async def radio(self): r = await api_call('artist', self.id, 'radio') return [AttrDict(track) for track in r]
class VarietySlideshow(Gtk.Window): def __init__(self): super(Gtk.Window, self).__init__() def current_monitors_help(self): result = 'Your current monitors are: ' screen = Gdk.Screen.get_default() for i in range(0, screen.get_n_monitors()): geo = screen.get_monitor_geometry(i) result += (', ' if i > 0 else '') + '%d - %s, %dx%d' % ( i + 1, screen.get_monitor_plug_name(i), geo.width, geo.height) return result def load_options(self): if '--defaults' in sys.argv: self.options = AttrDict() return try: with io.open(os.path.expanduser(u'~/.config/variety/variety_slideshow.json'), encoding='utf8') as f: self.options = AttrDict(json.loads(f.read())) except: self.options = AttrDict() def save_options(self): try: try: os.makedirs(os.path.expanduser(u'~/.config/variety/')) except: pass with io.open(os.path.expanduser(u'~/.config/variety/variety_slideshow.json'), 'w', encoding='utf8') as f: f.write(json.dumps(self.options, indent=4, ensure_ascii=False, encoding='utf8')) except: logging.exception(u'Could not save options:') def parse_options(self): """Support for command line options""" usage = """%prog [options] [list of images and/or image folders] Starts a slideshow using the given images and/or image folders. Options are automatically saved, and reused next time you start the slideshow.""" parser = optparse.OptionParser(usage=usage) parser.add_option("-s", "--seconds", action="store", type="float", dest="seconds", default=self.options.get("seconds", SECONDS), help="Interval in seconds between image changes.\n" "Default is %s.\n" "Float, at least 0.1." % SECONDS) parser.add_option("--fade", action="store", type="float", dest="fade", default=self.options.get("fade", FADE), help="Fade duration, as a fraction of the interval.\n" "Default is 0.4, i.e. 0.4 * 6 = 2.4 seconds.\n" "Float, between 0 and 1.\n" "0 disables fade.") parser.add_option("--zoom", action="store", type="float", dest="zoom", default=self.options.get("zoom", ZOOM), help="How much to zoom in or out images, as a ratio of their size.\n" "Default is %s.\n" "Float, at least 0.\n" "0 disables zoom." % ZOOM) parser.add_option("--pan", action="store", type="float", dest="pan", default=self.options.get("pan", PAN), help="How much to pan images sideways, as a ratio of screen size.\n" "Default is %s.\n" "Float, at least 0.\n" "0 disables pan." % PAN) parser.add_option("--sort", action="store", type="string", dest="sort", default=self.options.get("sort", "random"), help=""" In what order to cycle the files. Possible values are: random - random order (Default); keep - keep order, specified on the commandline (only useful when specifying files, not folders); name - sort by folder name, then by filename; date - sort by file date;""") parser.add_option("--order", action="store", dest="sort_order", default=self.options.get("sort_order", "asc"), help="Sort order: asc/ascending (this is the default), or desc/descending") parser.add_option("--monitor", action="store", type="int", dest="monitor", default=self.options.get("monitor", 1), help="On which monitor to run - 1, 2, etc. up to the number of monitors.\n" + self.current_monitors_help()) parser.add_option("--mode", action="store", dest="mode", default=self.options.get("mode", "fullscreen"), help="Window mode: possible values are 'fullscreen', 'maximized', 'desktop', 'window' and 'undecorated'. " "Default is fullscreen.") parser.add_option("--title", action="store", type="string", dest="title", default=self.options.get("title", "Variety Slideshow"), help="Window title") parser.add_option("--defaults", action="store_true", dest="defaults", help="Do not load saved options, use defaults instead. " "You can still specify commandline parameters to override them.") parser.add_option("--quit-on-motion", action="store_true", dest="quit_on_motion", help="Should mouse motion stop the slideshow, like a screensaver?") cmd_options, args = parser.parse_args(sys.argv) self.options.update(vars(cmd_options)) if 'defaults' in self.options: del self.options['defaults'] if len(args) > 1: self.options.files_and_folders = args[1:] if 'files_and_folders' not in self.options: self.options.files_and_folders = ['/usr/share/backgrounds/'] if self.options.seconds < 0.1: parser.error("Seconds should be at least 0.1") self.interval = self.options.seconds * 1000 if self.options.fade < 0 or self.options.fade > 1: parser.error("Fade should be between 0 and 1") self.fade_time = self.interval * self.options.fade if self.options.zoom < 0: parser.error("Zoom should be at least 0") if self.options.pan < 0: parser.error("Pan should be at least 0") self.options.mode = self.options.mode.lower() if self.options.mode not in ('fullscreen', 'maximized', 'desktop', 'window', 'undecorated', 'desktop'): parser.error("Window mode: possible values are " "'fullscreen', 'maximized', 'desktop', 'window' and 'undecorated'") self.parser = parser def prepare_file_queues(self): self.queued = [] self.files = [] self.error_files = set() self.cursor = 0 for arg in self.options.files_and_folders: path = os.path.abspath(os.path.expanduser(arg)) if is_image(path): self.files.append(path) elif os.path.isdir(path): for root, dirnames, filenames in os.walk(path): for filename in filenames: full_path = os.path.join(root, filename) if is_image(full_path): self.files.append(full_path) if len(self.files) > 2000: break else: continue break else: continue break if not self.files: self.parser.error('You should specify some files or folders') sort = self.options.sort.lower() if sort == 'keep': pass elif sort == 'name': self.files.sort() elif sort == 'date': self.files.sort(key=os.path.getmtime) else: random.shuffle(self.files) if self.options.sort_order.lower().startswith('desc'): self.files.reverse() def get_next_file(self): if not self.running: return None if len(self.queued): return self.queued.pop(0) else: if self.error_files == set(self.files): logging.error('Could not find any non-corrupt images, exiting.') self.quit() return None f = self.files[self.cursor] self.cursor = (self.cursor + 1) % len(self.files) if f in self.error_files: return self.get_next_file() else: return f def queue(self, filename): self.queued.append(filename) def connect_signals(self): # Connect signals def on_button_press(*args): if self.current_mode == 'fullscreen' and not self.mode_was_changed: self.quit() def on_motion(*args): if self.options.quit_on_motion and self.current_mode == 'fullscreen' and not self.mode_was_changed: self.quit() def on_key_press(widget, event): if self.current_mode == 'fullscreen' and not self.mode_was_changed: self.quit() return key = Gdk.keyval_name(event.keyval) if key == 'Escape': self.quit() elif key in ('f', 'F', 'F11'): if self.current_mode == 'desktop': return if self.current_mode == 'fullscreen': self.current_mode = 'window' self.unfullscreen() else: self.current_mode = 'fullscreen' self.fullscreen() self.mode_was_changed = True GObject.timeout_add(200, self.next) elif key in ('d', 'D'): if self.current_mode == 'undecorated': self.current_mode = 'window' self.set_decorated(True) else: self.current_mode = 'undecorated' self.set_decorated(False) self.connect("delete-event", self.quit) self.stage.connect('destroy', self.quit) self.stage.connect('key-press-event', on_key_press) self.stage.connect('button-press-event', on_button_press) self.stage.connect('motion-event', on_motion) def run(self): self.running = True self.load_options() # loads from config file self.parse_options() # parses the command-line arguments, these take precedence over the saved config self.save_options() self.prepare_file_queues() self.set_title(self.options.title) self.screen = self.get_screen() self.embed = GtkClutter.Embed() self.add(self.embed) self.embed.set_visible(True) self.stage = self.embed.get_stage() self.stage.set_color(Clutter.Color.get_static(Clutter.StaticColor.BLACK)) if self.options.mode == 'fullscreen': self.stage.hide_cursor() self.texture = Clutter.Texture.new() self.next_texture = None self.prev_texture = None self.data_queue = Queue() self.connect_signals() self.will_enlarge = random.choice((True, False)) self.resize(600, 400) self.move_to_monitor(self.options.monitor) self.current_mode = self.options.mode self.mode_was_changed = False if self.options.mode == 'fullscreen': self.fullscreen() self.set_skip_taskbar_hint(True) elif self.options.mode == 'maximized': self.maximize() elif self.options.mode == 'desktop': self.maximize() self.set_decorated(False) self.set_keep_below(True) elif self.options.mode == 'undecorated': self.set_decorated(False) def after_show(*args): def f(): self.move_to_monitor(self.options.monitor) self.prepare_next_data() self.next() GObject.timeout_add(200, f) self.connect('show', lambda *args: GObject.idle_add(after_show)) self.show() Gtk.main() def quit(self, *args): logging.info('Exiting...') self.running = False Gtk.main_quit() def move_to_monitor(self, i): i = max(1, min(i, self.screen.get_n_monitors())) rect = self.screen.get_monitor_geometry(i - 1) self.move(rect.x + (rect.width - self.get_size()[0]) / 2, rect.y + (rect.height - self.get_size()[1]) / 2) def next(self, *args): if not self.running: return try: if hasattr(self, 'next_timeout'): GObject.source_remove(self.next_timeout) delattr(self, 'next_timeout') image_data = self.data_queue.get(True, timeout=1) if not isinstance(image_data, tuple): if image_data: logging.info('Error in %s, skipping it' % image_data) self.error_files.add(image_data) self.prepare_next_data() return self.next() self.next_texture = self.create_texture(image_data) target_size, target_position = self.initialize_pan_and_zoom(self.next_texture) self.stage.add_actor(self.next_texture) self.toggle(self.texture, False) self.toggle(self.next_texture, True) self.start_pan_and_zoom(self.next_texture, target_size, target_position) if self.prev_texture: self.prev_texture.destroy() self.prev_texture = self.texture self.texture = self.next_texture self.next_timeout = GObject.timeout_add(int(self.interval), self.next, priority=GLib.PRIORITY_HIGH) self.prepare_next_data() except: logging.exception('Oops, exception in next, rescheduling:') self.next_timeout = GObject.timeout_add(100, self.next, priority=GLib.PRIORITY_HIGH) def get_ratio_to_screen(self, texture): return max(self.stage.get_width() / texture.get_width(), self.stage.get_height() / texture.get_height()) def prepare_next_data(self): filename = self.get_next_file() if not filename: self.data_queue.put(None) return def _prepare(filename): os.nice(20) max_w = self.stage.get_width() * (1 + 2 * self.options.zoom) max_h = self.stage.get_height() * (1 + 2 * self.options.zoom) try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(filename, max_w, max_h, True) data = ( pixbuf.get_pixels(), pixbuf.get_has_alpha(), pixbuf.get_width(), pixbuf.get_height(), pixbuf.get_rowstride(), 4 if pixbuf.get_has_alpha() else 3) self.data_queue.put(data) except: logging.exception('Could not open file %s' % filename) self.data_queue.put(filename) p = Process(target=_prepare, args=(filename,)) p.daemon = True p.start() def create_texture(self, image_data): data = tuple(image_data) + (Clutter.TextureFlags.NONE,) texture = Clutter.Texture.new() texture.set_from_rgb_data(*data) texture.set_opacity(0) texture.set_keep_aspect_ratio(True) return texture def initialize_pan_and_zoom(self, texture): self.will_enlarge = not self.will_enlarge pan_px = max(self.stage.get_width(), self.stage.get_height()) * self.options.pan rand_pan = lambda: random.choice((-1, 1)) * (pan_px + pan_px * random.random()) zoom_factor = (1 + self.options.zoom) * (1 + self.options.zoom * random.random()) scale = self.get_ratio_to_screen(texture) base_w, base_h = texture.get_width() * scale, texture.get_height() * scale safety_zoom = 1 + self.options.pan / 2 if self.options.zoom > 0 else 1 small_size = base_w * safety_zoom, base_h * safety_zoom big_size = base_w * safety_zoom * zoom_factor, base_h * safety_zoom * zoom_factor small_position = (-(small_size[0] - self.stage.get_width()) / 2, -(small_size[1] - self.stage.get_height()) / 2) big_position = (-(big_size[0] - self.stage.get_width()) / 2 + rand_pan(), -(big_size[1] - self.stage.get_height()) / 2 + rand_pan()) if self.will_enlarge: initial_size, initial_position = small_size, small_position target_size, target_position = big_size, big_position else: initial_size, initial_position = big_size, big_position target_size, target_position = small_size, small_position # set initial size texture.set_size(*initial_size) texture.set_position(*initial_position) return target_size, target_position def start_pan_and_zoom(self, texture, target_size, target_position): # start animating to target size texture.save_easing_state() texture.set_easing_mode(Clutter.AnimationMode.LINEAR) texture.set_easing_duration(self.interval + self.fade_time) texture.set_size(*target_size) texture.set_position(*target_position) def toggle(self, texture, visible): texture.set_reactive(visible) texture.save_easing_state() texture.set_easing_mode(Clutter.AnimationMode.EASE_OUT_SINE if visible else Clutter.AnimationMode.EASE_IN_SINE) texture.set_easing_duration(self.fade_time) texture.set_opacity(255 if visible else 0) if visible: self.stage.raise_child(texture, None)
async def search(query): data = {'access_token': genius_token, 'q': query} r = await request_get('https://api.genius.com/search', params=data) search_obj = AttrDict(await r.json()) return [Song(r.result) for r in search_obj.response.hits]