def treeview_sort_column(app, tv, col, rev): ''' Ordena as linhas com base em uma coluna. ''' entradas = tv.get_children() if len(entradas) == 0: return ctrl.update_action(app, 'sort') unsorted_entradas = entradas sorted_entradas = sorted( entradas, key=lambda i: move_to_beginning(SORTING_KEYS[tv.winfo_name()](i), col), reverse=rev) for i, item in enumerate(sorted_entradas): tv.move(item, '', i) ctrl.update_action(app, 'sort') historico_redo = monitoradas.historico_redo historico_undo = monitoradas.historico_undo historico_redo.clear() historico_undo.append(['sort', tv, unsorted_entradas]) tv.heading(col, command=lambda coluna=col: treeview_sort_column( app, tv, coluna, not rev))
def undo_reserva(event): historico_undo = monitoradas.historico_undo historico_redo = monitoradas.historico_redo try: evento = chave, *dados = historico_undo.pop(-1) historico_redo.append(evento) actions = hst.desfazer[chave](app, *dados) try: close = dados[0][:-6] != onibus except (IndexError, TypeError): close = True if not close: info_reserva = decodificar_id_reserva(dados[0]) assento = info_reserva.assento nonlocal selected if selected['text'] != 'None': selected['style'] = 'available.TLabel' labels[assento]['style'] = 'selected.TLabel' labels[assento]['cursor'] = 'hand2' labels[assento].bind('<Button-1>', selecionar) selected = labels[assento] button_state() 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] = labels.get( assento_indisponivel, ttk.Label(painel_assentos, text=assento_indisponivel, anchor='center', takefocus=True, width=4, style='available.TLabel', cursor='hand2')) i, j = coordenadas_assento(assento_indisponivel) labels[assento_indisponivel].grid( row=i, column=j, padx=(0, 10) if j == 1 else (10, 0) if j == 2 else 0) painel_assentos.rowconfigure(i, weight=1) if actions == 0: hst.undo(app) else: ctrl.update_action(app, cte.ACOES_COMPLEMENTARES.get(chave, chave)) except IndexError: close = False if close: monitoradas.janela_reservas.destroy()
def redo(app, evento=None): print('CTRL+SHIFT+Z') try: evento = chave, *dados = monitoradas.historico_redo.pop(-1) monitoradas.historico_undo.append(evento) actions = refazer[chave](app, *dados) if actions == 0: redo(app) else: print(cte.ACTIONS.get(chave, chave)) ctrl.update_action(app, chave) except IndexError: print('Nothing to redo...')
def undo(app, evento=None): print('CTRL+Z') try: evento = chave, *dados = monitoradas.historico_undo.pop(-1) monitoradas.historico_redo.append(evento) actions = desfazer[chave](app, *dados) if actions == 0: undo(app) else: print( cte.ACOES_COMPLEMENTARES.get(chave, cte.ACTIONS.get(chave, chave))) ctrl.update_action(app, cte.ACOES_COMPLEMENTARES.get(chave, chave)) except IndexError: print('Nothing to undo...')
def remover_selecao_del(app): ''' Remove as linhas selecionadas. ''' linhas = app.linhas itens = sorted(linhas.selection()) if itens == []: # nenhuma linha selecionada return indices = [] for item in itens.copy(): if item in estado.linhas_visiveis: estado.linhas_visiveis.remove(item) onibus_filhos = estado.linhas_entradas[item].onibus.keys() monitoradas.onibus_visiveis -= onibus_filhos estado.onibus_invisiveis.update(onibus_filhos) elif item in monitoradas.onibus_visiveis: monitoradas.onibus_visiveis.remove(item) estado.onibus_invisiveis.add(item) else: itens.remove(item) continue ctrl.update_action(app, 'rmv') indices.append(linhas.index(item)) linhas.detach(item) # atualiza os contadores, já que removemos algumas linhas app.contador_linhas['text'] = fm.form_cont_linhas( len(estado.linhas_visiveis)) app.contador_onibus['text'] = fm.form_cont_onibus( len(monitoradas.onibus_visiveis)) if len(indices) > 0: historico_undo = monitoradas.historico_undo historico_redo = monitoradas.historico_redo indices = indices[::-1] itens = itens[::-1] if len(historico_undo ) == 0 or historico_undo[-1] != ['rmv', indices, itens]: ctrl.update_action(app, 'rmv') historico_redo.clear() # adicionamos o evento da remoção no histórico, para permitir CTRL+Z historico_undo.append(['rmv', indices, itens])
def check_pressed_rmv(app, indices, itens): pressed = monitoradas.pressed if pressed: indices, itens = remover_selecao(app, indices, itens) app.root.after(20, lambda *_: check_pressed_rmv(app, indices, itens)) else: if len(indices) > 0: historico_undo = monitoradas.historico_undo historico_redo = monitoradas.historico_redo historico_redo.clear() indices = indices[::-1] itens = itens[::-1] if len(historico_undo ) == 0 or historico_undo[-1] != ['rmv', indices, itens]: ctrl.update_action(app, 'rmv') historico_redo.clear() historico_undo.append(['rmv', indices, itens])
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 remover_selecao(app, indices, itens): linhas = app.linhas # assim as linhas são acessadas primeiro, removendo possíveis ônibus filhos, já que o detach de uma linha desabilita junto os seus filhos (ônibus) cur_itens = sorted(linhas.selection()) if cur_itens == []: try: cur_itens = [linhas.get_children()[0]] except IndexError: released(1) return indices, itens cur_indices = [] for item in cur_itens.copy(): if item in estado.linhas_visiveis: estado.linhas_visiveis.remove(item) onibus_filhos = estado.linhas_entradas[item].onibus.keys() monitoradas.onibus_visiveis -= onibus_filhos estado.onibus_invisiveis.update(onibus_filhos) elif item in monitoradas.onibus_visiveis: monitoradas.onibus_visiveis.remove(item) estado.onibus_invisiveis.add(item) else: # remove ônibus filhos caso sua linha haja sido selecionada cur_itens.remove(item) continue ctrl.update_action(app, 'rmv') cur_indices.append(linhas.index(item)) linhas.detach(item) app.contador_linhas['text'] = fm.form_cont_linhas( len(estado.linhas_visiveis)) app.contador_onibus['text'] = fm.form_cont_onibus( len(monitoradas.onibus_visiveis)) indices.extend(cur_indices) itens.extend(cur_itens) return indices, itens
def redo_reserva(event): historico_undo = monitoradas.historico_undo historico_redo = monitoradas.historico_redo try: evento = chave, *dados = historico_redo.pop(-1) historico_undo.append(evento) actions = hst.refazer[chave](app, *dados) try: close = dados[0][:-6] != onibus except (IndexError, TypeError): close = True if not close: info_reserva = decodificar_id_reserva(dados[0]) assento, tipo = info_reserva.assento, info_reserva.passagem.tipo labels[assento]['cursor'] = 'X_cursor' labels[assento]['style'] = estilo[tipo] labels[assento].unbind('<Button-1>') nonlocal selected selected = ttk.Label(painel_assentos, text='None') button_state() if len(estado.linhas_entradas[linha].onibus[onibus]) == 1: 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() if actions == 0: hst.redo(app) else: ctrl.update_action(app, chave) except IndexError: close = False if close: monitoradas.janela_reservas.destroy()
def adicionar_linha(app): ''' Adiciona uma linha com base nos campos de texto. ''' # lemos todos os campos de texto campos = [ dest.atualizar_destino(), date.update(app), vg.atualizar_vagas(), it.atualizar_inteira() ] for campo in campos: if popup.campos_invalidos[campo](): return # aqui, todos os campos de texto possuem valores válidos, então vamos criar a linha linhas = app.linhas destino = monitoradas.destino.get() tempo_exibido = monitoradas.tempo_exibido vagas = monitoradas.vagas.get() inteira = monitoradas.inteira.get() linha = gerar_id_linha(destino, tempo_exibido) # antes de inseri-la graficamente, temos que verificar se ela já existe if linha in estado.linhas_entradas: # Linha desabilitada. Deseja reabilitá-la? if linha not in estado.linhas_visiveis: texto = 'Linha desabilitada, deseja reabilitá-la?.' resposta = messagebox.askyesno(title='Linha existe', message=texto, default='yes') if resposta: app.linhas.reattach(linha, '', 0) estado.linhas_visiveis.add(linha) onibus_filhos_visiveis = set(app.linhas.get_children(linha)) estado.onibus_invisiveis -= onibus_filhos_visiveis monitoradas.onibus_visiveis.update(onibus_filhos_visiveis) ctrl.update_action(app, 'Restaurar') monitoradas.historico_redo.clear() monitoradas.historico_undo.append(['show', [0], [linha]]) app.linhas.selection_set(linha) app.contador_linhas['text'] = fm.form_cont_linhas( len(estado.linhas_visiveis)) app.contador_onibus['text'] = fm.form_cont_onibus( len(monitoradas.onibus_visiveis)) # nova ação: reabilitar else: app.linhas.see(linha) app.linhas.selection_set(linha) return h = minutos_dia(tempo_exibido) estado.linhas_possiveis.discard((destino, h)) estado.horarios_linhas[h] = estado.horarios_linhas.get(h, set()).union( {linha}) dest.add_destino(app) ctrl.update_action(app, 'add') linhas.insert(parent='', index=0, values=(destino, fm.form_tempo(tempo_exibido), vagas, inteira), iid=linha) estado.linhas_visiveis.add(linha) app.contador_linhas['text'] = fm.form_cont_linhas( len(estado.linhas_visiveis)) estado.linhas_entradas[linha] = cte.ESTRUTURA_LINHA( destino, tempo_exibido, int(vagas) // 2, round(float(inteira) * 100 - 300), dict()) data_atual = monitoradas.data_atual.replace(second=0, microsecond=0) for d in range(cte.MAXIMO_NUMERO_DE_DIAS_ATE_RESERVA + 1): partida = (data_atual + dt.timedelta(d)).replace( hour=tempo_exibido.hour, minute=tempo_exibido.minute) if data_atual <= partida <= data_atual + dt.timedelta( cte.MAXIMO_NUMERO_DE_DIAS_ATE_RESERVA): onibus = gerar_id_onibus(linha, partida) linhas.insert(parent=linha, index='end', values=('-', fm.form_data(partida), vagas, '-'), iid=onibus) monitoradas.onibus_visiveis.add(onibus) estado.linhas_entradas[linha].onibus[onibus] = dict() app.contador_onibus['text'] = fm.form_cont_onibus( len(monitoradas.onibus_visiveis)) app.contador_linhas['text'] = fm.form_cont_linhas( len(estado.linhas_visiveis)) app.contador_onibus['text'] = fm.form_cont_onibus( len(monitoradas.onibus_visiveis)) historico_undo = monitoradas.historico_undo historico_redo = monitoradas.historico_redo historico_redo.clear() historico_undo.append(['add', [[estado.linhas_entradas[linha], list()]]]) linhas.selection_set(linha)
def editar_linha(app): ''' Edita informações sobre uma linha. ''' selecao = app.linhas.selection() if len(selecao) != 1 or app.linhas.parent(selecao[0]) != '': texto = 'Selecione uma única linha.' messagebox.showwarning(title='Impossível Editar', message=texto) return [linha] = selecao dados_linha = estado.linhas_entradas[linha] # a condição a seguir é True sse existe algum ônibus com reservas na linha selecionada. if any(len(x) != 0 for x in dados_linha.onibus.values()): texto = 'Linha possui reservas.' messagebox.showwarning(title='Impossível Editar', message=texto) return campos = [ dest.atualizar_destino(), date.update(app), vg.atualizar_vagas(), it.atualizar_inteira() ] for campo in campos: if popup.campos_invalidos[campo](): return destino_alterada = monitoradas.destino.get() tempo_exibido = monitoradas.tempo_exibido vagas_alterada = monitoradas.vagas.get() inteira_alterada = monitoradas.inteira.get() linha_alterada = gerar_id_linha(destino_alterada, tempo_exibido) h = minutos_dia(estado.linhas_entradas[linha].horario) if linha_alterada in estado.linhas_entradas: if linha_alterada in estado.linhas_visiveis: if linha != linha_alterada: texto = 'Linha já existe.' messagebox.showwarning(title='Impossível Editar', message=texto) elif dados_linha[2:4] != [ int(vagas_alterada) // 2, round(float(inteira_alterada) * 100 - 300) ]: dados_linha_alterada = estado.linhas_entradas[ linha] = cte.ESTRUTURA_LINHA( *dados_linha[:2], int(vagas_alterada) // 2, round(float(inteira_alterada) * 100 - 300), dados_linha[-1]) app.linhas.item( linha, values=campos_linha_formatados(dados_linha_alterada)) app.linhas.selection_set(linha_alterada) monitoradas.historico_redo.clear() monitoradas.historico_undo.append( ['change', dados_linha, dados_linha_alterada, False]) app.contador_linhas['text'] = fm.form_cont_linhas( len(estado.linhas_visiveis)) app.contador_onibus['text'] = fm.form_cont_onibus( len(monitoradas.onibus_visiveis)) else: texto = 'Linha desabilitada, deseja reabilitá-la?' resposta = messagebox.askyesno(title='Impossível Editar', message=texto, default='no') if resposta: app.linhas.reattach(linha_alterada, '', app.linhas.index(linha)) app.linhas.item(linha_alterada, open=app.linhas.item(linha, 'open')) estado.linhas_visiveis.add(linha_alterada) onibus_filhos_visiveis = set( app.linhas.get_children(linha_alterada)) estado.onibus_invisiveis -= onibus_filhos_visiveis monitoradas.onibus_visiveis.update(onibus_filhos_visiveis) ctrl.update_action(app, 'Restaurar') estado.horarios_linhas[h].remove(linha) app.linhas.delete(linha) estado.linhas_visiveis.remove(linha) for onibus in estado.linhas_entradas[linha].onibus: monitoradas.onibus_visiveis.discard(onibus) estado.onibus_invisiveis.discard(onibus) monitoradas.historico_redo.clear() monitoradas.historico_undo.append([ 'change', estado.linhas_entradas[linha], estado.linhas_entradas[linha_alterada], True ]) estado.linhas_possiveis.add((dados_linha.destino.title(), h)) del estado.linhas_entradas[linha] app.linhas.selection_set(linha_alterada) app.contador_linhas['text'] = fm.form_cont_linhas( len(estado.linhas_visiveis)) app.contador_onibus['text'] = fm.form_cont_onibus( len(monitoradas.onibus_visiveis)) return h_alt = tempo_exibido.hour * 60 + tempo_exibido.minute estado.linhas_possiveis.remove((destino_alterada, h_alt)) estado.linhas_possiveis.add((dados_linha.destino.title(), h)) estado.horarios_linhas[h].remove(linha) estado.horarios_linhas[h_alt] = estado.horarios_linhas.get( h_alt, set()).union({linha_alterada}) dest.add_destino(app) ctrl.update_action(app, 'change') indice = app.linhas.index(linha) app.linhas.insert(parent='', index=indice, values=(destino_alterada, fm.form_tempo(tempo_exibido), vagas_alterada, inteira_alterada), iid=linha_alterada, open=app.linhas.item(linha, 'open')) app.linhas.delete(linha) estado.linhas_visiveis.remove(linha) estado.linhas_visiveis.add(linha_alterada) onibus_filhos = dict() data_atual = monitoradas.data_atual.replace(second=0, microsecond=0) for d in range(cte.MAXIMO_NUMERO_DE_DIAS_ATE_RESERVA + 1): partida = (data_atual + dt.timedelta(d)).replace( hour=tempo_exibido.hour, minute=tempo_exibido.minute) if data_atual <= partida <= data_atual + dt.timedelta( cte.MAXIMO_NUMERO_DE_DIAS_ATE_RESERVA): onibus = gerar_id_onibus(linha_alterada, partida) app.linhas.insert(parent=linha_alterada, index='end', values=('-', fm.form_data(partida), vagas_alterada, '-'), iid=onibus) monitoradas.onibus_visiveis.add(onibus) onibus_filhos[onibus] = dict() estado.linhas_entradas[linha_alterada] = cte.ESTRUTURA_LINHA( destino_alterada, tempo_exibido, int(vagas_alterada) // 2, round(float(inteira_alterada) * 100 - 300), onibus_filhos) for onibus in estado.linhas_entradas[linha].onibus: monitoradas.onibus_visiveis.discard(onibus) estado.onibus_invisiveis.discard(onibus) monitoradas.historico_redo.clear() monitoradas.historico_undo.append([ 'change', estado.linhas_entradas[linha], estado.linhas_entradas[linha_alterada], False ]) del estado.linhas_entradas[linha] app.contador_linhas['text'] = fm.form_cont_linhas( len(estado.linhas_visiveis)) app.contador_onibus['text'] = fm.form_cont_onibus( len(monitoradas.onibus_visiveis)) ctrl.update_action(app, 'change') app.linhas.selection_set(linha_alterada)
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()