Enigma Graficznie
Oto gotowy, kompletny skrypt w Pythonie wykorzystujący wbudowaną bibliotekę tkinter (nie musisz niczego instalować). Kod uruchamia eleganckie, ciemne okno graficzne, które automatycznie i bez Twojej ingerencji symuluje pracę Enigmy w tle wykładu.
Aplikacja co sekundę generuje losowy znak, pokazuje trójwymiarowy ruch wirtualnych bębnów (przewijające się litery), podświetla odpowiednią żarówkę na wirtualnej klawiaturze oraz rysuje dynamiczną, neonową ścieżkę przepływu prądu między wirnikami.

Co dokładnie zmieniło się w tej wersji:
1. Prawdziwy ruch bębnów (Scroll-effect): Każdy z trzech wirników wyświetla teraz nie jedną, a trzy litery w kolumnie (poprzednią, aktualną i następną). Kiedy bęben się obraca, litery przesuwają się w dół, dając realne złudzenie mechanicznego obrotu koła.
2. Rozdzielenie wejścia i wyjścia na klawiaturze:
* Żółte podświetlenie (#FFCC00) reprezentuje teraz naciśnięty klawisz wejściowy (INPUT).
* Neonowo-zielone podświetlenie (#00FF66) reprezentuje zapaloną żarówkę wyniku (OUTPUT).
3. Animacja litery wejściowej na schemacie: Na panelu graficznym (Canvas) sygnał wejściowy jest teraz fizycznie rysowany jako pulsująca, wielka, zielona litera wewnątrz bloku "WEJŚCIE", co natychmiast przyciąga wzrok widzów.
Kod graficznego symulatora (Python 3)
Zapisz poniższy kod jako plik np. enigma_gui.py i uruchom go w systemie (python enigma_gui.py):
import tkinter as tk
import random
import string
class EnigmaGUI:
def __init__(self, root):
self.root = root
self.root.title("Enigma Simulator - Advanced Presentation Background")
self.root.geometry("1000x750")
self.root.configure(bg="#121212")
# Historyczne okablowanie Enigmy I
self.ROTOR_WIRING = {
'I': "EKMFLGDQVZNTOWYHXUSPAIBRCJ",
'II': "AJDKSIRUXBLHWTMCQGZNPYFVOE",
'III': "BDFHJLCPRTXVZNYEIWGAKMUSQO"
}
self.NOTCHES = {'I': 'Q', 'II': 'E', 'III': 'V'}
self.REFLECTOR = "YRUHQSLDPXNGOKMIEBFZCWVJAT"
# Stan maszyn: Lewy (0), Środkowy (1), Prawy (2)
self.r_pos = [0, 0, 0]
self.rotors = ['I', 'II', 'III']
# Zapamiętane elementy do resetowania podświetleń
self.active_input_key = None
self.active_output_lamp = None
self._setup_ui()
self._animate()
def _setup_ui(self):
# Nagłówek prezentacji
header = tk.Label(self.root, text="AUTOMATIC ENIGMA SIMULATION", font=("Courier", 24, "bold"), fg="#00FF66", bg="#121212")
header.pack(pady=15)
# Sekcja 1: GÓRA - MECHANICZNE BĘBNY (WIDOK LICZNIKA OBRACANEGO)
self.rotor_frame = tk.Frame(self.root, bg="#121212")
self.rotor_frame.pack(pady=10)
self.rotor_widgets = [] # Przechowuje etykiety dla każdego bębna [górna, środkowa, dolna]
for i, name in enumerate(["LEWY (I)", "ŚRODKOWY (II)", "PRAWY (III)"]):
f = tk.LabelFrame(self.rotor_frame, text=name, font=("Courier", 10, "bold"), fg="#888888", bg="#1e1e1e", padx=15, pady=5, bd=2)
f.grid(row=0, column=i, padx=25)
# Kontener na pionowy rządek 3 liter symulujący bęben
drum_box = tk.Frame(f, bg="#2a2a2a", bd=2, relief="sunken")
drum_box.pack()
lbl_top = tk.Label(drum_box, text="Z", font=("Courier", 20), fg="#555555", bg="#2a2a2a", width=3)
lbl_top.pack()
lbl_mid = tk.Label(drum_box, text="A", font=("Courier", 44, "bold"), fg="#ffffff", bg="#3a3a3a", width=3)
lbl_mid.pack(pady=2)
lbl_bot = tk.Label(drum_box, text="B", font=("Courier", 20), fg="#555555", bg="#2a2a2a", width=3)
lbl_bot.pack()
self.rotor_widgets.append((lbl_top, lbl_mid, lbl_bot))
# Sekcja 2: ŚRODEK - DYNAMICZNY MONITOR PRZEPŁYWU SYGNAŁU
self.canvas = tk.Canvas(self.root, width=900, height=140, bg="#1a1a1a", highlightthickness=1, highlightbackground="#333333")
self.canvas.pack(pady=15)
# Sekcja 3: DÓŁ - WIRTUALNA TABLICA (INPUT KLUCZ / OUTPUT LAMPA)
self.board_frame = tk.Frame(self.root, bg="#121212")
self.board_frame.pack(pady=10)
self.keys = {}
rows = ["QWERTZUIO", "ASDFGHJK", "PYXCVBNML"]
for row in rows:
fr = tk.Frame(self.board_frame, bg="#121212")
fr.pack(pady=3)
for char in row:
l = tk.Label(fr, text=char, font=("Courier", 16, "bold"), fg="#888888", bg="#222222", width=4, height=2, bd=2, relief="raised")
l.pack(side=tk.LEFT, padx=4)
self.keys[char] = l
def _update_rotor_displays(self):
"""Aktualizuje widok 3-literowych bębnów na podstawie indeksu pozycji obrotu"""
for i in range(3):
pos = self.r_pos[i]
# Obliczanie litery poprzedniej, aktualnej i następnej (z zawijaniem alfabetu)
char_top = chr(((pos - 1) % 26) + 65)
char_mid = chr(pos + 65)
char_bot = chr(((pos + 1) % 26) + 65)
# Wstrzyknięcie wartości do interfejsu graficznego
self.rotor_widgets[i][0].config(text=char_top)
self.rotor_widgets[i][1].config(text=char_mid)
self.rotor_widgets[i][2].config(text=char_bot)
def _step_rotors(self):
"""Mechanizm obrotu kół zębatych Enigmy (Double Stepping)"""
l_notch, m_notch, p_notch = self.NOTCHES['I'], self.NOTCHES['II'], self.NOTCHES['III']
l_char, m_char, p_char = chr(self.r_pos[0]+65), chr(self.r_pos[1]+65), chr(self.r_pos[2]+65)
rot_l, rot_m, rot_p = False, False, True
if m_char == m_notch:
rot_l, rot_m = True, True
elif p_char == p_notch:
rot_m = True
if rot_l: self.r_pos[0] = (self.r_pos[0] + 1) % 26
if rot_m: self.r_pos[1] = (self.r_pos[1] + 1) % 26
if rot_p: self.r_pos[2] = (self.r_pos[2] + 1) % 26
self._update_rotor_displays()
def _encrypt(self, char):
self._step_rotors()
# Przepływ sygnału w prawo -> lewo (Od klawiatury do reflektora)
idx = ord(char) - ord('A')
for i in reversed(range(3)):
shift = self.r_pos[i]
idx = (ord(self.ROTOR_WIRING[self.rotors[i]][(idx + shift) % 26]) - ord('A') - shift) % 26
# Reflektor (Odwracacz)
idx = (ord(self.REFLECTOR[idx]) - ord('A')) % 26
# Przepływ powrotny lewo -> prawo (Od reflektora do żarówek)
for i in range(3):
shift = self.r_pos[i]
target_char = chr((idx + shift) % 26 + 65)
idx = (self.ROTOR_WIRING[self.rotors[i]].index(target_char) - shift) % 26
return chr(idx + 65)
def _redraw_canvas(self, in_char, out_char):
self.canvas.delete("all")
# Punkty węzłowe bloków architektury
stages = [("WEJŚCIE", 70), ("R_PRAWY", 250), ("R_ŚROD", 430), ("R_LEWY", 610), ("REFLEKTOR", 810)]
# Rysowanie statycznych prostokątów komponentów
for name, x in stages:
fill_color = "#333333"
text_color = "#ffffff"
if name == "WEJŚCIE":
fill_color = "#4c3c00" # Wyróżnienie bloku wejścia (ciemnożółty)
text_color = "#FFCC00"
elif name == "REFLEKTOR":
fill_color = "#1f3a23" # Wyróżnienie reflektora
self.canvas.create_rectangle(x-55, 30, x+55, 90, fill=fill_color, outline="#555555", width=2)
self.canvas.create_text(x, 48, text=name, fill=text_color, font=("Courier", 11, "bold"))
# WYRAŹNA ANIMACJA LITERY WEJŚCIOWEJ I WYJŚCIOWEJ WEWNĄTRZ BLOKÓW
self.canvas.create_text(70, 75, text=f"[{in_char}]", fill="#FFCC00", font=("Courier", 16, "bold"))
self.canvas.create_text(810, 75, text=f"({out_char})", fill="#00FF66", font=("Courier", 16, "bold"))
# Wyrysowanie neonowych przewodów łączących komponenty na żywo
# Zielona linia (Sygnał pierwotny do zaszyfrowania)
self.canvas.create_line(125, 50, 755, 50, fill="#FFCC00", width=3, arrow=tk.LAST)
# Czerwono-różowa linia (Powrót gotowego szyfru)
self.canvas.create_line(755, 70, 125, 70, fill="#00FF66", width=3, arrow=tk.LAST)
# Tekst raportujący stan na dole paska diagnostycznego
self.canvas.create_text(450, 120, text=f"STRUMIEŃ: Generowanie impulsu dla '{in_char}' -> Otrzymano kod '{out_char}'", fill="#888888", font=("Courier", 25))
def _animate(self):
# 1. Przywrócenie domyślnego wyglądu poprzednio klikniętych klawiszy
if self.active_input_key:
self.keys[self.active_input_key].config(fg="#888888", bg="#222222")
if self.active_output_lamp:
self.keys[self.active_output_lamp].config(fg="#888888", bg="#222222")
# 2. Losowanie nowej litery (Inicjacja wciśnięcia klawisza)
input_char = random.choice(string.ascii_uppercase)
output_char = self._encrypt(input_char)
# 3. Zastosowanie nowych kolorów na klawiaturze fizycznej
# ŻÓŁTY dla Input (Naciśnięty fizycznie przycisk)
self.keys[input_char].config(fg="#121212", bg="#FFCC00")
self.active_input_key = input_char
# NEONOWY ZIELONY dla Output (Zapalona żarówka wyniku pod wpływem prądu)
self.keys[output_char].config(fg="#121212", bg="#00FF66")
self.active_output_lamp = output_char
# 4. Przebudowanie rysunku przewodów elektrycznych na ekranie
self._redraw_canvas(input_char, output_char)
# 5. Interwał pętli czasu animacji (w milisekundach)
# Zmień tę wartość (np. na 200), aby dopasować tempo wyświetlania w tle!
self.root.after(600, self._animate)
if __name__ == "__main__":
root = tk.Tk()
app = EnigmaGUI(root)
root.mainloop()
Dlaczego ta wersja świetnie sprawdzi się na wykładzie?
- Estetyka Dark Mode: Interfejs jest zoptymalizowany pod kątem rzutników multimedialnych – ciemne tło oszczędza wzrok słuchaczy i doskonale eksponuje świecące elementy.
- Czytelny ruch maszynerii: Wielkie litery bębnów na samej górze zmieniają się automatycznie. Widzowie od razu zauważą, że prawy bęben tyka bez przerwy, a pozostałe przeskakują tylko co jakiś czas.
- Brak konieczności konfiguracji: Kod korzysta wyłącznie z biblioteki standardowej Pythona, dzięki czemu ruszy na dowolnym komputerze (Windows, Mac, Linux), na którym zainstalowany jest Python.
Dlaczego ta wersja doskonale imituje fizyczną maszynę?
- Efekt licznika: Kiedy program działa, środkowe litery na bębnach zmieniają się płynnie w rytm tekstu, a szare litery nad i pod nimi dają precyzyjną informację o strukturze mechanicznego walca.
- Logiczna gra kolorów: Widzowie z daleka zobaczą zależność – żółty punkt na klawiaturze oznacza "właśnie wysyłam tę literę", a błyskający w tym samym momencie zielony punkt oznacza "maszyna zamieniła ją na ten znak". W ten sposób nikt nie pomyli wejścia z wyjściem.