class StdConfigItemNumber(StdConfigItemBase): def __init__(self, fmt, udm, min, max, step, *args): self._fmt = fmt self._udm = udm self._min = min self._max = max self._step = step self._val = None self._dia = None StdConfigItemBase.__init__(self, *args) def label_end_get(self, url, user_data): if self._udm: return ini.get(self._sec, self._opt) + ' ' + self._udm else: return ini.get(self._sec, self._opt) def item_selected(self, url, user_data): self._val = ini.get_float(self._sec, self._opt) self._dia = EmcDialog(style='minimal', title=self._lbl, text='') self._dia.button_add(_('Ok'), self._btn_ok_cb) self._dia.button_add(None, self._btn_plus_cb, icon='icon/plus') self._dia.button_add(None, self._btn_minus_cb, icon='icon/minus') self._dia.button_add(_('Cancel'), self._btn_canc_cb) self._dia_text_update() def _dia_text_update(self): val = (self._fmt % self._val) + ' ' + self._udm self._dia.text_set('<br><br><br><center><bigger>%s</bigger></center>' % val) def _btn_plus_cb(self, btn): self._val += self._step self._val = min(self._val, self._max) self._dia_text_update() def _btn_minus_cb(self, btn): self._val -= self._step self._val = max(self._val, self._min) self._dia_text_update() def _btn_canc_cb(self, btn): self._dia.delete() def _btn_ok_cb(self, btn): val = self._fmt % self._val ini.set(self._sec, self._opt, val) self._dia.delete() StdConfigItemBase.__done__(self)
class Test_Url(GenericItemClass): url1 = 'http://ipv4.download.thinkbroadband.com/5MB.zip' url2 = 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/' \ 'resources/pdf/dummy.pdf' def __init__(self): super().__init__() self.dia = None self.dload = None def item_selected(self, url, user_data): self.dia = EmcDialog(style='progress', title=user_data, text='Press start to test a 5MB download') self.dia.button_add('Close', lambda b: self.dia.delete()) self.dia.button_add('delete()', self.delete_btn_cb) self.dia.button_add('To file (5M)', self.start_btn_cb, self.url1) self.dia.button_add('To mem (13K)', self.start_btn_cb, self.url2) def start_btn_cb(self, btn, url): self.dia.text_set('Download started...') dest = '::tmp::' if url == self.url1 else '::mem::' self.dload = EmcUrl(url, dest=dest, done_cb=self.done_cb, prog_cb=self.prog_cb, decode=None, parent=self.dia) def delete_btn_cb(self, btn): if self.dload: self.dload.delete() def done_cb(self, url, success, dest): print("DONE", success) if success and url.dest == '::mem::': size = len(dest) self.dia.text_set( 'Download successfully completed to Memory<br>' 'First bytes: {}<br>Download size: {} bytes'.format( dest[:10], size)) elif success and os.path.exists(dest): size = utils.hum_size(os.path.getsize(dest)) self.dia.text_set('Download successfully completed to<br>{}<br>' 'File size: {} '.format(dest, size)) else: self.dia.text_set("Error !!") self.dload = None def prog_cb(self, url, total, received): print("PROGRESS", url, total, received) self.dia.progress_set(received / total if total else 0)
class Test_Exe(GenericItemClass): path = os.path.dirname(__file__) infinite_path = os.path.join(path, 'infinite.py') def __init__(self): super().__init__() self.dia = None self.exe = None def item_selected(self, url, user_data): self.dia = EmcDialog(title='EmcExe test', text='Press a button to run a command') self.dia.button_add('Close', lambda b: self.dia.delete()) self.dia.button_add('delete()', lambda b: self.exe.delete()) self.dia.button_add('"wrong"', self.run_btn_cb, ('wrong', )) self.dia.button_add('"infinite.py"', self.run_btn_cb, (sys.executable, self.infinite_path)) self.dia.button_add('"ls -l"', self.run_btn_cb, ('ls', '-l')) self.dia.button_add('"ls"', self.run_btn_cb, ('ls', )) def run_btn_cb(self, btn, pars): cmd = pars[0] params = pars[1:] self.exe = EmcExe(cmd, params, grab_output=True, done_cb=self.exe_done_cb, parent=self.dia, asd1='asd_1', asd2='asd_2') self.dia.text_set('Running: "{} {}"<br><br>'.format( cmd, ' '.join(params))) def exe_done_cb(self, exit_code, output, asd1, asd2): assert asd1 == 'asd_1' assert asd2 == 'asd_2' print("OUT", exit_code, output) self.dia.text_append('Process completed<br>' 'exit_code: {}<br>' 'output: {}'.format(exit_code, repr(output)))
class EmcFolderSelector(object): """ Open a dialog that allow the user to choose a path on the filesystem. Args: title: The (optional) dialog title. done_cb: The (mandatory) function to call when the selection is done. Signature: func(path, **kargs) **kargs: Any other keyword arguments will be passed back in the done_cd func """ def __init__(self, title=None, done_cb=None, **kargs): self._user_cb = done_cb self._user_kargs = kargs self._dialog = EmcDialog(title or _('Source Selector'), style='list') self._dialog.button_add(_('Select'), self._btn_select_cb) self._dialog.button_add(_('Browse'), self._btn_browse_cb, default=True) self.populate_devices() def populate_devices(self): self._dialog.list_clear() # other storage devices for dev in storage.list_devices(): if dev.is_mounted: it = self._dialog.list_item_append(dev.label, dev.icon) it.data['root'] = it.data['path'] = dev.mount_point self._dialog.list_go() def populate_folder(self, root, folder): if folder == '': # back in '/' self.populate_devices() return try: folders = os.listdir(folder) except PermissionError: EmcDialog(style='error', text=_('Permission denied')) return self._dialog.list_clear() # back item parent = os.path.normpath(os.path.join(folder, '..')) it = self._dialog.list_item_append(_('Back'), 'icon/back') it.data['root'] = root it.data['path'] = parent if parent != folder else '' # back in '/' # folders for fname in utils.natural_sort(folders): fullpath = os.path.join(folder, fname) if fname[0] != '.' and os.path.isdir(fullpath): it = self._dialog.list_item_append(fname, 'icon/folder') it.data['root'] = root it.data['path'] = fullpath self._dialog.list_go() def _btn_browse_cb(self, btn): it = self._dialog.list_item_selected_get() if len(it.data['path']) < len(it.data['root']): self.populate_devices() else: self.populate_folder(it.data['root'], it.data['path']) def _btn_select_cb(self, btn): path = self._dialog.list_item_selected_get().data['path'] if path and callable(self._user_cb): self._user_cb('file://' + path, **self._user_kargs) self._dialog.delete()
class CastPanel(object): def __init__(self, pid=None, name=None, lang=DEFAULT_INFO_LANG): self.pid = pid self.info = None tmdb = TMDBv3(lang=lang) if name: tmdb.person_search(name, self._search_done_cb) elif pid: tmdb.get_cast_info(self.pid, self._fetch_done_cb) self._dia = EmcDialog(style='minimal', title=_('Fetching info'), content='image/tmdb_logo.png', spinner=True) def _search_done_cb(self, tmdb, result): # TODO manage errors self.pid = result['results'][0]['id'] tmdb.get_cast_info(self.pid, self._fetch_done_cb) def _fetch_done_cb(self, tmdb, result): self.info = result self._dia.delete() text = '' if self.info.get('biography'): text += '%s<br><br>' % self.info['biography'].replace('\n', '<br>') if self.info.get('birthday'): text += '<name>%s:</name> %s<br>' % (_('Birthday'), self.info['birthday']) if self.info.get('deathday'): text += '<name>%s:</name> %s<br>' % (_('Deathday'), self.info['deathday']) if self.info.get('place_of_birth'): text += '<name>%s:</name> %s<br>' % (_('Place of birth'), self.info['place_of_birth']) self._dia = EmcDialog(title=self.info['name'], style='panel', content=self.info['profile_path'], text=text) # merge cast and crew in a single movies list movies = {} # key: tmdbid val: tmdb-data-dict + 'jobs' for movie in self.info['credits']['crew']: tid = movie['id'] if tid in movies: movies[tid]['jobs'].append(movie.get('job')) else: movie['jobs'] = [movie.get('job')] movies[tid] = movie for movie in self.info['credits']['cast']: tid = movie['id'] if tid in movies: movies[tid]['character'] = movie.get('character') else: movies[tid] = movie self._dia.button_add( _('Movies (%s)') % len(movies), lambda b: self.movies_dialog(movies)) num = len(self.info['images']['profiles']) self._dia.button_add( _('Photos (%s)') % num, lambda b: self.photos_dialog()) def photos_dialog(self): dia = EmcDialog(style='image_list_portrait', title=self.info['name']) for image in self.info['images']['profiles']: dia.list_item_append(None, image['file_path']) dia.list_go() def movies_dialog(self, movies): dia = EmcDialog(style='list', title=self.info['name']) for movie in sorted(movies.values(), key=itemgetter('title')): label = '<big>{}</big>'.format(movie['title']) if 'character' in movie: label += ' <i>{} <big>{}</big></i>'.format( _('as'), movie['character']) if 'jobs' in movie: label += ' <i>({})</i>'.format(', '.join(movie['jobs'])) dia.list_item_append(label, movie.get('poster_path')) dia.list_go()
class KeyboardModule(EmcModule): name = 'input_keyb' label = _('Input - Keyboard') icon = 'icon/keyboard' info = _('The keyboard module lets you control the application using ' 'your keyboard, or any other device that act as a keyboard.') def __init__(self): DBG('Init module') self.grab_key_func = None # set up default bindings section = 'keyboard' if not ini.has_section(section): ini.add_section(section) defs = EmcGui.instance().default_keymap_get() for key, event in defs.items(): ini.set(section, key, event) # read mapping from config self.keys = dict() for key, event in ini.get_options(section): DBG('Map key "%s" to event %s' % (key, event)) self.keys[key] = event # add an entry in the config gui section config_gui.root_item_add('keyb', 50, _('Keyboard'), icon='icon/keyboard', callback=self.config_panel_cb) # ask the gui to forward key events to us EmcGui.instance().key_down_connect(self._key_down_cb) def __shutdown__(self): DBG('Shutdown module') config_gui.root_item_del('keyb') EmcGui.instance().key_down_connect(None) def _key_down_cb(self, key): DBG('Key: %s' % key) key = key.lower() # if grabbed request call the grab function, else emit the signal if self.grab_key_func and callable(self.grab_key_func): self.grab_key_func(key) else: try: input_events.event_emit(self.keys[key]) except KeyError: print('Not mapped key: ' + key) # ---- config panel stuff ---- class KeyItemClass(EmcItemClass): def item_selected(self, url, data): key, event, mod = data txt = '%s<br><br>%s ⇾ %s' % ( _('Are you sure you want to remove the mapping?'), key, event) EmcDialog(style='yesno', title=_('Remove key'), text=txt, done_cb=self._remove_confirmed, user_data=data) def _remove_confirmed(self, dia): key, event, mod = dia.data_get() # remove key from mapping and from config mod.keys.pop(key, None) ini.remove_option('keyboard', key) # kill the dialog and refresh the browser bro = config_gui.browser_get() bro.refresh(hard=True) dia.delete() def label_get(self, url, data): key, event, mod = data return key def label_end_get(self, url, data): key, event, mod = data return event def icon_get(self, url, data): return 'icon/key' def config_panel_cb(self): bro = config_gui.browser_get() bro.page_add('config://keyb/', _('Keyboard'), None, self.populate_keyb) def populate_keyb(self, browser, url): config_gui.standard_item_action_add(_('Add a new key'), icon='icon/plus', cb=self._add_item_cb) for key, event in sorted(self.keys.items(), key=lambda x: x[1]): browser.item_add(self.KeyItemClass(), 'config://keyb/button', (key, event, self)) def _add_item_cb(self): # grab the next pressed key and show the first dialog self.grab_key_func = self.grabbed_key_func self.dia = EmcDialog(title=_('Add a new key'), style='cancel', text=_('Press a key on your keyboard'), canc_cb=self.ungrab_key) def ungrab_key(self, dialog): self.grab_key_func = None dialog.delete() def grabbed_key_func(self, key): # ungrab remote keys & delete the first dialog self.grab_key_func = None self.pressed_key = key self.dia.delete() # create the dialog to choose the event to bind dia = EmcDialog(title=_('Choose an event to bind'), style='list', done_cb=self.event_choosed_cb) for event in input_events.STANDARD_EVENTS.split(): dia.list_item_append(event) dia.list_go() def event_choosed_cb(self, dia): event = str(dia.list_item_selected_get().label) key = str(self.pressed_key) # save the pressed key in mapping and config self.keys[key] = event ini.set('keyboard', key, event) # kill the dialog and refresh the browser dia.delete() bro = config_gui.browser_get() bro.refresh(hard=True)