#!/usr/bin/env python # -*- coding: utf-8 -*- # Widgets.py por: # Flavio Danesse # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os import base64 from math import pi import gi from gi.repository import Gtk from gi.repository import Gdk from gi.repository import GObject from gi.repository import Pango from gi.repository import GdkPixbuf import Globales as G HOME = os.environ["HOME"] WORKPATH = os.path.join(HOME, "CeibalEncuesta") PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__)) ICON = PROJECT_ROOT + '/Iconos/ceibal.png' NEXT_PAGE_ACTIVE = PROJECT_ROOT + "/Iconos/next-page-active.png" NEXT_PAGE_INACTIVE = PROJECT_ROOT + "/Iconos/next-page-inactive.png" NEXT_PAGE_OVER = PROJECT_ROOT + "/Iconos/next-page-over.png" NEXT_PAGE_DOWN = PROJECT_ROOT + "/Iconos/next-page-down.png" PREV_PAGE_ACTIVE = PROJECT_ROOT + "/Iconos/prev-page-active.png" PREV_PAGE_OVER = PROJECT_ROOT + "/Iconos/prev-page-over.png" PREV_PAGE_DOWN = PROJECT_ROOT + "/Iconos/prev-page-down.png" QUESTION_FONT_SIZE = "16" OPTIONS_FONT_SIZE = "12" GRADO_FONT_SIZE = "10" def draw_rounded(cr, area, radius): """ draws rectangles with rounded (circular arc) corners """ a,b,c,d = area cr.arc(a + radius, c + radius, radius, 2*(pi/2), 3*(pi/2)) cr.arc(b - radius, c + radius, radius, 3*(pi/2), 4*(pi/2)) cr.arc(b - radius, d - radius, radius, 0*(pi/2), 1*(pi/2)) cr.arc(a + radius, d - radius, radius, 1*(pi/2), 2*(pi/2)) cr.close_path() class Container2(Gtk.VBox): def do_draw(self, cr): top_allocation = self.get_toplevel().get_allocation() top_level_height = top_allocation.height x,y,w,h = (0, 0, top_allocation.width, top_level_height) h = self.get_parent().size_request().height if h < top_level_height: h = top_level_height cr.set_source_rgb(55/255.0, 55/255.0, 55/255.0) cr.rectangle(x, y, w, h) cr.fill_preserve() cr.stroke() scrollbar = self.get_parent().get_parent().get_vscrollbar() scrollbar_w = scrollbar.size_request().width scrollbar_h = scrollbar.size_request().height cr.set_source_rgb(126/255.0, 173/255.0, 69/255.0) draw_rounded(cr, (8, w - scrollbar_w - 16 - 25, 25, h - 8 - 25), 20) cr.fill_preserve() cr.stroke() cr.set_source_rgb(209/255.0, 232/255.0, 197/255.0) draw_rounded(cr, (58 + 3, w - scrollbar_w - 16, 25 + 35, h - 8), 20) cr.fill_preserve() Gtk.VBox.do_draw(self, cr) class Panel(Gtk.Paned): __gsignals__ = { "new":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT, GObject.TYPE_PYOBJECT, GObject.TYPE_PYOBJECT, GObject.TYPE_PYOBJECT, GObject.TYPE_PYOBJECT)), "new-selection":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT,)), "text_and_change":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT, GObject.TYPE_PYOBJECT, GObject.TYPE_PYOBJECT, GObject.TYPE_PYOBJECT))} def __init__(self): Gtk.Paned.__init__(self, orientation = Gtk.Orientation.HORIZONTAL) self.encuestados = None # mantiene csv para guardarlo como tal self.lista = None # Lista() Lista de encuestados self.encuesta = None # Dict Encuesta self.encuesta_tipo = None # Tipo de encuesta (general o normal) # Izquierda box = Gtk.VBox() self.scroll_list = Gtk.ScrolledWindow() self.scroll_list.set_policy( Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER) box.pack_start(self.scroll_list, True, True, 0) box.set_size_request(200, -1) self.pack1(box, resize = False, shrink = True) # Derecha self._window = Gtk.ScrolledWindow(vadjustment=Gtk.Adjustment()) self._window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self._window.set_shadow_type(Gtk.ShadowType.NONE) # Make scrolleable main box questions container, one group container = Container2() self.box_encuesta = Gtk.VBox() # Group title halign = Gtk.Alignment() halign.set(0.92, 0, 0, 0) halign.set_padding(25, 0, 0, 0) self.current_group_label = Gtk.Label("") self.current_group_label.modify_font(Pango.FontDescription(GRADO_FONT_SIZE)) halign.add(self.current_group_label) container.pack_start(halign, False, False, 0) container.pack_start(self.box_encuesta, True, True, 0) self.toolbar_encuesta = ToolbarEncuesta() container.pack_end(self.toolbar_encuesta, False, False, 30) self._window.add_with_viewport(container) self.pack2(self._window, resize = True, shrink = True) self.show_all() self.toolbar_encuesta.connect("accion", self.__accion_encuesta) def __check_sensibility_butons(self): """ Habilita y deshabilita los botones para recorrer los grupos de preguntas. """ index_visible = 0 grupos = self.box_encuesta.get_children() for child in grupos: if child.get_visible(): index_visible = grupos.index(child) break if index_visible == 0: self.toolbar_encuesta.anterior.hide() else: self.toolbar_encuesta.anterior.show_now() if index_visible == grupos.index(grupos[-1]): self.toolbar_encuesta.siguiente.hide() else: self.toolbar_encuesta.siguiente.show_now() self.toolbar_encuesta.siguiente.emit("update_status", self) def current_group(self): grupos = self.box_encuesta.get_children() for child in grupos: if child.get_visible(): index_visible = grupos.index(child) break return grupos[index_visible] def visible_questions(self): visible_questions = [] current_group = self.current_group() for question in current_group.box_preguntas: if question.get_visible(): visible_questions.append(question) return visible_questions def incomplete_questions(self): without_answer = [] for question in self.visible_questions(): active_options = question.widget_obtions.get_active_options() # TODO: Revisar get_active_options para que funcione con cualquier # tipo de widget y no diferenciar cuando es TextInput if not len(active_options): if question.pregunta.get("widget_type", None) == "TextInput": if not question.widget_obtions.get_text().strip(): without_answer.append(question) else: without_answer.append(question) return without_answer def __accion_encuesta(self, widget, accion): """ Pasa la vista de un grupo de preguntas a otro según valor de accion. """ index_visible = 0 grupos = self.box_encuesta.get_children() for child in grupos: if child.get_visible(): index_visible = grupos.index(child) break if accion == "": if index_visible > 0: map(self.__hide_groups, grupos) map(self.__show_groups, [grupos[index_visible - 1]]) self._window.get_vadjustment().set_value(0) elif accion == "Siguiente" and self.toolbar_encuesta.siguiente.active: if index_visible < len(grupos)-1: map(self.__hide_groups, grupos) map(self.__show_groups, [grupos[index_visible + 1]]) self._window.get_vadjustment().set_value(0) self.__check_sensibility_butons() def load_encuestados(self, encuestados): """ Carga una lista a encuestar. """ if not encuestados: return self.show() self.scroll_list.show_all() if self.lista: self.lista.destroy() self.encuestados = encuestados if not "NOMBRE" in self.encuestados[0]: ### Encuesta de tipo General. self.encuesta_tipo = "general" id = "ID" in self.encuestados[0] if not id: self.encuestados[0].append("ID") for encuestado in self.encuestados[1:]: encuestado.append(str(0)) self.lista = GeneralWidget(self.encuestados) self.lista.connect("new_encuestado", self.__new_encuestado) else: ### Encuesta de tipo normal self.encuesta_tipo = "normal" self.lista = Lista(self.encuestados) self.lista.connect("new-selection", self.__new_selection) self.scroll_list.add_with_viewport(self.lista) self.__check_sensitive() def load_encuesta(self, encuesta): """ Carga una nueva Encuesta eliminando la anterior. Encuesta es un diccionario. """ if not encuesta: return self.show() self.box_encuesta.show_all() self.encuesta = encuesta.copy() # Limpiar la interfaz. for child in self.box_encuesta.get_children(): child.destroy() # un grupo es un frame grupos = self.encuesta.keys() for indice in range(0, len(grupos)): # indice es el indice del grupo, Encuesta[indice] es el contenido # cada grupo es un diccionario con dos keys (name y fields) # name es el nombre del grupo == text del frame. fields son las preguntas grupo = Grupo(str(indice), self.encuesta[str(indice)]) self.box_encuesta.pack_start(grupo, False, True, 5) grupo.connect("new", self.__change) grupo.connect("text_and_change", self.__emit_text_and_change) self.__check_sensitive() grupos = self.box_encuesta.get_children() map(self.__hide_groups, grupos) map(self.__show_groups, [grupos[0]]) self.__check_sensibility_butons() def __emit_text_and_change(self, widget_grupo, pregunta, id_opcion, text, activan): """ Cuando el usuario ingresa algún valor en una entrada de texto, se manda guardar los datos y chequear las dependencias. """ encuestado = self.__get_encuestado() dict_pregunta = dict(pregunta.pregunta.copy()) if activan: dict_pregunta["default"] = list(activan) # list(pregunta.widget_obtions.default) if text: dict_pregunta["options"][id_opcion]["newtext"] = text self.emit("text_and_change", encuestado, widget_grupo, pregunta, dict_pregunta) self.__set_chequeo_dependencias(widget_grupo, pregunta) def __change(self, widget_grupo, pregunta, activan): """ Cuando se contesta una pregunta, se manda guardar los datos y chequear las dependencias. """ encuestado = self.__get_encuestado() grupo_name = widget_grupo.grupo["name"] dict_pregunta = pregunta.pregunta.copy() if activan: dict_pregunta["default"] = list(activan) # list(pregunta.widget_obtions.default) indice_grupo = widget_grupo.indice indice_pregunta = pregunta.indice ### Guarda la respuesta en archivo de salida self.emit("new", encuestado, indice_grupo, grupo_name, indice_pregunta, dict_pregunta) self.__set_chequeo_dependencias(widget_grupo, pregunta) def __set_chequeo_dependencias(self, widget_grupo, pregunta): """ Chequear dependencias para todas las preguntas posteriores a la que ha enviado la señal de cambio en ella. """ ### Obtener todas las opciones activas opciones_activas = [] grupos = self.box_encuesta.get_children() # grupos de preguntas for grupo in grupos: preguntas = grupo.box_preguntas.get_children() for preg in preguntas: opciones_activas = opciones_activas + preg.widget_obtions.get_active_options() almacenar = False # flag preguntas_a_checkear = [] # preguntas posteriores a la modificada ### Obtener preguntas posteriores a la que ha lanzado "new". for grupo in grupos[grupos.index(widget_grupo):]: for preg in grupo.box_preguntas.get_children(): if almacenar: preguntas_a_checkear.append(preg) if preg == pregunta: # preguntas posteriores a la que ha lanzado "new" almacenar = True self.__chequear_dependencias(opciones_activas, preguntas_a_checkear) def __chequear_dependencias(self, opciones_activas, preguntas_a_checkear): """ Chequeo de dependencias propiamente dicho. """ preguntas_para_activar = [] preguntas_para_desactivar = [] for pregunta in preguntas_a_checkear: dependencia = pregunta.pregunta.get("dependence", False) ### Solo si tienen dependencias if dependencia: valor, forma = G.convert(dependencia, opciones_activas) ### Si las dependencias de esta pregunta se cumplen if G.evaluar(valor, forma): preguntas_para_activar.append(pregunta) ### Si las dependencias no se cumplen else: preguntas_para_desactivar.append(pregunta) else: preguntas_para_activar.append(pregunta) map(self.__activar, preguntas_para_activar) map(self.__desactivar, preguntas_para_desactivar) """ A continuación: Resetear la 1º pregunta desactivada, lo cual ejecutará recurrencia sobre todo el sistema de dependencias. Esto es necesario para evitar que quede contestada una pregunta inactiva. Analisis del caso: Se contesta una opción en una 1ª pregunta que activa a una 2ª pregunta. Se contesta la 2ª pregunta. Luego se "descontesta" la opcíon de la 1ª pregunta que ha activado a esta 2ª pregunta. La 2ª pregunta será desactivada, pero quedará contestada, a menos que se la resetee. """ if preguntas_para_desactivar: GObject.idle_add(preguntas_para_desactivar[0].reset) def __get_encuestado(self): """ Devuelve lista con datos del encuestado seleccionado. """ modelo, iter = self.lista.get_selected_item() encuestado = [] for index in range(0, self.lista.get_num_columns()): encuestado.append(modelo.get_value(iter, index)) return encuestado def __new_selection(self, widget, encuestado): """ Cuando el usuario cambia de Encuestado. """ self.emit("new-selection", encuestado) def __show_groups(self, widget): widget.set_visible(True) def __hide_groups(self, widget): widget.set_visible(False) def __activar(self, widget): """ Recibe una pregunta. Cuando se cumplen las dependencias para esta pregunta, la misma se hace visible para de esa forma, permitir ser contestada. """ widget.set_visible(True) def __desactivar(self, widget): """ Recibe una pregunta. Cuando no se cumplen las dependencias para esta pregunta, la misma se hace invisible. """ widget.set_visible(False) def __check_sensitive(self): """ Habilita y deshabilita - Visibiliza e invisibiliza widgets y funcionalidad general según condiciones. """ if not self.encuesta: self.box_encuesta.hide() elif self.encuesta: self.box_encuesta.show_all() if not self.lista: self.scroll_list.hide() elif self.lista: self.scroll_list.show_all() if self.encuesta and self.lista: self.set_sensitive(True) else: self.set_sensitive(False) if self.encuesta_tipo == "general": self.get_children()[0].hide() elif self.encuesta_tipo == "normal": self.get_children()[0].show_all() def show_lista(self): """ Muestra la lista en encuestas de tipo general. """ self.get_children()[0].show() def __new_encuestado(self, widget, encuestado): """ Cuando se crea un nuevo encuestado, este debe agregarse a la lista de encuestados del panel. """ self.encuestados.append(encuestado) def update(self, dict): """ Cuando se selecciona un usuario, se actualiza la interfaz gráfica de la encuesta según respuestas del usuario. (dict == {}, actualiza la interfaz de una encuesta sin responder). """ self.load_encuesta(self.encuesta) grupos = self.box_encuesta.get_children() # grupos de preguntas ### Actualiza Respuestas. for grupo in grupos: if not grupo.indice in dict.keys(): continue else: grupo.update(dict[grupo.indice]) ### Obtener todas las opciones activas opciones_activas = [] grupos = self.box_encuesta.get_children() # grupos de preguntas for grupo in grupos: preguntas = grupo.box_preguntas.get_children() for preg in preguntas: opciones_activas = opciones_activas + preg.widget_obtions.get_active_options() ### Actualiza Visibilidad. preguntas_para_activar = [] preguntas_para_desactivar = [] for grupo in grupos: preguntas = grupo.box_preguntas.get_children() for preg in preguntas: contestada = preg.widget_obtions.get_active_options() con_dependencias = preg.pregunta.get("dependence", False) if contestada or not con_dependencias: preguntas_para_activar.append(preg) elif not contestada and con_dependencias: dependencia = preg.pregunta.get("dependence", False) ### Solo si tienen dependencias valor, forma = G.convert(dependencia, opciones_activas) ### Si las dependencias de esta pregunta se cumplen if G.evaluar(valor, forma): preguntas_para_activar.append(preg) ### Si las dependencias no se cumplen else: preguntas_para_desactivar.append(preg) map(self.__activar, preguntas_para_activar) map(self.__desactivar, preguntas_para_desactivar) GObject.idle_add(self.check_text_inputs) def check_text_inputs(self): """ Forzar autocontestado en opciones de entrada de texto con valor por defecto . . . """ grupos = self.box_encuesta.get_children() # grupos de preguntas for grupo in grupos: preguntas = grupo.box_preguntas.get_children() for pregunta in preguntas: widget_options = pregunta.widget_obtions if widget_options.nombre == "TextInput": widget_options.check_text_inputs() class My_FileChooser(Gtk.FileChooserDialog): """ Selector de Archivos para poder cargar archivos desde cualquier dispositivo o directorio. """ __gsignals__ = { "load":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT,))} def __init__(self, parent_window = None, action_type = None, filter_type = None, title = None): Gtk.FileChooserDialog.__init__(self, parent = parent_window, action = action_type, flags = Gtk.DialogFlags.MODAL, title = title) self.set_size_request( 640, 480 ) self.set_select_multiple(False) self.set_current_folder_uri("file:///%s" % WORKPATH) if filter_type != None: filter = Gtk.FileFilter() filter.set_name(filter_type) filter.add_mime_type(filter_type) self.add_filter(filter) hbox = Gtk.Box(orientation = Gtk.Orientation.HORIZONTAL) texto = "" if action_type == Gtk.FileChooserAction.OPEN: texto = "Abrir" elif action_type == Gtk.FileChooserAction.SAVE: texto = "Guardar" elif action_type == Gtk.FileChooserAction.SELECT_FOLDER: texto = "Elegir ubicacion" abrir = Gtk.Button(texto) salir = Gtk.Button("Salir") hbox.pack_end(salir, False, False, 5) hbox.pack_end(abrir, False, False, 5) self.set_extra_widget(hbox) salir.connect("clicked", self.__salir) abrir.connect("clicked", self.__abrir) self.show_all() def __abrir(self, widget): if self.get_action() == Gtk.FileChooserAction.SELECT_FOLDER: self.emit("load", self.get_current_folder()) else: self.emit("load", self.get_filename()) self.__salir(None) def __salir(self, widget): self.destroy() class Lista(Gtk.TreeView): """ Lista Dinámica de encuestados para encuestas de tipo normal. """ __gsignals__ = { "new-selection":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT, ))} def __init__(self, encuestados): encabezado = encuestados[0] encuestados = encuestados[1:] Gtk.TreeView.__init__(self) self.set_property("rules-hint", True) self.set_headers_clickable(True) self.set_headers_visible(True) self.valor_select = None strings = [] for x in encabezado: strings.append(GObject.TYPE_STRING) self.modelo = Gtk.ListStore(*strings) for item in encabezado: visible = False if item == "CI" or item == "NOMBRE" or item == "ID": visible = True self.append_column( self.__construir_columa( item, encabezado.index(item), visible)) self.treeselection = self.get_selection() self.treeselection.set_select_function( self.__selecciones, self.modelo) self.set_model(self.modelo) self.show_all() self.add_encuestados(encuestados) def __selecciones(self, treeselection, model, path, is_selected, listore): """ Cuando se selecciona un item en la lista. """ iter = model.get_iter(path) encuestado = "" for index in range(0, len(self.get_columns())): encuestado += "%s " % model.get_value(iter, index) encuestado = encuestado.strip() if not is_selected and self.valor_select != encuestado: self.valor_select = encuestado self.emit("new-selection", self.valor_select) return True def __construir_columa(self, text, index, visible): render = Gtk.CellRendererText() columna = Gtk.TreeViewColumn(text, render, text=index) columna.set_sort_column_id(index) columna.set_property("visible", visible) columna.set_property("resizable", False) columna.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) return columna def add_encuestados(self, encuestados): """ Recibe lista de: encuestados y Comienza secuencia de agregado. """ GObject.idle_add(self.__run_add_encuestado, encuestados) def __run_add_encuestado(self, encuestados): """ Agrega los items a la lista, uno a uno, actualizando. """ if not encuestados: self.seleccionar_ultimo() return encuestado = encuestados[0] self.modelo.append( encuestado ) encuestados.remove(encuestado) GObject.idle_add(self.__run_add_encuestado, encuestados) def __seleccionar_primero(self, widget = None): """ Selecciona el primer elemento en la lista. """ self.treeselection.select_path(0) def get_selected_item(self): """ Devuelve modelo de la lista e iter seleccionado. """ modelo, iter = self.treeselection.get_selected() return (modelo, iter) def get_num_columns(self): """ Devuelve la cantidad de columnas de la lista. """ return len(self.get_columns()) def seleccionar_ultimo(self): """ Selecciona el último elemento en la lista. """ model = self.get_model() item = model.get_iter_first() iter = None while item: iter = item item = model.iter_next(item) if iter: self.treeselection.select_iter(iter) path = model.get_path(iter) self.scroll_to_cell(path) class Grupo(Gtk.Frame): """ Frame para grupo de Preguntas de la encuesta. Un grupo es: "0": { "name": "group 0", "fields": { . . . => Preguntas} } """ __gsignals__ = { "new":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT, GObject.TYPE_PYOBJECT)), "text_and_change":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_PYOBJECT))} def __init__(self, indice_grupo, grupo): Gtk.Frame.__init__(self) self.indice = indice_grupo # indice del grupo en la encuesta self.grupo = grupo.copy() # Diccionario del grupo en la encuesta self.set_shadow_type(Gtk.ShadowType.NONE) self.box_preguntas = Gtk.VBox() self.add(self.box_preguntas) preguntas = self.grupo["fields"] keys = preguntas.keys() keys.sort() for indice in keys: pregunta = Pregunta(indice, preguntas[indice]) self.box_preguntas.pack_start(pregunta, True, False, 3) pregunta.connect("new", self.__change) pregunta.connect("text_and_change", self.__emit_text_and_change) self.show_all() def __emit_update_button(self): panel = self.get_toplevel().panel siguiente = panel.toolbar_encuesta.siguiente siguiente.emit("update_status", panel) def __emit_text_and_change(self, pregunta, id_opcion, text, activan): """ Cuando el usuario ingresa algún valor en una entrada de texto, se manda guardar los datos y chequear las dependencias. """ self.__emit_update_button() self.emit("text_and_change", pregunta, id_opcion, text, activan) def __change(self, widget, activan): """ Cuando se contesta una pregunta, chequea las dependencias y se guardan los datos. """ self.__emit_update_button() self.emit("new", widget, activan) def update(self, dict): """ Cuando se selecciona un usuario que ha contestado al menos parte de la encuesta, se actualiza la interfaz gráfica de la misma con esos valores. """ self.__emit_update_button() for child in self.box_preguntas.get_children(): # Si se respondió esta pregunta if dict["fields"].get(child.indice, False): child.update(dict["fields"][child.indice]) class Pregunta(Gtk.VBox): """ Box con Pregunta. Una pregunta es: { "0": { "widget_type": "RadioButton", "name": "¿El Alumno se Encuentra en Clase?", "options":{ "00001": {"text": "Si"}, "00002": {"text": "No"}, }, "dependence": "" } } """ __gsignals__ = { "new":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE,(GObject.TYPE_PYOBJECT,)), "text_and_change":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE,(GObject.TYPE_STRING, GObject.TYPE_STRING,GObject.TYPE_PYOBJECT))} def __str__(self): return "pos: %s, pregunta: %s" % (self.indice, self.pregunta["name"]) def __init__(self, indice_pregunta, pregunta): Gtk.VBox.__init__(self) self.indice = indice_pregunta self.pregunta = pregunta.copy() # CEIBAL ICON PIXBUF # TODO: def question_icon(self, scaled_size) self.question_icon = GdkPixbuf.Pixbuf.new_from_file(ICON) self.widget_obtions = None widget = self.pregunta["widget_type"] if widget == "DropDownList": self.widget_obtions = Widget_DropDownList(self.pregunta["options"].copy()) self.widget_obtions.connect("new", self.__change) elif widget == "TextInput": self.widget_obtions = Widget_TextInput(self.pregunta["options"].copy()) self.widget_obtions.connect("text_and_change", self.__emit_text_and_change) elif widget == "MultipleCheckBox" or widget == "ImageCheckBox": self.widget_obtions = Widget_MultipleCheckBox(self.pregunta["options"].copy()) self.widget_obtions.connect("new", self.__change) elif widget == "RadioButton" or widget == "ImageRadioButton": self.widget_obtions = Widget_RadioButon(self.pregunta["options"].copy()) self.widget_obtions.connect("new", self.__change) else: print "Widget no Considerado", widget label = Gtk.Label(self.pregunta["name"]) pangoFont = Pango.FontDescription(QUESTION_FONT_SIZE) label.modify_font(pangoFont) label.set_line_wrap(True) #label.set_size_request(900, -1) # --- QUESTION TITLE INDENT: horizontal alignment halign = Gtk.Alignment() halign.set(0, 0, 1, 0) halign.set_padding(50, 0, 50, 0) # CEIBAL ICON | Question title question_hbox = Gtk.HBox() question_icon = Gtk.Image() scaled_qicon = self.question_icon.scale_simple( 60, 60, GdkPixbuf.InterpType.BILINEAR) question_icon.set_from_pixbuf(scaled_qicon) question_hbox.pack_start(question_icon, False, False, 10) title = label question_hbox.pack_start(title, False, False, 25) halign.add(question_hbox) self.pack_start(halign, False, False, 0) # --- END QUESTION TITLE INDENT # --- OPTIONS INDENT: horizontal alignment for question's options halign = Gtk.Alignment() halign.set(0.2, 0.1, 0, 0) # Vertical list of question's options vbbox = Gtk.VButtonBox() vbbox.add(self.widget_obtions) halign.add(vbbox) self.pack_end(halign, False, False, 0) # -- END OPTIONS INDENT self.show_all() def __emit_text_and_change(self, widget, id_opcion, text, activan): """ Cuando el usuario ingresa algún valor en una entrada de texto, se manda guardar los datos y chequear las dependencias. """ self.emit("text_and_change", id_opcion, text, activan) def __change(self, widget, activan): """ Cuando se Contesta una pregunta, se chequean las dependencia y se guardan los datos. """ self.emit("new", activan) def update(self, dict): """ Cuando se selecciona un usuario que ha contestado al menos parte de la encuesta, se actualiza la interfaz gráfica de la misma con esos valores. """ self.widget_obtions.update(dict) def reset(self): """ Devolver la pregunta a su estado original. Descontestar cualquier respuesta ingresada. """ self.widget_obtions.reset() class Widget_DropDownList(Gtk.ComboBoxText): """ Contenedor de opciones para respuestas posibles en una pregunta. Opciones es: "options": { "00001": {"text": "opcion 1"}, "00002": {"text": "opcion 2"} } """ __gsignals__ = { "new":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT,))} def __init__(self, options): Gtk.ComboBoxText.__init__(self) self.nombre = "DropDownList" self.options = options.copy() self.updating = False for key in self.options.keys(): self.append_text(self.options[key]["text"]) self.show_all() def get_active_options(self): """ Devuelve los indices de las opciones activas. """ activas = [] for key in self.options.keys(): if self.options[key]["text"] == self.get_active_text(): if not key in activas: activas.append(key) elif not self.options[key]["text"] == self.get_active_text(): if key in activas: activas.remove(key) return activas def do_changed(self): """ Cuando el usuario hace click sobre una opción, se checkean dependencias y se guardan los datos. """ if self.updating: return activan = [] for key in self.options.keys(): if self.options[key]["text"] == self.get_active_text(): if not key in activan: activan.append(key) elif not self.options[key]["text"] == self.get_active_text(): if key in activan: activan.remove(key) self.emit("new", activan) def update(self, dict): """ Cuando el usuario cambia de encuestado, actualiza sus respuestas en la pregunta. """ self.updating = True model = self.get_model() item = model.get_iter_first() count = 0 for default in dict.get("default", []): while item: if model.get_value(item, 0) == \ dict["options"][default]["text"]: self.set_active(count) self.updating = False return item = model.iter_next(item) count += 1 self.updating = False def reset(self): """ La pregunta vuelve a su estado original. """ self.remove_all() activan = [] for key in self.options.keys(): self.append_text(self.options[key]["text"]) ### Porque se debe avisar que esta pregunta se ha desactivado. self.emit("new", activan) class Widget_TextInput(Gtk.Entry): """ Contenedor para entrada de texto para respuesta a una pregunta. Opciones es: "options":{ "00001":{"text": ""} } """ __gsignals__ = { "text_and_change":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE,(GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_PYOBJECT))} def __init__(self, options): Gtk.Entry.__init__(self) font_description = Pango.FontDescription(OPTIONS_FONT_SIZE) self.modify_font(font_description) self.set_size_request(int(OPTIONS_FONT_SIZE)*30, -1) self.nombre = "TextInput" self.options = options.copy() self.updating = False key = self.options.keys()[0] text = self.options[key].get("text", False) if text: self.set_text(text) self.show_all() def do_key_release_event(self, void): """ Cuando el usuario ingresa algún valor en la opción, se manda guardar los datos y chequear las dependencias. """ if self.updating: return activan = [] key = self.options.keys()[0] text = self.get_text() if text: activan.append(key) else: return self.reset() self.emit("text_and_change", key, text, activan) def get_active_options(self): """ Devuelve los indices de las opciones activas. """ activas = [] key = self.options.keys()[0] text = self.options[key].get("text", False) if text: activas.append(key) return activas def update(self, dict): """ Cuando el usuario cambia de encuestado, actualiza sus respuestas en la pregunta. """ self.updating = True key = self.options.keys()[0] if dict["options"][key].get("newtext", False): self.set_text(dict["options"][key]["newtext"]) else: text = self.options[key].get("text", "") self.set_text(text) self.updating = False def reset(self): """ La pregunta vuelve a su estado original. Si esta opción tenían un valor por defecto, luego de ser reseteada, ese valor ya no se tomará en cuenta. """ key = self.options.keys()[0] text = " " self.set_text("") activan = [] ### Porque se debe avisar que esta pregunta se ha desactivado. self.emit("text_and_change", key, text, activan) def check_text_inputs(self): """ Cuando una opcion del tipo entrada de texto trae un valor por defecto, forzamos el autocontestado de esta pregunta para que este valor se guarde en la salida ya que de lo contrario, solo se guardará si el usuario contesta en forma explicita esta opción, modificando el valor por defecto. """ key = self.options.keys()[0] if not self.options[key].get("newtext", False) and self.options[key].get("text", False): activan = [] text = self.options[key]["text"] activan.append(key) self.emit("text_and_change", key, text, activan) class Widget_RadioButon(Gtk.VButtonBox): """ Contenedor de opciones para respuestas posibles en una pregunta. Opciones es: "options": { "00001": {"text": "opcion 1"}, "00002": {"text": "opcion 2"} } """ __gsignals__ = { "new":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT,))} def __init__(self, options): Gtk.VButtonBox.__init__(self) self.nombre = "RadioButton" # Grupo de radio buttons. Necesario para pregunta sin contestar. self.null_button = Gtk.RadioButton() self.options = options.copy() self.updating = False grupo = None keys = self.options.keys() keys.sort() for key in keys: radio = MyRadioButton(key) text = self.options[key].get("text", "") imagen = self.options[key].get("img", "") if imagen: loader = GdkPixbuf.PixbufLoader() image_string = base64.b64decode(imagen) loader.write(image_string) loader.close() pixbuf = loader.get_pixbuf().scale_simple( 80, 80, GdkPixbuf.InterpType.BILINEAR) radio.set_image(Gtk.Image.new_from_pixbuf(pixbuf)) radio.join_group(self.null_button) r = MyContainer(radio, text) self.pack_start(r, False, False, 3) self.null_button.set_active(True) radio.connect("toggled", self.__change) self.show_all() def get_active_options(self): """ Devuelve los indices de las opciones activas. """ activas = [] options = self.get_children() for child in options: child = child.get_button() if child.get_active(): activas.append(child.indice) return activas def __change(self, widget): """ Cuando el usuario hace click sobre una opción, se checkean dependencias y se guardan los datos. """ if self.updating: return options = self.get_children() activan = [] for child in options: child = child.get_button() if child.get_active(): activan.append(child.indice) self.emit("new", activan) def update(self, dict): """ Cuando el usuario cambia de encuestado, actualiza sus respuestas en la pregunta. """ self.updating = True options = self.get_children() for child in options: child = child.get_button() if child.indice in dict.get("default", []): child.set_active(True) else: child.set_active(False) self.updating = False def reset(self): """ La pregunta vuelve a su estado original. """ # FIXME: Esto funciona bien en la persistencia # pero no actualiza gráficamente, debido a que siempre debe # haber un item seleccionado en un grupo de radio buttons. options = self.get_children() activan = [] for child in options: child = child.get_button() child.set_active(False) self.null_button.set_active(True) ### Porque se debe avisar que esta pregunta se ha desactivado. self.emit("new", activan) class Widget_MultipleCheckBox(Gtk.VButtonBox): """ Contenedor de opciones para respuestas posibles en una pregunta. Opciones es: "options": { "00001": {"text": "opcion 1"}, "00002": {"text": "opcion 2"} } """ __gsignals__ = { "new":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT,))} def __init__(self, options): self.nombre = "MultipleCheckBox" Gtk.VButtonBox.__init__(self) self.options = options.copy() self.updating = False keys = self.options.keys() keys.sort() for key in keys: check = MyCheckButton(key) text = self.options[key].get("text", None) imagen = self.options[key].get("img", "") if imagen: loader = GdkPixbuf.PixbufLoader() image_string = base64.b64decode(imagen) loader.write(image_string) loader.close() pixbuf = loader.get_pixbuf().scale_simple( 80, 80, GdkPixbuf.InterpType.BILINEAR) check.set_image(Gtk.Image.new_from_pixbuf(pixbuf)) c = MyContainer(check, text) self.pack_start(c, False, False, 3) check.connect("toggled", self.__change) self.show_all() def get_active_options(self): """ Devuelve los indices de las opciones activas. """ activas = [] options = self.get_children() for child in options: child = child.get_button() if child.get_active(): activas.append(child.indice) return activas def __change(self, widget): """ Cuando el usuario hace click sobre una opción, se checkean dependencias y se guardan los datos. """ if self.updating: return options = self.get_children() activan = [] for child in options: child = child.get_button() if child.get_active(): activan.append(child.indice) self.emit("new", activan) def update(self, dict): """ Cuando el usuario cambia de encuestado, actualiza sus respuestas en la pregunta. """ self.updating = True options = self.get_children() for child in options: child = child.get_button() if child.indice in dict.get("default", []): child.set_active(True) else: child.set_active(False) self.updating = False def reset(self): """ La pregunta vuelve a su estado original. """ options = self.get_children() activan = [] for child in options: child = child.get_button() child.set_active(False) ### Porque se debe avisar que esta pregunta se ha desactivado. self.emit("new", activan) class MyContainer(Gtk.HBox): def __init__(self, button, label): Gtk.HBox.__init__(self) self.button = button self.text_label = label self.label = None if self.text_label: self.label = Gtk.Label(label) self.label.modify_font(Pango.FontDescription(OPTIONS_FONT_SIZE)) align = Gtk.Alignment() align.add(self.button) self.pack_start(align, False, False, 0) if self.label: align = Gtk.Alignment() align.set_padding(0, 0, 20, 0) align.add(self.label) self.pack_start(align, False, False, 0) self.show_all() def get_button(self): return self.button class MyCheckButton(Gtk.CheckButton): def __init__(self, indice): Gtk.CheckButton.__init__(self) self.indice = indice self.show_all() class MyRadioButton(Gtk.RadioButton): def __init__(self, indice): Gtk.RadioButton.__init__(self) self.indice = indice self.show_all() class My_Alert_Dialog(Gtk.Dialog): """ Dialogo que alerta al usuario sobre una acción que obliga a perder los datos. """ def __init__(self, parent_window = None, label = ""): Gtk.Dialog.__init__( self, title = "ATENCION !", parent = parent_window, flags = Gtk.DialogFlags.MODAL, buttons = ["OK", Gtk.ResponseType.ACCEPT]) label = Gtk.Label(label) label.show() self.set_border_width(10) self.vbox.pack_start(label, True, True, 0) class ToolbarEncuesta(Gtk.HBox): __gsignals__ = { "accion":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING,))} def __init__(self): Gtk.HBox.__init__(self) self.anterior = PageButton( "", img=PREV_PAGE_ACTIVE, imgs={"enter": PREV_PAGE_OVER, "down": PREV_PAGE_DOWN} ) self.anterior.set_tooltip_text(u"Ir a la Página de Preguntas Anterior") self.anterior.connect("pressed", self.__button_clicked) self.siguiente = PageButton( "Siguiente", img=NEXT_PAGE_ACTIVE, imgs={"enter": NEXT_PAGE_OVER, "down": NEXT_PAGE_DOWN} ) self.siguiente.set_tooltip_text(u"Ir a la Siguiente Página de Preguntas") self.siguiente.connect("pressed", self.__button_clicked) # DOWN BUTTONS: < prev next > # TODO: self.__draw_pagination_buttons(self, parent_widget=vbox) halign_prev = Gtk.Alignment() halign_prev.set_padding(0, 0, 5, 0) halign_prev.add(self.anterior) halign_next = Gtk.Alignment() halign_next.set(0.85, 0, 0, 0) halign_next.add(self.siguiente) self.pack_start(halign_prev, True, True, 0) self.pack_end(halign_next, True, True, 0) #vbox.pack_start(hbox, False, False, 10) # -- END DOWN BUTTONS self.show_all() def __button_clicked(self, widget): self.emit("accion", widget.accion) panel = self.get_toplevel().panel if len(panel.visible_questions()) == 0: self.__button_clicked(widget) class PageButton(Gtk.Button): __gsignals__ = { "enter-notify-event" : "override", "update_status":( GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT, ) ) } def __init__(self, label, img, imgs, *args, **kwargs): Gtk.Button.__init__(self, None, *args, **kwargs) self.label = None self.accion = label self.active = True self.imgs = { "normal": img, "leave": img, } self.img_normal = Gtk.Image() img_normal_pixbuf = GdkPixbuf.Pixbuf.new_from_file(img) img_normal_scaled = img_normal_pixbuf.scale_simple( 45, 45, GdkPixbuf.InterpType.BILINEAR) self.img_normal.set_from_pixbuf(img_normal_scaled) box = Gtk.HBox() if label: self.label = Gtk.Label(label) self.label.set_padding(10, 0) self.label.modify_fg(0, Gdk.color_parse("#B40404")) box.pack_start(self.label, False, False, 0) box.pack_start(self.img_normal, False, False, 0) self.add(box) self.set_relief(Gtk.ReliefStyle.NONE) self.imgs.update(imgs) self.connect("activate", self.__button_event, "down") self.connect("enter", self.__button_event, "enter") self.connect("leave", self.__button_event, "leave") self.connect("update_status", self.__update_status) def __update_status(self, widget, panel): new_img = NEXT_PAGE_ACTIVE incomplete_questions = panel.incomplete_questions() color = "#B40404" if len(incomplete_questions): new_img = NEXT_PAGE_INACTIVE color = "#7EAD45" self.active = False else: self.active = True img = Gtk.Image() img_pixbuf = GdkPixbuf.Pixbuf.new_from_file(new_img) img_scaled = img_pixbuf.scale_simple( 45, 45, GdkPixbuf.InterpType.BILINEAR) self.img_normal.set_from_pixbuf(img_scaled) if self.label: self.label.modify_fg(0, Gdk.color_parse(color)) def do_enter_notify_event(self, widget): self.emit("enter") def __button_event(self, widget, img_file=None): if not self.active: return if img_file is None: return img = Gtk.Image() img_pixbuf = GdkPixbuf.Pixbuf.new_from_file(self.imgs[img_file]) img_scaled = img_pixbuf.scale_simple( 45, 45, GdkPixbuf.InterpType.BILINEAR) self.img_normal.set_from_pixbuf(img_scaled) class InfoWidget(Gtk.EventBox): """ Widget con información sobre encuesta en proceso. """ def __init__(self): Gtk.EventBox.__init__(self) self.toolbar = Gtk.Toolbar(orientation = Gtk.Orientation.HORIZONTAL) self.info = {} self.poll_name = Gtk.Label("Encuesta: ") self.pollster_username = Gtk.Label("Encuestador: ") self.encuestado = Gtk.Label("Encuestado: ") self.poll_name.show() self.pollster_username.show() self.encuestado.show() separador = Gtk.SeparatorToolItem() separador.props.draw = False separador.set_size_request(5, -1) separador.set_expand(False) self.toolbar.insert(separador, -1) item = Gtk.ToolItem() item.set_expand(False) item.add(self.poll_name) self.toolbar.insert(item, -1) separador = Gtk.SeparatorToolItem() separador.props.draw = False separador.set_size_request(5, -1) separador.set_expand(False) self.toolbar.insert(separador, -1) item = Gtk.ToolItem() item.set_expand(False) item.add(self.pollster_username) self.toolbar.insert(item, -1) separador = Gtk.SeparatorToolItem() separador.props.draw = False separador.set_size_request(5, -1) separador.set_expand(False) self.toolbar.insert(separador, -1) item = Gtk.ToolItem() item.set_expand(False) item.add(self.encuestado) self.toolbar.insert(item, -1) separador = Gtk.SeparatorToolItem() separador.props.draw = False separador.set_size_request(0, -1) separador.set_expand(True) self.toolbar.insert(separador, -1) self.add(self.toolbar) self.show_all() def set_encuestado(self, encuestado): self.encuestado.set_text("Encuestado: %s" % encuestado) def set_info(self, dict): """ Ejemplo: dict = { "poll_name": "Encuesta de Prueba", "poll_id": "111111111111111111111111", "pollster_id": "5163fe24421aa9263991b229", "pollster_username": "José Antonio Rodriguez"} """ for key in dict.keys(): self.info[key] = dict[key] self.poll_name.set_text("Encuesta: %s" % self.info["poll_name"]) self.pollster_username.set_text("Encuestador: %s" % self.info["pollster_username"]) self.show_all() def get_info(self): return self.info class GeneralWidget(Gtk.Box): """ Lista Dinámica de encuestados, para encuestas de tipo general. """ __gsignals__ = { "new-selection":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT,)), "new_encuestado":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT, ))} def __init__(self, encuestados): Gtk.Box.__init__(self, orientation = Gtk.Orientation.VERTICAL) self.lista = None self.listas = {} encabezado = encuestados[0] encuestados = encuestados[1:] grupos = [] id_grado = encabezado.index("GRADO") id_grupo = encabezado.index("GRUPO") ### Obteniendo los grupos en la lista. for encuestado in encuestados: grado = encuestado[id_grado] grupo = encuestado[id_grupo] item = "%s %s" % (grado, grupo) if not item in grupos: grupos.append(item) self.listas[item] = [encabezado] self.listas[item].append(encuestado) self.combo = ComboGrupos() self.pack_start(self.combo, False, False, 0) for g in grupos: self.combo.append_text(g) self.scroll_list = Gtk.ScrolledWindow() self.scroll_list.set_policy( Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) # self.scroll_list.add_with_viewport(self.lista) self.pack_start(self.scroll_list, True, True, 0) box = Gtk.Box(orientation = Gtk.Orientation.VERTICAL) continuar = Gtk.Button("Continuar") siguiente = Gtk.Button("Siguiente") box.pack_start(continuar, True, True, 0) box.pack_start(siguiente, True, True, 0) self.pack_start(box, False, False, 0) self.show_all() self.combo.connect("new", self.__new_group) self.combo.select(grupos[0]) self.__new_group(None, grupos[0]) # Forzar nueva lista continuar.connect("clicked", self.__continuar) siguiente.connect("clicked", self.__siguiente) def __siguiente(self, widget): """ Obtiene el item seleccionado en la lista. Si hay más delante de éste, pasa al siguiente. De lo contrario crear uno nuevo y lo selecciona. """ msg = "¿Esta seguro que desea terminar esta encuesta y seguir con una nueva?" dialog = My_Alert_Dialog2( parent_window = self.get_toplevel(), label = msg) response = dialog.run() if Gtk.ResponseType(response) != Gtk.ResponseType.ACCEPT: dialog.destroy() return dialog.destroy() modelo, iter = self.get_selected_item() if not modelo.iter_next(iter): item = [] for x in range(0, self.get_num_columns()): valor = modelo.get_value(iter, x) item.append(str(valor)) item[-1] = str(int(item[-1]) + 1) ### Agregar a la lista de este grupo columnas = self.lista.get_columns() for columna in columnas: cabecera = columna.get_title() if cabecera == "GRADO": id_grado = columnas.index(columna) if cabecera == "GRUPO": id_grupo = columnas.index(columna) grado = modelo.get_value(iter, id_grado) grupo = modelo.get_value(iter, id_grupo) valor = "%s %s" % (grado, grupo) self.listas[valor].append(item) ### Agregar a la lista de este grupo self.lista.add_encuestados([item]) self.emit("new_encuestado", item) while Gtk.events_pending(): Gtk.main_iteration() self.lista.treeselection.select_iter(modelo.iter_next(iter)) # FIXME: Ver si se puede insensibilizar la linea. # Probablemente no se pueda y haya que usar filtros. # modelo[iter] http://faq.pygtk.org/index.py?file=faq13.048.htp&req=show def __new_group(self, widget, valor): if self.lista: self.lista.destroy() self.lista = Lista(self.listas[valor]) self.lista.set_headers_clickable(False) self.lista.connect("new-selection", self.__new_selection) self.scroll_list.add_with_viewport(self.lista) def __continuar(self, widget): self.get_toplevel().menu_bar.hide() self.get_toplevel().infowidget.hide() self.get_parent().get_parent().get_parent().hide() def __new_selection(self, widget, encuestado): """ Cuando el usuario cambia de Encuestado. """ self.emit("new-selection", encuestado) self.scroll_list.hide() self.get_toplevel().menu_bar.hide() self.get_toplevel().infowidget.hide() def get_selected_item(self): """ Devuelve modelo de la lista e iter seleccionado. """ return self.lista.get_selected_item() def get_num_columns(self): """ Devuelve la cantidad de columnas de la lista. """ return self.lista.get_num_columns() class ComboGrupos(Gtk.ComboBoxText): __gsignals__ = { "new":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING,))} def __init__(self): Gtk.ComboBoxText.__init__(self) self.updating = False self.show_all() def get_current_group(self): count = 0 model = self.get_model() item = model.get_iter_first() while item: if count == self.get_active(): return model.get_value(item, 0) item = model.iter_next(item) count += 1 def select(self, valor): """ Setea el combo a un valor específico. """ self.updating = True model = self.get_model() item = model.get_iter_first() count = 0 while item: if model.get_value(item, 0) == str(valor): self.set_active(count) self.updating = False return item = model.iter_next(item) count += 1 self.updating = False def do_changed(self): """ Cuando el usuario hace click sobre una opción. """ if self.updating: return self.emit("new", self.get_active_text()) class Wizard(Gtk.Dialog): def __init__(self, parent_window = None): Gtk.Dialog.__init__( self, title = "Asistente", parent = parent_window, flags = Gtk.DialogFlags.MODAL, buttons = [ "Proceder", Gtk.ResponseType.ACCEPT, "Salir", Gtk.ResponseType.CANCEL, ] ) self.set_border_width(5) abrirencuesta = Gtk.Button("Buscar . . .") abrirlista = Gtk.Button("Buscar . . .") cargarencuesta = Gtk.Button("Buscar . . .") abrirencuesta.connect("clicked", self.__abrir_encuesta) abrirlista.connect("clicked", self.__abrir_lista) cargarencuesta.connect("clicked", self.__cargar_encuesta) self.label_1 = Gtk.Label("") self.label_2 = Gtk.Label("") self.label_3 = Gtk.Label("") frame0 = Gtk.Frame() box0 = Gtk.Box(orientation = Gtk.Orientation.VERTICAL) frame0.add(box0) frame = Gtk.Frame() frame.set_label("Seleccionar modelo de encuesta. (archivo *.json)") box = Gtk.Box(orientation = Gtk.Orientation.HORIZONTAL) box.pack_start(abrirencuesta, False, False, 10) box.pack_start(self.label_1, False, False, 0) frame.add(box) frame.show_all() box0.pack_start(frame, False, False, 10) frame = Gtk.Frame() frame.set_label("Seleccionar lista a encuestar. (archivo *.csv)") box = Gtk.Box(orientation = Gtk.Orientation.HORIZONTAL) box.pack_start(abrirlista, False, False, 10) box.pack_start(self.label_2, False, False, 0) frame.add(box) frame.show_all() box0.pack_start(frame, False, False, 10) self.vbox.pack_start(Gtk.Label("Nuevas encuestas"), False, False, 2) self.vbox.pack_start(frame0, False, False, 30) frame0.show_all() frame = Gtk.Frame() frame.set_label("Seleccionar encuesta desde archivo temporal. (archivo *.encuesta)") box = Gtk.Box(orientation = Gtk.Orientation.HORIZONTAL) box.pack_start(cargarencuesta, False, False, 10) box.pack_start(self.label_3, False, False, 0) frame.add(box) frame.show_all() self.vbox.pack_start(Gtk.Label("Encuestas inconclusas"), False, False, 2) self.vbox.pack_start(frame, False, False, 10) self.set_border_width(5) self.set_size_request(640, 480) def __abrir_encuesta(self, widget): """ Recuperar encuesta parcial o totalmente respondida. """ filechooser = My_FileChooser( parent_window = self, action_type = Gtk.FileChooserAction.OPEN, filter_type = None, title = "Cargar Nueva Encuesta") filechooser.connect("load", self.__set_encuesta) def __abrir_lista(self, widget): """ Abrir lista a encuestar. """ filechooser = My_FileChooser( parent_window = self, action_type = Gtk.FileChooserAction.OPEN, filter_type = "text/csv", title = "Cargar Lista a Encuestar") filechooser.connect("load", self.__set_encuestados) def __cargar_encuesta(self, widget): """ Abrir encuesta vacía. """ filechooser = My_FileChooser( parent_window = self, action_type = Gtk.FileChooserAction.OPEN, #filter_type = "text/encuesta", title = "Recuperar Encuesta") filechooser.connect("load", self.__set_encuesta_respondida) def __set_encuesta(self, widget, archivo): """ Las encuestas respondidas son *.json o *.slv. """ extension = os.path.splitext(os.path.split(archivo)[1])[1] if "json" in extension or "slv" in extension: self.label_1.set_text(archivo) def __set_encuestados(self, widget, archivo): """ Las listas son *.csv. """ extension = os.path.splitext(os.path.split(archivo)[1])[1] if "csv" in extension: self.label_2.set_text(archivo) def __set_encuesta_respondida(self, widget, archivo): """ Las encuestas respondidas son *.encuesta. """ extension = os.path.splitext(os.path.split(archivo)[1])[1] if "encuesta" in extension: self.label_3.set_text(archivo) class Password_Dialog(Gtk.Dialog): """ Dialogo para introdicir password y acceder a opciones de Encuestador. """ def __init__(self, parent_window = None, title = "ATENCION !", label = ""): Gtk.Dialog.__init__( self, title = title, parent = parent_window, flags = Gtk.DialogFlags.MODAL, buttons = [ "Aceptar", Gtk.ResponseType.ACCEPT, "Cancelar", Gtk.ResponseType.CANCEL]) label = Gtk.Label(label) label.show() self.entry = Gtk.Entry() self.entry.set_visibility(False) self.set_border_width(10) self.entry.show() self.set_default_response(Gtk.ResponseType.ACCEPT) self.vbox.pack_start(label, True, True, 0) self.vbox.pack_start(self.entry, True, True, 0) def get_password(self): """ Deveulve el password introducido. """ return self.entry.get_text().strip() class My_Alert_Dialog2(Gtk.Dialog): def __init__(self, parent_window = None, label = ""): Gtk.Dialog.__init__( self, title = "ATENCION !", parent = parent_window, flags = Gtk.DialogFlags.MODAL, buttons = [ "Aceptar", Gtk.ResponseType.ACCEPT, "Cancelar", Gtk.ResponseType.CANCEL]) label = Gtk.Label(label) label.show() self.set_border_width(10) self.vbox.pack_start(label, True, True, 0) class My_Alert_Dialog3(Gtk.Dialog): def __init__(self, parent_window = None, label = ""): Gtk.Dialog.__init__( self, title="ATENCION !", parent=parent_window, flags=Gtk.DialogFlags.MODAL, buttons=("Volver al asistente", Gtk.ResponseType.CANCEL) ) self.set_default_response(Gtk.ResponseType.CANCEL) label = Gtk.Label(label) label.show() self.set_border_width(10) self.vbox.pack_start(label, True, True, 0)