Página 1 de 4

Cuadro de configuracion personalizado

Publicado: 04 Dic 2015, 15:45
por super_berny
(Editado por actualizacion PR#157)

La idea de este cuadro es añadir ajustes por canales, aunque podreis utilizarlo dentro de vuestros canales siempre q necesiteis un cuadro de opciones.
En este caso se trata de un cuadro para Kodi y PLEX, pero la idea seria adaptarlo al resto de las plataformas.
Y despues de esta introduccion vamos a ver en q consiste y como utilizarlo:

SettingWindow:
Esta clase deriva de xbmcgui.WindowXMLDialogy permite crear un cuadro de dialogo con controles del tipo: Radio Button (bool), Cuadro de texto (text), Lista (list) y Etiquetas informativas (label).
Tambien podemos personalizar el cuadro añadiendole un titulo (caption).

Metodo constructor:
SettingWindow(listado_controles, dict_values, title, callback, item)
  • Parametros:
  • listado_controles: (list) Lista de controles a incluir en la ventana, segun el siguiente esquema:

    Código: Seleccionar todo

    (opcional)list_controls= [
                                    {'id': "nameControl1",
                                      'type': "bool",                       # bool, text, list, label 
                                      'label': "Control 1: tipo RadioButton",
                                      'color': '0xFFee66CC',                # color del texto en formato ARGB hexadecimal
                                      'default': True,
                                      'enabled': True,
                                      'visible': True
                                    },
                                    {'id': "nameControl2",
                                      'type': "text",                       # bool, text, list, label 
                                      'label': "Control 2: tipo Cuadro de texto",
                                      'color': '0xFFee66CC',
                                      'default': "Valor por defecto",
                                      'hidden': False,                      # only for type = text Indica si hay que ocultar el texto (para passwords)
                                      'enabled': True,
                                      'visible': True
                                    },
                                    {'id': "nameControl3",
                                      'type': "list",                       # bool, text, list, label 
                                      'label': "Control 3: tipo Lista",
                                      'color': '0xFFee66CC',
                                      'default': 0,                         # Indice del valor por defecto en lvalues 
                                      'enabled': True,
                                      'visible': True,
                                      'lvalues':["item1", "item2", "item3", "item4"],  # only for type = list
                                    },
                                    {'id': "nameControl4",
                                      'type': "label",                       # bool, text, list, label 
                                      'label': "Control 4: tipo Etiqueta",
                                      'color': '0xFFee66CC',               
                                      'enabled': True,
                                      'visible': True
                                    }]
    
    Si no se incluye el listado_controles, se intenta obtener del xml del canal desde donde se hace la llamada.

    Código: Seleccionar todo

    El formato de los controles en el xml es:
                            <?xml version="1.0" encoding="UTF-8" ?>
                            <channel>
                                ...
                                ...
                                <settings>
                                    <id>nameControl1</id>
                                    <type>bool</type>
                                    <label>Control 1: tipo RadioButton</label>
                                    <default>false</default>
                                    <enabled>true</enabled>
                                    <visible>true</visible>
                                    <color>0xFFee66CC</color>
                                </settings>
                                <settings>
                                    <id>nameControl2</id>
                                    <type>text</type>
                                    <label>Control 2: tipo Cuadro de texto</label>
                                    <default>Valor por defecto</default>
                                    <hidden>true</hidden>
                                    <enabled>true</enabled>
                                    <visible>true</visible>
                                    <color>0xFFee66CC</color>
                                </settings>
                                <settings>
                                    <id>nameControl3</id>
                                    <type>list</type>
                                    <label>Control 3: tipo Lista</label>
                                    <default>0</default>
                                    <enabled>true</enabled>
                                    <color>0xFFee66CC</color>
                                    <visible>true</visible>
                                    <lvalues>item1</lvalues>
                                    <lvalues>item2</lvalues>
                                    <lvalues>item3</lvalues>
                                    <lvalues>item4</lvalues>
                                </settings>
                                <settings>
                                    <id>nameControl4</id>
                                    <type>label</type>
                                    <label>Control 4: tipo Etiqueta</label>
                                    <enabled>true</enabled>
                                    <visible>true</visible>
                                    <color>0xFFee66CC</color>
                                </settings>
                                ...
                            </channel>  
    Los campos 'label', 'default' y 'lvalues' pueden ser un numero precedido de '@'. En cuyo caso se buscara el literal en el archivo string.xml del idioma seleccionado.
    Los campos 'enabled' y 'visible' admiten los comparadores eq(), gt() e it() y su funcionamiento se describe en: http://kodi.wiki/view/Add-on_settings#Different_types
  • dict_values: (dict) Diccionario que representa el par (id: valor) de cada control de la lista.
    Si algun control de la lista no esta incluido en este diccionario se le asignara el valor por defecto.

    Código: Seleccionar todo

    dict_values={"nameControl1": False,
                  "nameControl2": "Esto es un ejemplo"}
  • (opcional) title: (str) Titulo de la ventana de configuracion. Se puede localizar mediante un numero precedido de '@'
  • (opcional) callback (str) Nombre de la funcion, del canal desde el que se realiza la llamada, que sera invocada al pulsar
    el boton aceptar de la ventana. A esta funcion se le pasara como parametros el objeto 'item' y el dicionario 'dict_values'
Retorno:
  • Si se especifica 'callback' se devolvera lo que devuelva esta funcion. Si no devolvera None

Modos de uso:
  • platformtools.show_channel_settings(): Así tal cual, sin pasar ningún argumento, la ventana detecta de que canal se ha hecho la llamada,
    y lee los ajustes del XML y carga los controles, cuando le das a Aceptar los vuelve a guardar.
  • return platformtools.show_channel_settings(list_controls=list_controls, dict_values=dict_values, callback='cb', ítem=ítem):
    Así abre la ventana con los controles pasados y los valores de dict_values, si no se pasa dict_values, carga los valores por defecto de los controles, cuando le das a aceptar, llama a la función 'cb' del canal desde donde se ha llamado, pasando como parámetros, el ítem y el dict_values. Esta llamada devolvera lo que devuelva la funcion callback(item,dict_values). El siguiente codigo es un ejemplo de funcion callback que nos ayudara a recuperar los valores seleccionados en el cuadro:

    Código: Seleccionar todo

    def cb(ítem,dict_values):
        for v in dict_values:
          # Hacemos algun tratamiento a los valores devueltos
           .......... 
    return dict_values
    

Actualizaciones
  • v2.0: Gracias a divardr por todo su trabajo en esta version
    • Añadido parametro 'color' en todos los controles para fijar el color de la etiqueta.
      Los parametros 'enabled' y 'visible' ahora admiten las funciones condicionales eq(), gt(), it() y sus negaciones de modo similar a los descritos en http://kodi.wiki/view/Add-on_settings#Different_types
    • Se ha incluido el parametro 'hidden' en los controles de texto para ocultar las contraseñas.
    • Los valores guardados por los controles del tipo lista pasan a ser el indice de la lista en lugar de su valor como hasta ahora (esto facilita el uso de listas localizadas al idioma por defecto).
    v1.6:
    • Se incluye la posibilidad de añadir etiquetas @XXXXX que seran sustituidas por los literales incluidos en el archivo string.xml del idioma utilizado.
    • Tambien se puede utilizar #X para referirse al indice del elemento por defecto en los controle tipo lista.
    • El tamaño de la ventana varia en funcion del numero de controles añadidos.
    v1.4:
    • Se añade un nuevo metodo show_settings que muestra un cuadro de dialogo personalizado y guarda los datos al cerrarlo.
    v1.3
    • Se añaden metodos para llamar a los cuadro de dialogo propios de xbmcgui.

Re: Cuadro de configuracion personalizado

Publicado: 04 Dic 2015, 18:55
por SeiTaN
jo jo jo berny noel me ha traído un regalo de navidad :mrgreen:

Le echaré un ojo, según he mirado por encima tiene buena pinta porque es parecido a lo que había pensado para mi desarrollo y no había encontrado la manera con la documentación de xbmc.

- canal.json --> tiene los arrays de configuración del canal, calidad e idioma, además de si el canal está adaptado para tener filtro, en un futuro personalizar los custom tag para que sea uno para idioma, calidad, etc...
- USERDATA\canal.data.json --> los datos personalizados del canal, en mi caso los filtros de series
- xbmctools --> tendría una opcion "Filtrar" para un menu contextual, que a través del channeltools obtener el valor de si ese canal soporta filtro, y este a su vez habra otro menu contextual hijo (aquí tu desarrollo).
- filtertools --> mi gestor de filtros.
-canal.py --> modificaciones para pasarle los enlaces a filtertools, y obtendría los valores de idioma o calidad del canal.json a través del channeltools.

Por cierto una capturilla quedaría bien, y prometo darte mi opinión ;)

Re: Cuadro de configuracion personalizado

Publicado: 04 Dic 2015, 22:57
por SeiTaN
Imagen

No está mal, me falta hacer crear los componentes dinamicamente, y ya me pondré a probar como hacer el "aceptar" para que salve ;)

super_berny, ¿habría una opción de poder llamar a una ventana desde otra?

Es decir que yo llame a la ventana listado, con las series que están filtradas y al pinchar en una yo llamaría a la ventana que se muestra en la captura.

un componente botón, no sé si me explico.

Re: Cuadro de configuracion personalizado

Publicado: 04 Dic 2015, 23:22
por super_berny
@seitan:
El tema del botón lo había pensado, pero lo descarte pensando q nadie lo iba a utilizar. Jejeje ya ves :lol:
De todos modos creó q yo enfocaria el tema de la siguiente manera:
  • Añadiría un ítem en el menú principal que abriese la ventana con todas las series filtradas (la captura q has puesto, pero con todas las series que tengas filtradas)
  • Cuando entras dentro de una serie, el primer ítem (o el último) antes de los enlaces a las temporadas o capítulos, habría un nuevo ítem "Filtrar serie" q abrira la ventana pero en este caso sólo con los datos de esa serie (como tu captura).
De esta manera no necesitas abrir varias ventanas, ni utilizar menú contextual.
¿Q te parece?

Re: Cuadro de configuracion personalizado

Publicado: 04 Dic 2015, 23:36
por SeiTaN
super_berny escribió:@seitan:
El tema del botón lo había pensado, pero lo descarte pensando q nadie lo iba a utilizar. Jejeje ya ves :lol:
De todos modos creó q yo enfocaria el tema de la siguiente manera:
  • Añadiría un ítem en el menú principal que abriese la ventana con todas las series filtradas (la captura q has puesto, pero con todas las series que tengas filtradas)
Eso ya lo hago en mi v2.5, y aparte permito configurar con todo el menú que es un caos como sabes con el botón volver.
super_berny escribió: [*] Cuando entras dentro de una serie, el primer ítem (o el último) antes de los enlaces a las temporadas o capítulos, habría un nuevo ítem "Filtrar serie" q abrira la ventana pero en este caso sólo con los datos de esa serie (como tu captura). [/list]
De esta manera no necesitas abrir varias ventanas, ni utilizar menú contextual.
¿Q te parece?
Es otra opción, si al final voy a acabar haciendo minimo 3 versiones, la que tengo ya, la que sugieres tu y la de robalo con menu contextual :lol: :lol: :lol: :lol: :lol:


La verdad es que la manera de gestionar las series filtradas me está empezando a dar dolor de cabeza, porque hay tantas posibilidades...

De momento he creado dos menús contextuales, cuando se muestran los enlaces.

Imagen

Si ya está filtrada la serie y no muestra enlaces
Imagen

Se puede eliminar el filtro de esa serie
Imagen

Y cuando pincho en "...." (volver), ya muestra todos los enlaces sin filtrar
Imagen

Esta ultima parte me gustaría que se recargara solo, la llamada al context_menu es esta

Código: Seleccionar todo

        filter_serie_command = "XBMC.Container.Update(%s?channel=%s&action=%s&category=%s&title=%s&url=%s" \
                               "&thumbnail=%s&show=%s&extra=%s)" \
                               % (sys.argv[0], "filtertools", "del_filter", urllib.quote_plus(category),
                                  urllib.quote_plus(title), urllib.quote_plus(url), urllib.quote_plus(thumbnail),
                                  urllib.quote_plus(show), urllib.quote_plus(extradata))
        context_commands.append(("eliminar filtro", filter_serie_command))
A ver que se te ocurre.

Gracias.

Re: Cuadro de configuracion personalizado

Publicado: 05 Dic 2015, 10:40
por SeiTaN
Ya he conseguido la última parte, llamando directamente al back, aunque quedaría mejor que se hiciera automaticamente el refresh.

Código: Seleccionar todo

    xbmc.executebuiltin("XBMC.Action(back)")

Re: Cuadro de configuracion personalizado

Publicado: 05 Dic 2015, 16:10
por SeiTaN
super_berny, ¿como se si ha pulsado en "aceptar" al guardar?

Gracias.

Re: Cuadro de configuracion personalizado

Publicado: 05 Dic 2015, 16:40
por super_berny
SeiTaN escribió:super_berny, ¿como se si ha pulsado en "aceptar" al guardar?
En un principio pense que get_values() devolviese dos valores: uno el diccionario dict_values y otro un booleano q indicara si se ha pulsado Aceptar o no, pero para no complicarle la vida a los 'posibles' usuarios lo descarte y ahora solo retorna el diccionario.

De todos modos te voy a explicar un truco para saber si se ha pulsado Aceptar (o la secuencia cerrar 'X' + 'conservar lo cambios') o no:
en list_controls añadimos un control no visible, pero no lo añadimos a dict_values. Despues de llamar a get_values comprobamos si existe ese control en el diccionario: si existe es por q se han guardado los cambios, si no existe es por q se ha salido sin guardar.
Atencion antes de guardar el diccionario en un fichero habria q eliminar esta key.
Por ejemplo:

Código: Seleccionar todo

list_controls= [...#aqui estarian nuestros controles...
                    ,{'id': "nameControl5",
                      'type': "bool",                       # bool, text, list, label 
                      'label': "Control 5: No visible",
                      'default': True,
                      'enabled': True,
                      'visible': False,
                      'lvalues':[],                         # only for type = list
                    }]
dict_values={# Cuanquier cosa menos nameControl5
                 "nameControl1": False,
                 "nameControl2": "Esto es un ejemplo"}

ventana = guitools.SettingWindow(list_controls, dict_values, 'Titulo ventana de pruebas')
ventana.doModal()
dict_resultado = ventana.get_values()
if dict_resultado.has_key('nameControl5'):
      del dict_resultado['nameControl5']
      guitools.dialog_ok("Se han guardado los cambios", str(dict_resultado))
else:
      guitools.dialog_ok("No se han guardado los cambios", str(dict_resultado))



Re: Cuadro de configuracion personalizado

Publicado: 05 Dic 2015, 16:48
por SeiTaN
super_berny escribió:
SeiTaN escribió:super_berny, ¿como se si ha pulsado en "aceptar" al guardar?
En un principio pense que get_values() devolviese dos valores: uno el diccionario dict_values y otro un booleano q indicara si se ha pulsado Aceptar o no, pero para no complicarle la vida a los 'posibles' usuarios lo descarte y ahora solo retorna el diccionario.

De todos modos te voy a explicar un truco para saber si se ha pulsado Aceptar (o la secuencia cerrar 'X' + 'conservar lo cambios') o no:
en list_controls añadimos un control no visible, pero no lo añadimos a dict_values. Despues de llamar a get_values comprobamos si existe ese control en el diccionario: si existe es por q se han guardado los cambios, si no existe es por q se ha salido sin guardar.
Atencion antes de guardar el diccionario en un fichero habria q eliminar esta key.
Por ejemplo:

Código: Seleccionar todo

list_controls= [...#aqui estarian nuestros controles...
                    ,{'id': "nameControl5",
                      'type': "bool",                       # bool, text, list, label 
                      'label': "Control 5: No visible",
                      'default': True,
                      'enabled': True,
                      'visible': False,
                      'lvalues':[],                         # only for type = list
                    }]
dict_values={# Cuanquier cosa menos nameControl5
                 "nameControl1": False,
                 "nameControl2": "Esto es un ejemplo"}

ventana = guitools.SettingWindow(list_controls, dict_values, 'Titulo ventana de pruebas')
ventana.doModal()
dict_resultado = ventana.get_values()
if dict_resultado.has_key('nameControl5'):
      del dict_resultado['nameControl5']
      guitools.dialog_ok("Se han guardado los cambios", str(dict_resultado))
else:
      guitools.dialog_ok("No se han guardado los cambios", str(dict_resultado))


¿No es más sencillo crear una propiedad para la ventana? "is_confirmed" boolean que se cambia cuando se ejecuta "__save_values()", y que se acceda por un metodo publico, ¿"is_confirmed()"? Así no habría que estar creando un control como flag.

Re: Cuadro de configuracion personalizado

Publicado: 05 Dic 2015, 16:53
por super_berny
SeiTaN escribió:¿No es más sencillo crear una propiedad para la ventana? "is_confirmed" boolean que se cambia cuando se ejecuta "__save_values()", y que se acceda por un metodo publico, ¿"is_confirmed()"? Así no habría que estar creando un control como flag.
:oops:
Pues si, lo apunto para la proxima revision