def descadastrar_linhas(app, dados): actions = 0 reservas_changed = False for dados_linha, reservas in dados: linha = gerar_id_linha(*dados_linha[:2]) for reserva in reservas: estado.reservas.discard(reserva) app.reservas.delete(reserva) reservas_changed = True app.contador_reservas['text'] = fm.form_cont_reservas( len(estado.reservas)) t = minutos_dia(dados_linha.horario) del estado.linhas_entradas[linha] estado.linhas_possiveis.add((dados_linha.destino.title(), t)) estado.horarios_linhas[t].remove(linha) estado.linhas_visiveis.remove(linha) app.contador_linhas['text'] = fm.form_cont_linhas( len(estado.linhas_visiveis)) onibus_filhos = dados_linha.onibus.keys() monitoradas.onibus_visiveis -= onibus_filhos estado.onibus_invisiveis -= onibus_filhos app.contador_onibus['text'] = fm.form_cont_onibus( len(monitoradas.onibus_visiveis)) app.linhas.delete(linha) actions += 1 if not reservas_changed: app.abas.select(app.aba_linhas) return actions
def reservar(app, indice, reserva): ''' Atualiza o estado da aplicação para incluir a reserva especificada no argumento. ''' # lembrando que reserva é um wrapper (veja csv_to_reserva) e reserva.reserva é o id da reserva estado.reservas.add(reserva.reserva) # o wrapper nos dá as informações que precisamos campus = reserva.campus.title() partida = reserva.partida.strftime('%d/%m/%y %H:%M') assento = reserva.assento passagem = '%8s: R$%s' % (cte.INDICE_PASSAGEM[reserva.passagem].title( ), it.inteira_termo(reserva.inteira, reserva.passagem)) # adicionamos a reserva à interface gráfica app.reservas.insert(parent='', index=indice, values=(campus, partida, assento, passagem), iid=reserva.reserva) onibus = reserva.onibus linha = reserva.linha estado.linhas_entradas[linha].onibus[onibus][reserva.assento] = reserva.passagem # vamos atualizar o valor da coluna "assentos livres" do ônibus associado à reserva prev_values = app.linhas.set(onibus) prev_values['assentos livres'] = int(prev_values['assentos livres']) - 1 # colocamos foco e selecionamos o ônibus para o qual foi realizada a reserva app.linhas.see(onibus) app.linhas.selection_add(onibus) app.linhas.item(onibus, values=tuple(prev_values.values())) app.contador_reservas['text'] = fm.form_cont_reservas(len(estado.reservas))
def devolver_reservas(app, reservas): actions = 0 app.linhas.selection_set(*[]) for _, reserva in reservas: estado.reservas.discard(reserva) app.reservas.delete(reserva) onibus = reserva[:-6] linha = onibus[:-7] if onibus in estado.linhas_entradas[linha].onibus: del estado.linhas_entradas[linha].onibus[onibus][ decodificar_id_reserva(reserva).assento] prev_values = app.linhas.set(onibus) prev_values['assentos livres'] = int( prev_values['assentos livres']) + 1 app.linhas.see(onibus) app.linhas.selection_add(onibus) app.linhas.item(onibus, values=tuple(prev_values.values())) actions += 1 app.contador_reservas['text'] = fm.form_cont_reservas(len(estado.reservas)) return actions
def rereservar_reservas(app, reservas): actions = 0 app.reservas.selection_set(*[]) app.linhas.selection_set(*[]) for indice, reserva in reservas: estado.reservas.add(reserva) app.reservas.insert(parent='', index=indice, values=campos_reserva_formatados(reserva), iid=reserva) app.reservas.selection_add(reserva) onibus = reserva[:-6] linha = onibus[:-7] if onibus in estado.linhas_entradas[linha].onibus: info_reserva = decodificar_id_reserva(reserva) assento, passagem = info_reserva.assento, info_reserva.passagem.tipo estado.linhas_entradas[linha].onibus[onibus][assento] = passagem prev_values = app.linhas.set(onibus) prev_values['assentos livres'] = int( prev_values['assentos livres']) - 1 app.linhas.see(onibus) app.linhas.selection_add(onibus) app.linhas.item(onibus, values=tuple(prev_values.values())) actions += 1 app.contador_reservas['text'] = fm.form_cont_reservas(len(estado.reservas)) return actions
def devolver(app): ''' Remove as reservas selecionadas, devolvendo as vagas. ''' selecao = app.reservas.selection() if len(selecao) == 0: texto = 'Selecione reservas cujos ônibus ainda não partiram.' messagebox.showwarning(title='Não Há O Que Devolver', message=texto) return devolviveis = [ reserva for reserva in selecao if app.linhas.exists(reserva[:-6]) ] if len(devolviveis) < len(selecao): if len(devolviveis) == 0: texto = 'Ônibus já partiram. Impossível devolver.' messagebox.showwarning(title='Impossível Devolver', message=texto) return else: texto = 'Os ônibus de algumas reservas já partiram, deseja devolver as demais?' resposta = messagebox.askyesno(title='ônibus Partiram', message=texto, default='yes') if not resposta: return reservas = [] app.linhas.selection_set(*[]) for reserva in devolviveis: reservas.insert(0, [app.reservas.index(reserva), reserva]) estado.reservas.discard(reserva) app.reservas.delete(reserva) onibus = reserva[:-6] linha = onibus[:-7] del estado.linhas_entradas[linha].onibus[onibus][ decodificar_id_reserva(reserva).assento] prev_values = app.linhas.set(onibus) prev_values['assentos livres'] = int( prev_values['assentos livres']) + 1 app.linhas.see(onibus) app.linhas.selection_add(onibus) app.linhas.item(onibus, values=tuple(prev_values.values())) app.contador_reservas['text'] = fm.form_cont_reservas(len(estado.reservas)) monitoradas.historico_redo.clear() monitoradas.historico_undo.append(['refund', reservas])
def finalizar(): ''' Realiza a reserva. Chamado quando o usuário clica no botão Reservar. ''' nonlocal selected assento = int(selected['text']) if len(estado.linhas_entradas[linha].onibus[onibus]) == 0: shift = (sum(coordenadas_assento(assento)) + 1) % 2 assentos_invisiveis = { assento_coordenadas(i, 2 * j + (i + shift) % 2) for j in range(2) for i in range(num_fileiras) } for assento_indisponivel in assentos_invisiveis: labels[assento_indisponivel].grid_remove() tipo = passagem.get() estado.linhas_entradas[linha].onibus[onibus][assento] = tipo reserva = gerar_id_reserva(onibus, assento, tipo) estado.reservas.add(reserva) # insere essa reserva na aba de reservas app.reservas.insert(parent='', index=0, values=campos_reserva_formatados(reserva), iid=reserva) prev_values = linhas.set(onibus) prev_values['assentos livres'] = int( prev_values['assentos livres']) - 1 linhas.item(onibus, values=tuple(prev_values.values())) labels[assento]['cursor'] = 'X_cursor' labels[assento]['style'] = estilo[tipo] labels[assento].unbind('<Button-1>') selected = ttk.Label(painel_assentos, text='None') historico_undo = monitoradas.historico_undo historico_redo = monitoradas.historico_redo historico_redo.clear() historico_undo.append(['book', reserva]) button_state() app.contador_reservas['text'] = fm.form_cont_reservas( len(estado.reservas)) ctrl.update_action(app, 'book')
def povoar_treeviews(app): ''' Inicializa a interface gráfica com os dados persistidos. ''' onibus_invisiveis = set() for linha in estado.ordem_inicial_linhas: onibus_invisiveis.update(reinsert_linha(app, linha)) estado.ordem_inicial_linhas = tuple() for linha in estado.linhas_entradas.keys() - estado.linhas_visiveis: onibus_invisiveis.update(reinsert_linha(app, linha, False)) for reserva in estado.ordem_inicial_reservas: app.reservas.insert(parent='', index=0, values=campos_reserva_formatados(reserva), iid=reserva) estado.onibus_invisiveis = onibus_invisiveis app.contador_linhas['text'] = fm.form_cont_linhas( len(estado.linhas_visiveis)) app.contador_onibus['text'] = fm.form_cont_onibus( len(monitoradas.onibus_visiveis)) app.contador_reservas['text'] = fm.form_cont_reservas(len(estado.reservas)) print('Dados persistentes carregados.')
def gerar(event=None): ''' Gera as linhas conforme a quantidade indicada pelo spin_linhas. É executado quando o usuário clica no botão de gerar, ou aperta ENTER. ''' if not atualizar_num(): # aqui, atualizar_num retornou False, o que significa que a entrada é inválida messagebox.showwarning(title='Entrada Inválida', message='Valor inválido!', parent=configuracoes) return from random import choice, randrange, sample ocupar = monitoradas.ocupar_assentos.get() # Uma sequência em que o valor assento_coordenadas(i, j) indica o número do assento na linha i e coluna j. # Note que para cada linha, há 4 colunas (índice j varia de 0 a 3). # As colunas 0 e 3 são adjacentes à janela e as colunas 1 e 2 são adjacentes ao corredor. # Essa sequência pode ser vista ao tentar reservar um assento. def assento_coordenadas(i, j): return 2*i + j + \ 1 if j <= 1 else 2*(num_fileiras + i + 2) - j dados = [] data_atual = monitoradas.data_atual minutos_atual = data_atual.hour*60 + data_atual.minute # termo(monitoradas.indice_linhas_geradas) é o número de linhas indicado pelo spin_linhas # note que minutes[:termo(monitoradas.indice_linhas_geradas)] seleciona # justamente a quantidade de linhas que queremos! # como minutes foi permutado aleatoriamente, os horários em minutes escolhidos # pelo slicing são aleatórios e diferentes entre si for destino, t in sample(estado.linhas_possiveis, termo(monitoradas.indice_linhas_geradas)): estado.linhas_possiveis.discard((destino, t)) # t é uma quantidade de minutos da lista minutes, que usaremos para calcular o horário dessa linha que # iremos cadastrar! interval = dt.timedelta(minutes=t) horario = data_atual.replace(hour=0, minute=0) + interval if data_atual > horario: horario += dt.timedelta(1) linha = gerar_id_linha(destino, horario) indice_vagas = choice(range(cte.MAXIMO_NUMERO_DE_FILEIRAS)) num_fileiras = indice_vagas + 1 vagas = vg.vagas_termo(indice_vagas) indice_inteira = choice(range(301)) inteira = it.inteira_termo(indice_inteira) estado.horarios_linhas[t] = estado.horarios_linhas.get( t, set()).union({linha}) app.linhas.insert(parent='', index=0, values=(destino, fm.form_tempo( horario), vagas, inteira), iid=linha) estado.linhas_entradas[linha] = cte.ESTRUTURA_LINHA( destino, horario, num_fileiras, indice_inteira, dict()) estado.linhas_visiveis.add(linha) reservas = list() # calculamos o período de dias em que haverá ônibus partindo if t == minutos_atual: periodo = range(cte.MAXIMO_NUMERO_DE_DIAS_ATE_RESERVA + 1) elif t < minutos_atual: # aqui, o horário da linha é menor que o horário atual, # então não vamos criar um ônibus para o dia de hoje, pois ele já partiu periodo = range(1, cte.MAXIMO_NUMERO_DE_DIAS_ATE_RESERVA + 1) else: # aqui, o horário da linha é maior que o horário atual, # então vamos incluir o dia de hoje periodo = range(cte.MAXIMO_NUMERO_DE_DIAS_ATE_RESERVA) for d in periodo: # para cada dia no período calculado, vamos adicionar um ônibus # note que d = 0 representa o dia de hoje, d = 1 é amanhã, etc. partida = data_atual + dt.timedelta(d) onibus = gerar_id_onibus(linha, partida) assentos_disponiveis = randrange(vagas+1) if ocupar else vagas app.linhas.insert(parent=linha, index='end', values=( '-', fm.form_data(partida), assentos_disponiveis, '-'), iid=onibus) estado.linhas_entradas[linha].onibus[onibus] = dict() if assentos_disponiveis < vagas: # nesse caso, significa que o usuário pediu para "Ocupar assentos" # e o valor aleatorio assentos disponíveis é menor que a quantidade de vagas # então, devemos fazer algumas reservas aleatórias! # primeiro, vamos escolher quais assentos serão escolhíveis # (afinal, metade dos assentos serão disponíveis, então vamos escolher qual metade) shift = choice(range(2)) # os assentos escolhíveis respeitam as condições impostas pela pandemia # basicamente, selecionamos (i, (i + shift) % 2) e (i, 2 + (i + shift) % 2) para todo i # por exemplo, # i = 0, shift = 0 -> selecionamos (0, 0) e (0, 2) # i = 1, shift = 0 -> selecionamos (1, 1) e (1, 3) # i = 2, shift = 0 -> selecionamos (2, 0) e (2, 2) # Note que a primeira posição é um assento da seção da esquerda # e a segunda posição é um assento da seção da direita # e selecionamos dois assentos por linha assentos_visiveis = {assento_coordenadas(i, 2 * j + (i + shift) % 2) for j in range(2) for i in range(num_fileiras)} # a função sample seleciona aleatoriamente números de assentos escolhíveis para # fazermos reserva for assento in sample(assentos_visiveis, vagas - assentos_disponiveis): # a variável assento é um número de assento escolhível aleatório # devemos reservar esse assento! passagem = choice([2]*3+[1]*2+[0]) estado.linhas_entradas[linha].onibus[onibus][assento] = passagem reserva = gerar_id_reserva(onibus, assento, passagem) estado.reservas.add(reserva) reservas.insert(0, reserva) app.reservas.insert(parent='', index=0, values=campos_reserva_formatados( reserva), iid=reserva) monitoradas.onibus_visiveis.add(onibus) dados.append([estado.linhas_entradas[linha], reservas]) ctrl.update_action(app, 'add') historico_undo = monitoradas.historico_undo historico_redo = monitoradas.historico_redo historico_redo.clear() historico_undo.append(['add', dados]) app.contador_linhas['text'] = fm.form_cont_linhas( len(estado.linhas_visiveis)) app.contador_onibus['text'] = fm.form_cont_onibus( len(monitoradas.onibus_visiveis)) app.contador_reservas['text'] = fm.form_cont_reservas( len(estado.reservas)) configuracoes.destroy()
def recadastrar_linhas(app, dados): actions = 0 reservas_changed = False app.linhas.selection_set(*[]) app.reservas.selection_set(*[]) for dados_linha, reservas in dados: linha = gerar_id_linha(*dados_linha[:2]) estado.linhas_entradas[linha] = cte.ESTRUTURA_LINHA( *dados_linha[:-1], dict()) for reserva in reservas: estado.reservas.add(reserva) app.reservas.insert(parent='', index=0, values=campos_reserva_formatados(reserva), iid=reserva) reservas_changed = True app.contador_reservas['text'] = fm.form_cont_reservas( len(estado.reservas)) t = minutos_dia(dados_linha.horario) estado.linhas_possiveis.remove((dados_linha.destino.title(), t)) estado.horarios_linhas[t] = estado.horarios_linhas.get(t, set()).union( {linha}) estado.linhas_visiveis.add(linha) app.contador_linhas['text'] = fm.form_cont_linhas( len(estado.linhas_visiveis)) app.linhas.insert(parent='', index=0, values=campos_linha_formatados(dados_linha), iid=linha) data_atual = monitoradas.data_atual.replace(second=0, microsecond=0) minutos_atual = data_atual.hour * 60 + data_atual.minute if t == minutos_atual: periodo = range(cte.MAXIMO_NUMERO_DE_DIAS_ATE_RESERVA + 1) elif t < minutos_atual: periodo = range(1, cte.MAXIMO_NUMERO_DE_DIAS_ATE_RESERVA + 1) else: periodo = range(cte.MAXIMO_NUMERO_DE_DIAS_ATE_RESERVA) for d in periodo: partida = (data_atual + dt.timedelta(d)).replace( hour=dados_linha.horario.hour, minute=dados_linha.horario.minute) onibus = gerar_id_onibus(linha, partida) assentos = dados_linha.onibus.get(onibus, dict()) app.linhas.insert( parent=linha, index='end', values=('-', fm.form_data(partida), 2 * int(dados_linha.fileiras) - len(assentos), '-'), iid=onibus) monitoradas.onibus_visiveis.add(onibus) app.contador_onibus['text'] = fm.form_cont_onibus( len(monitoradas.onibus_visiveis)) estado.linhas_entradas[linha].onibus[onibus] = assentos app.linhas.selection_add(linha) app.reservas.selection_add(*reservas) actions += 1 if not reservas_changed: app.abas.select(app.aba_linhas) return actions
def __init__(self, root): ''' Inicializa a interface gráfica e armazena as instâncias dos componentes nesse objeto (self). O argumento root é uma instância de Tk, a janela principal da aplicação. O manager grid é utilizado para o posicionamento dos componentes, sendo especificado posições de linhas e colunas para os filhos de um componente. ::: self.component.grid(row=1, column=2, sticky='n') significa que component será posicionado na LINHA 1 e COLUNA 2 em relação ao seu master e alinhado ao NORTE ('n'), ou seja, o componente "gruda" no topo. sticky='ns' significa que o componente "gruda" no NORTE e no SUL, se esticando verticalmente. Outros sticky possuem significado com lógica similar. ::: self.component.columnconfigure(0, weight=1) ::: self.component.columnconfigure(1, weight=1) ::: self.component.columnconfigure(2, weight=1) significa que os filhos de component nas colunas 0, 1 e 2 possuem o mesmo PESO, ou seja, ocupam a mesma quantidade de espaço ::: self.component.bind('<Evento>', funcao) significa que o event handler "funcao" será associado (bind) ao evento <Evento>. assim, sempre que <Evento> for recebido por component, funcao irá executar ''' root.minsize(*cte.TAMANHO_DA_JANELA_PRINCIPAL ) # tamanho mínimo da janela principal # título, aparece na porção superior do programa root.title(cte.TITULO) # permitimos o acesso por self.root (ou app.root, fora dessa função) self.root = root # ícone self.estilo = ttk.Style() self.estilo.configure('inteira.TLabel', background='#e30000', foreground='#fff') self.estilo.configure('meia.TLabel', background='#eb7100', foreground='#fff') self.estilo.configure('gratuita.TLabel', background='#095f9c', foreground='#fff') self.estilo.configure('available.TLabel', background='#07de00', foreground='#fff') self.estilo.configure('selected.TLabel', background='#fff', foreground='#005') self.frame = ttk.Frame() self.frame.pack(fill='both', expand=True) # crio um objeto Notebook em root (janela principal), que permite a divisão de uma janela em abas self.abas = ttk.Notebook(self.frame) # a primeira aba, onde o usuário poderá manipular as entradas de linhas de ônibus self.aba_linhas = ttk.Frame(self.abas, padding=12, name='aba_linhas') self.aba_linhas.pack( ) # peço ao módulo para exibir a aba usando o método pack self.linhas = ttk.Treeview(self.aba_linhas, columns=cte.COLUNAS_LINHAS, selectmode='extended', name='linhas') self.linhas.column('#0', width=20, stretch=0) for i, coluna in enumerate(cte.COLUNAS_LINHAS): self.linhas.heading( i, text=coluna.title(), command=lambda col=i: util.treeview_sort_column( self, self.linhas, col, False)) self.linhas.column(i, width=0, minwidth=len(coluna) * 15, anchor='center') self.linhas.bind('<Motion>', lambda ev: util.last_separator(ev, self.linhas)) self.linhas.bind('<1>', lambda ev: util.last_separator(ev, self.linhas)) self.linhas_scroller_v = ttk.Scrollbar(self.aba_linhas, orient='vertical', command=self.linhas.yview) self.linhas_scroller_h = ttk.Scrollbar(self.aba_linhas, orient='horizontal', command=self.linhas.xview) self.linhas['yscrollcommand'] = self.linhas_scroller_v.set self.linhas['xscrollcommand'] = self.linhas_scroller_h.set # gridding - aba_linhas self.linhas.grid(row=0, column=0, sticky='nsew') self.linhas_scroller_v.grid(row=0, column=1, sticky='ens') self.linhas_scroller_h.grid(row=1, column=0, sticky='sew') self.aba_reservas = ttk.Frame(self.abas, padding=12, name='aba_reservas') self.aba_reservas.pack() self.reservas = ttk.Treeview(self.aba_reservas, columns=cte.COLUNAS_RESERVAS, show='headings', selectmode='extended', name='reservas') for i, coluna in enumerate(cte.COLUNAS_RESERVAS): self.reservas.heading( i, text=coluna.title(), command=lambda col=i: util.treeview_sort_column( self, self.reservas, col, False)) self.reservas.column(i, width=0, minwidth=len(coluna) * 15, anchor='center') self.reservas.bind('<Motion>', lambda ev: util.last_separator(ev, self.reservas)) self.reservas.bind('<1>', lambda ev: util.last_separator(ev, self.reservas)) self.reservas_scroller_v = ttk.Scrollbar(self.aba_reservas, orient='vertical', command=self.reservas.yview) self.reservas_scroller_h = ttk.Scrollbar(self.aba_reservas, orient='horizontal', command=self.reservas.xview) self.reservas['yscrollcommand'] = self.reservas_scroller_v.set self.reservas['xscrollcommand'] = self.reservas_scroller_h.set # gridding - aba_reservas self.reservas.grid(row=0, column=0, sticky='nsew') self.reservas_scroller_v.grid(row=0, column=1, sticky='ens') self.reservas_scroller_h.grid(row=1, column=0, sticky='new') self.painel_botoes_reservas = ttk.Frame(self.aba_reservas) self.painel_botoes_reservas.grid(row=2, column=0, columnspan=1, sticky='nesw', pady=(12, 6)) self.botao_devolver = ttk.Button(self.painel_botoes_reservas, command=lambda: util.devolver(self), text='Devolver') self.botao_devolver.grid(row=0, column=0, sticky='we') self.botao_importar_reservas = ttk.Button( self.painel_botoes_reservas, command=lambda: arq.importar_reservas(self), text='Importar') self.botao_importar_reservas.grid(row=0, column=1, sticky='we') self.botao_exportar_reservas = ttk.Button( self.painel_botoes_reservas, command=lambda: arq.exportar_reservas(self), text='Exportar') self.botao_exportar_reservas.grid(row=0, column=2, sticky='we') for i in range(3): self.painel_botoes_reservas.columnconfigure(i, weight=1) self.painel_entrada = ttk.Frame(self.aba_linhas) self.painel_entrada.columnconfigure(0, weight=1) self.painel_entrada.columnconfigure(1, weight=1) self.painel_entrada.columnconfigure(2, weight=1) self.painel_entrada.columnconfigure(3, weight=1) self.painel_destino = ttk.Frame(self.painel_entrada) self.painel_destino.grid(row=0, column=0, sticky='w', padx=0) self.painel_partida = ttk.Frame(self.painel_entrada) self.painel_partida.grid(row=0, column=1, sticky='we', padx=0) self.entrada_destino = ttk.Combobox(self.painel_destino, justify='center', textvariable=monitoradas.destino, values=estado.destinos, width=15) self.entrada_destino.bind('<FocusOut>', dest.atualizar_destino) self.entrada_destino.bind('<Return>', dest.validar_destino) self.entrada_destino.grid(row=0, column=0, sticky='ew', padx=0) self.botao_add_destino = ttk.Button( self.painel_destino, command=lambda: dest.add_destino(self), text='+', width=2) self.botao_add_destino.grid(row=0, column=2, sticky='w') self.botao_rmv_destino = ttk.Button( self.painel_destino, command=lambda: dest.rmv_destino(self), text='-', width=2) self.botao_rmv_destino.grid(row=0, column=1, sticky='w') self.spin_hora = Spinbox(self.painel_entrada, textvariable=monitoradas.tempo_formatado, justify='center', width=6) self.spin_hora.bind('<<Increment>>', lambda e: date.increment(self, cte.MIN)) self.spin_hora.bind('<<Decrement>>', lambda e: date.decrement(self, cte.MIN)) self.spin_hora.bind('<Return>', lambda e: date.update(self)) self.spin_hora.grid(row=0, column=1, sticky='', padx=(0, 30)) self.spin_vagas = Spinbox(self.painel_entrada, textvariable=monitoradas.vagas, justify='center', width=4) self.spin_vagas.bind('<<Increment>>', vg.incrementar_vagas) self.spin_vagas.bind('<<Decrement>>', vg.decrementar_vagas) self.spin_vagas.bind('<Return>', vg.atualizar_vagas) self.spin_vagas.grid(row=0, column=2, sticky='', padx=(30, 0)) self.painel_inteira = ttk.Frame(self.painel_entrada) self.painel_inteira.columnconfigure(0, weight=1) self.painel_inteira.columnconfigure(1, weight=1) self.painel_inteira.grid(row=0, column=3, sticky='e', padx=0) self.reais = ttk.Label(self.painel_inteira, text='R$', width=4, anchor='center') self.reais.grid(row=0, column=0, sticky='e') self.spin_inteira = Spinbox(self.painel_inteira, textvariable=monitoradas.inteira, justify='center', width=5) self.spin_inteira.bind('<<Increment>>', it.incrementar_inteira) self.spin_inteira.bind('<<Decrement>>', it.decrementar_inteira) self.spin_inteira.bind('<Return>', it.atualizar_inteira) self.spin_inteira.grid(row=0, column=1, sticky='ew', padx=0) self.painel_botoes = ttk.Frame(self.painel_entrada) self.painel_botoes.columnconfigure(0, weight=1) self.painel_botoes.columnconfigure(1, weight=1) self.painel_botoes.columnconfigure(2, weight=1) self.painel_botoes.grid(row=1, column=0, columnspan=4, sticky='ew') self.root.bind('<Control-z>', lambda ev: hst.undo(self, ev)) self.root.bind('<Control-Shift-KeyPress-Z>', lambda ev: hst.redo(self, ev)) self.root.bind('<Control-a>', lambda ev: util.select_all(self, ev)) self.root.bind('<Delete>', lambda ev: util.del_selecao(self)) self.root.bind('<Control-w>', lambda *_: quit()) self.root.protocol("WM_DELETE_WINDOW", lambda: estado.persistir(self)) self.botao_remover = ttk.Button(self.painel_botoes, text='Remover') self.botao_remover.bind('<ButtonPress>', lambda ev: util.remover_linhas(self, ev)) self.botao_remover.bind('<ButtonRelease>', util.released) self.botao_remover.grid(row=0, column=0, sticky='we') self.botao_editar = ttk.Button(self.painel_botoes, text='Editar', command=lambda: util.editar_linha(self)) self.botao_editar.grid(row=0, column=1, sticky='we') self.botao_adicionar = ttk.Button( self.painel_botoes, text='Adicionar', command=lambda: util.adicionar_linha(self)) self.botao_adicionar.grid(row=0, column=2, sticky='we') self.botao_reservar = ttk.Button( self.painel_botoes, text='Reservar', command=lambda: util.reservar_viagem(self)) self.botao_reservar.grid(row=1, column=3, sticky='we', padx=(10, 0)) self.painel_botoes.columnconfigure(3, weight=1) self.botao_gerar = ttk.Button(self.painel_botoes, text='Gerar', command=lambda: gerar.gerar_linhas(self)) self.botao_gerar.grid(row=0, column=3, sticky='we', padx=(10, 0)) self.botao_relatorio = ttk.Button( self.painel_botoes, text='Relatório', command=lambda: rel.exibir_relatorio(self)) self.botao_relatorio.grid(row=1, column=0, sticky='we') self.botao_importar_linhas = ttk.Button( self.painel_botoes, text='Importar', command=lambda: arq.importar_linhas(self)) self.botao_importar_linhas.grid(row=1, column=1, sticky='we') self.botao_exportar_linhas = ttk.Button( self.painel_botoes, text='Exportar', command=lambda: arq.exportar_linhas(self)) self.botao_exportar_linhas.grid(row=1, column=2, sticky='we') self.painel_contadores = ttk.Frame(self.frame) self.contador_linhas = ttk.Label(self.painel_contadores, text=fm.form_cont_linhas( len(estado.linhas_visiveis)), anchor='sw') self.contador_linhas.grid(row=0, column=0, sticky='sw') self.contador_onibus = ttk.Label(self.painel_contadores, text=fm.form_cont_onibus( len(monitoradas.onibus_visiveis)), anchor='sw') self.contador_onibus.grid(row=0, column=1, sticky='s') self.contador_reservas = ttk.Label(self.painel_contadores, text=fm.form_cont_reservas( len(estado.reservas)), anchor='sw') self.contador_reservas.grid(row=0, column=2, sticky='s') self.label_action = ttk.Label(self.painel_contadores, textvariable=monitoradas.cur_action, anchor='w', width=10) self.label_action.grid(row=0, column=3, sticky='se') for j in range(4): self.painel_contadores.columnconfigure(j, weight=1) self.painel_entrada.rowconfigure(0, weight=1) self.painel_entrada.rowconfigure(1, weight=1) self.painel_entrada.rowconfigure(2, weight=1) self.painel_entrada.grid(row=2, column=0, sticky='nsew') self.aba_linhas.columnconfigure(0, weight=2000) self.aba_linhas.rowconfigure(0, weight=100) self.aba_linhas.columnconfigure(1, weight=1) self.aba_linhas.rowconfigure(1, weight=1) self.aba_linhas.rowconfigure(2, minsize=70, weight=0) self.aba_reservas.columnconfigure(0, weight=2000) self.aba_reservas.columnconfigure(1, weight=1) self.aba_reservas.rowconfigure(0, weight=1) self.abas.add(self.aba_linhas, text='Linhas') self.abas.add(self.aba_reservas, text='Reservas') self.abas.grid(row=0, column=0, sticky='nesw') self.painel_contadores.grid(row=1, column=0, sticky='we', pady=5, padx=15) self.frame.rowconfigure(0, weight=1) self.frame.columnconfigure(0, weight=1) if estado.data is not None: util.povoar_treeviews(self)