class WaitFiscalAnswer(Dialog):#{{{ def __init__(self, filename, title=None, timeout=10, interval=0.1):#{{{ self.filename = filename self.interval = interval self.timeout = timeout if title is None: title = "IMPRIMIENDO" msg = Text("Esperando la respuesta de la Impresora Fiscal ...", align='left') self.eta = Text("", align='left') self.dummy = Text("") self.dummy._selectable = True self.dummy.keypress = lambda s,k : None self.buttons = Columns([ AttrMap(Button("Seguir esperando", on_press=self.remove_buttons),'dialog.button', 'dialog.button.focus'), AttrMap(Button("Cancelar Impresion", on_press=lambda *w: self.quit()), 'dialog.button', 'dialog.button.focus'), ], dividechars=1) self.content = Pile([ msg, Divider(), self.eta, self.dummy, ]) self.__super.__init__( self.content, title=title, attr_style='dialog.waitfiscal', title_attr_style='dialog.waitfiscal.title', height=None, width=60, ) #}}} def run(self):#{{{ self._start_time = datetime.now() self._last_option = time.time() return self.__super.run(alarm=(self.interval, self._alarm)) #}}} def _alarm(self, main_loop, user_data=None):#{{{ if not os.path.exists(self.filename): self.update_status() main_loop.set_alarm_in(self.interval, self._alarm) return else: self.dialog_result = True self.quit() #}}} def update_status(self):#{{{ self.eta.set_text("Tiempo: %s" % get_elapsed_time(self._start_time)) if type(self.content.widget_list[-1]) is Text: if (time.time() - self._last_option) > self.timeout: self.add_buttons() #}}} def add_buttons(self):#{{{ self.content.widget_list[-1] = self.buttons self.content.set_focus(self.buttons) #}}} def remove_buttons(self, *btn):#{{{ self._last_option = time.time() self.content.widget_list[-1] = self.dummy self.content.set_focus(self.dummy)
class LoginWindow(WidgetWrap): signals = ['login', 'logout'] def __init__(self, app, extra=None, get_user=None, max_time=30): self.app = app self.extra = extra self.get_user = get_user self.max_time = max_time self._create_widgets() self._out_count = 0 self._evt_time = 0 self._parent = None self._key_sig_id = None self._timeout_sig_id = None self.__super.__init__(self.login_widget) def _create_widgets(self): self._create_login_widget() if self.extra: widget = Frame(LineBox(self.login_widget), footer=self.extra) else: widget = LineBox(self.login_widget) self.overlay = Overlay( widget, None, 'center', ('relative', 100), 'middle', ('relative', 100), ) def _create_login_widget(self): self.username_entry = Edit(align='right') self.username_entry.keypress = self._username_keypress self.password_entry = Password(align='right') self.password_entry.keypress = self._password_keypress username_row = Columns([ ('fixed', 10, Text("Usuario:", align='right')), ('fixed', 10, self.username_entry), ]) password_row = Columns([ ('fixed', 10, Text("Clave:", align='right')), ('fixed', 10, self.password_entry), ]) self.pile = Pile([ username_row, Divider(), password_row, ], focus_item=0) self.login_widget = Filler(Columns([Divider(), self.pile, Divider()])) def show(self): """Show login window""" #self.pile.set_focus(0) self.clear() loop = self.app.loop self.overlay.bottom_w = loop.widget loop.widget = self.overlay if loop.screen.started: loop.draw_screen() def hide(self): """Hide login window""" loop = self.app.loop loop.widget = self.overlay.bottom_w if loop.screen.started: loop.draw_screen() def login(self, user): """ Login the session, showing all content and hidding login window. """ # connect esc-esc signal to logout widget = self.overlay.bottom_w widget.orig_keypress = widget.keypress widget.keypress = self._wrapped_keypress self._last_key_time = time.time() self._timeout_sig_id = self.app.loop.set_alarm_in(self.max_time+1, self._check_logout) if hasattr(widget, 'set_user') and callable(widget.set_user): widget.set_user(user) self.hide() self._emit("login") def logout(self): """Logout the session, hidding all content and showing login window again. """ # disconnect esc-esc signal self.app.loop.widget.keypress = self.app.loop.widget.orig_keypress self.app.loop.remove_alarm(self._timeout_sig_id) self.show() self._emit("logout") def clear(self): self.username_entry.set_edit_text("") self.password_entry.set_edit_text("") self.pile.set_focus(0) def _wrapped_keypress(self, size, key): self._last_key_time = time.time() if key == 'esc': if self._out_count == 1 and (time.time() - self._evt_time) < 1: self._out_count = 0 self._evt_time = 0 self.logout() else: self._out_count = 1 self._evt_time = time.time() return None else: return self.app.loop.widget.orig_keypress(size, key) def _username_keypress(self, size, key): if key == 'enter': key = 'down' return self.username_entry.__class__.keypress(self.username_entry, size, key) def _password_keypress(self, size, key): if key == 'enter': password = self.password_entry.get_edit_text() username = self.username_entry.get_edit_text() self.password_entry.set_edit_text("") if password and username: user = self.get_user(username, password) if user: #self.username_entry.set_edit_text("") self.login(user) return return self.password_entry.__class__.keypress(self.password_entry, size, key) def _check_logout(self, main_loop, user_data=None): etime = int(time.time() - self._last_key_time) if etime >= self.max_time: self.logout() else: main_loop.remove_alarm(self._timeout_sig_id) self._timeout_sig_id = main_loop.set_alarm_in(self.max_time-etime, self._check_logout) return False
class SelectClient(Dialog): def __init__(self, title=None, cls=None): if cls is None: cls = type('Dummy', (object,), {'__init__': lambda *a, **k: None, 'run': lambda *a: None}) self.cls = cls self._obj = None _edit_cancel = lambda *w: self.focus_button(1) self.codigo_box = MaeterCodeBox(max_length=12, align='right') connect_signal(self.codigo_box, 'focus-in', highlight_focus_in), connect_signal(self.codigo_box, 'edit-done', self.on_codigo_edit_done) connect_signal(self.codigo_box, 'edit-cancel', _edit_cancel) connect_signal(self.codigo_box, 'search-client', self.on_client_search) self.nombre = Text(u'') client_row = Columns([ ('fixed', 8, AttrMap(Text("Cliente", align='right'), 'dialog.selectdate.label')), ('fixed', 6, AttrMap(self.codigo_box, 'dialog.selectdate.input', 'dialog.selectdate.input.focus')), AttrMap(self.nombre, 'dialog.selectdate.label'), ], dividechars=1) self.content = Pile([ client_row, Divider(), ]) buttons = [("Continuar", self.run_list), ("Cancelar", self.quit)] self.__super.__init__(self.content, buttons, title=title or u'<Falta titulo>', height=None, width=60, attr_style='dialog.selectdate', title_attr_style='dialog.selectdate.title') def run_list(self, *args): if not self._obj: self._pile.set_focus(0) self.content.set_focus(0) return subdialog = self.cls(cliente=self._obj) self.dialog_result = subdialog.run() self.quit() def on_codigo_edit_done(self, widget, code): if code != u"": query = session.query(Cliente).filter(Cliente.codigo==int(code)) query = query.filter(Cliente.relacion==u"C") try: self._obj = query.one() self.codigo_box.set_edit_text(self._obj.codigo) self.nombre.set_text(self._obj.nombre + " - " + self._obj.direccion) self.focus_button(0) except NoResultFound: self._obj = None self.nombre.set_text(u"") def on_client_search(self, widget, search_by=None, first_key=None): response = search_terceros(search_by=search_by, first_key=first_key) if response: self.codigo_box.set_edit_text(str(response[0].codigo)) self.nombre.set_text(str(response[0].nombre)) self.on_codigo_edit_done(self.codigo_box, str(response[0].codigo)) return None
class TreePile(WidgetWrap): _selectable = True def __init__(self, walker, **kwargs): if not isinstance(walker, TreeListWalker): walker = TreeListWalker(walker) self._walker = walker self._lines = [] self.loadlines() logging.debug('lines:\n\n%s' % str(self._lines)) self._pile = Pile(self._lines) self.__super.__init__(self._pile) def loadlines(self): widget, pos = self._walker.get_focus() while pos is not None: self._lines.append(widget) widget, pos = self._walker.get_next(pos) # Widget API def get_focus(self): return self._pile.get_focus() def keypress(self, size, key): key = self._pile.keypress(size, key) if key in ['left', 'right', '[', ']', '-', '+', 'C', 'E']: if key == 'left': self.focus_parent() elif key == 'right': self.focus_first_child() elif key == '[': self.focus_prev_sibling() elif key == ']': self.focus_next_sibling() if isinstance(self._walker, CollapseMixin): if key == '-': w, focuspos = self._walker.get_focus() self._walker.collapse(focuspos) elif key == '+': w, focuspos = self._walker.get_focus() self._walker.expand(focuspos) elif key == 'C': self._walker.collapse_all() elif key == 'E': self._walker.expand_all() # This is a hack around ListBox misbehaving: # it seems impossible to set the focus without calling keypress as # otherwise the change becomes visible only after the next render() return self._pile.keypress(size, None) else: return self._pile.keypress(size, key) # Tree based focus movement def focus_parent(self): w, focuspos = self._walker.get_focus() parent = self._walker.parent_position(focuspos) if parent is not None: self._pile.set_focus(parent) def focus_first_child(self): w, focuspos = self._walker.get_focus() child = self._walker.first_child_position(focuspos) if child is not None: self._outer_list.set_focus(child) def focus_next_sibling(self): w, focuspos = self._walker.get_focus() sib = self._walker.next_sibling_position(focuspos) if sib is not None: self._outer_list.set_focus(sib) def focus_prev_sibling(self): w, focuspos = self._walker.get_focus() sib = self._walker.prev_sibling_position(focuspos) if sib is not None: self._outer_list.set_focus(sib)
class SelectArticlesDateRange(SelectDateRange):#{{{ def __init__(self, title=None, cls=None):#{{{ if cls is None: cls = type('Dummy', (object,), {'run': lambda *a: None}) self.cls = cls self._desde_err = None self._hasta_err = None _edit_cancel = lambda *w: self.focus_button(1) _edit_ok = lambda *w: self.focus_button(0) def _focus_hasta(*w): self.content.set_focus(2) self.articulos = InputBox() self.articulos.filter_input = lambda t: t.upper() connect_signal(self.articulos, "edit-done", self.on_articulos_edit_done) connect_signal(self.articulos, "edit-cancel", _edit_cancel) self.desde = DateSelectorBox(out_fmt="%d/%m/%Y") err = ('desde_error', '_desde_err') connect_signal(self.desde, 'focus-in', self.on_fecha_focus_in, err) connect_signal(self.desde, 'focus-out', self.on_fecha_focus_out, err) connect_signal(self.desde, 'edit-cancel', _edit_cancel) connect_signal(self.desde, 'edit-done', self.on_fecha_edit_done, err+(_focus_hasta,)) connect_signal(self.desde, 'bad-date-error', self.on_fecha_error, err) self.desde_error = Text("", wrap='clip') self.hasta = DateSelectorBox(out_fmt="%d/%m/%Y") err = ('hasta_error', '_hasta_err') connect_signal(self.hasta, 'focus-in', self.on_fecha_focus_in, err) connect_signal(self.hasta, 'focus-out', self.on_fecha_focus_out, err) connect_signal(self.hasta, 'edit-cancel', _edit_cancel) connect_signal(self.hasta, 'edit-done', self.on_fecha_edit_done, err+(_edit_ok,)) connect_signal(self.hasta, 'bad-date-error', self.on_fecha_error, err) self.hasta_error = Text("", wrap='clip') articulos_row = Columns([ ('fixed', 14, AttrMap(Text("Artículos", align='right'), 'dialog.selectdate.label')), AttrMap(self.articulos, 'dialog.selectdate.input', 'dialog.selectdate.input.focus'), ], dividechars=1) desde_row = Columns([ ('fixed', 14, AttrMap(Text("Desde", align='right'), 'dialog.selectdate.label')), ('fixed', 11, AttrMap(self.desde, 'dialog.selectdate.input', 'dialog.selectdate.input.focus')), AttrMap(self.desde_error, 'dialog.selectdate.error'), ], dividechars=1) hasta_row = Columns([ ('fixed', 14, AttrMap(Text("Hasta", align='right'), 'dialog.selectdate.label')), ('fixed', 11, AttrMap(self.hasta, 'dialog.selectdate.input', 'dialog.selectdate.input.focus')), AttrMap(self.hasta_error, 'dialog.selectdate.error'), ], dividechars=1) self.include_inactives = CheckBox(u"Incluir Desactivados", state=True) include_inactives_row = Columns([ ('fixed', 14, Divider()), AttrMap(self.include_inactives, 'dialog.selectdate.label'), ], dividechars=1) self.content = Pile([ articulos_row, desde_row, hasta_row, include_inactives_row, Divider(), ]) t = u"Artículos y Rango de Fechas" if title: t += u" - %s" % title # Set initial dates self.desde.set_value(date(2001, 01, 01)) # FIXME: hardcoded > Principio de actividad self.hasta.set_value(date.today()+relativedelta(day=31)) # Fin de mes buttons = [("Continuar", self.run_list), ("Cancelar", self._quit)] Dialog.__init__(self, self.content, buttons, title=t, height=None, width=60, attr_style='dialog.selectdate', title_attr_style='dialog.selectdate.title') #}}} def run_list(self, *args): art_list = self.get_articles() if not art_list: show_error([u"Los artículos ingresados no producen ningún resultado válido, ", u"ingrese ", ('dialog.warning.important', u"artículos"), " o ", ('dialog.warning.important', "grupos"), " correctos."]) self._pile.set_focus(0) self.content.set_focus(0) return subdialog = self.cls(start_date=self.desde.get_value(), end_date=self.hasta.get_value(), articles=art_list) self.dialog_result = subdialog.run() self.quit() def get_articles(self): arts = [] code_list = set([c.strip() for c in self.articulos.get_edit_text().split(",")]) agrup_list = [c[1:] for c in code_list if c.startswith('@')] code_list = code_list.difference(agrup_list) query = session.query(Articulo) if self.include_inactives.get_state() is False: query = query.filter(Articulo.es_activo==True) if code_list: art = query.filter(Articulo.codigo.in_(code_list)).all() order = dict([(v, i) for i, v in enumerate(code_list)]) art.sort(cmp=lambda a, b: cmp(order[a.codigo], order[b.codigo])) arts.extend(art) if agrup_list: art = query.filter(Articulo.agrupacion.in_(agrup_list)).order_by(Articulo.codigo).all() order = dict([(v, i) for i, v in enumerate(agrup_list)]) art.sort(cmp=lambda a, b: cmp(order[a.agrupacion], order[b.agrupacion])) arts.extend(art) return arts def on_articulos_edit_done(self, widget, text): self.content.set_focus(1)