Untitled
unknown
plain_text
5 months ago
32 kB
20
No Index
""" EuroMillions QTRNG Correlator Copyright (c) Jags Uprising This script provides a GUI-based tool to correlate EuroMillions draw numbers with a quantum random number generator (QTRNG). It includes features to: - Input and check drawn numbers. - Fetch numbers and validate their ranks and scores. - Import and export data as CSV files. Requires: - PyFTDI for USB communication. - Tkinter for GUI. """ import io import threading import contextlib import tkinter as tk from tkinter import ttk, Canvas, Frame from datetime import datetime, timedelta import time import requests from pyftdi.ftdi import Ftdi import numpy as np from bs4 import BeautifulSoup from tkinter.filedialog import askopenfilename # Moved import to top-level # Constants DATES_ALLOWED = {"Tuesday", "Friday"} TEST_TIME = timedelta(minutes=10) # Test duration BITS_PER_SECOND = 620 TOTAL_NUMBERS = 50 + 12 # 50 normal + 12 lucky numbers BITS_PER_NUMBER = 10 def initialize_ftdi(): """Initializes the FTDI device for reading random bits.""" def list_devices(): """Lists connected FTDI devices.""" buffer = io.StringIO() try: with contextlib.redirect_stdout(buffer): Ftdi().show_devices() output = buffer.getvalue() for line in output.split("\n"): if "ftdi://" in line: return line.split()[0] return None except ValueError as e: print("No FTDI USB device available:", e) return None device_url = list_devices() if not device_url: raise RuntimeError('Defaulting to "Algorithmic" bit source.') ftdi = Ftdi() ftdi.open_from_url(device_url) ftdi.set_latency_timer(2) return ftdi def read_bits_from_ftdi(ftdi, num_bits=8): """ Reads bits from the FTDI device. Args: ftdi (Ftdi): The FTDI device instance. num_bits (int): The number of bits to read. Returns: list: A list of bits read from the device. """ data = ftdi.read_data(num_bits) bits = [] for byte in data: bits.extend([(byte >> i) & 1 for i in range(7, -1, -1)]) return bits class EuroMillionsTracker: """ A GUI application for tracking EuroMillions lottery data and analyzing random bit correlations. """ def __init__(self, master): """ Initializes the EuroMillionsTracker application. Args: master (tk.Tk): The Tkinter root window. """ self.master = master self.master.title("EuroMillions QTRNG Correlator") # Initialize attributes self.bit_source = "FTDI USB Device" # Default bit source self.device_available = True # USB device availability flag try: self.ftdi = initialize_ftdi() except RuntimeError as e: print(f"{e}") self.device_available = False self.bit_source = "Algorithmic" self.sums = np.zeros(TOTAL_NUMBERS) self.highest = np.full(TOTAL_NUMBERS, -np.inf) self.lowest = np.full(TOTAL_NUMBERS, np.inf) self.last = np.zeros(TOTAL_NUMBERS) self.round_robin_index = 0 self.running = False self.start_time = None self.timestamps = [] self.sum_history = [] self.create_widgets() def get_valid_dates(self): """ Calculates valid dates for EuroMillions draws. Returns: list: A list of valid dates as strings in YYYY-MM-DD format. """ base_date = datetime.now() - timedelta(days=60) valid_dates = [] for i in range(120): future_date = base_date + timedelta(days=i) if future_date.strftime("%A") in DATES_ALLOWED: valid_dates.append(future_date.strftime("%Y-%m-%d")) return valid_dates def import_csv(self, filename): """ Imports data from a CSV file. Args: filename (str): The path to the CSV file. """ try: with open(filename, "r", encoding="utf-8") as file: # Added encoding lines = file.readlines() correlated_date = None self.start_time = None self.bit_source = None self.timestamps = [] self.sum_history = [] # Reset statistics self.sums = np.zeros(TOTAL_NUMBERS) self.lowest = np.full(TOTAL_NUMBERS, np.inf) self.highest = np.full(TOTAL_NUMBERS, -np.inf) self.last = np.zeros(TOTAL_NUMBERS) line_index = 0 while line_index < len(lines): line = lines[line_index].strip() if line.startswith("Correlated Date:"): correlated_date = line.split(": ", 1)[1] elif line.startswith("Test Start Time:"): test_start_time = line.split(": ", 1)[1] self.start_time = datetime.strptime(test_start_time, "%Y-%m-%d %H:%M:%S.%f") elif line.startswith("Bit Source:"): self.bit_source = line.split(": ", 1)[1] elif line.startswith("Time (s),"): line_index += 1 break line_index += 1 if hasattr(self, "date_picker") and correlated_date: self.date_picker.set(correlated_date) while line_index < len(lines): line = lines[line_index].strip() if line == "": break parts = line.split(", ") timestamp = float(parts[0]) sums = list(map(lambda x: int(float(x)), parts[1:TOTAL_NUMBERS + 1])) self.timestamps.append(timestamp) self.sum_history.append(sums) for i in range(TOTAL_NUMBERS): value = sums[i] self.sums[i] = value self.lowest[i] = min(self.lowest[i], value) self.highest[i] = max(self.highest[i], value) self.last[i] = value line_index += 1 self.update_table() print("CSV file import successful.") except Exception as e: print(f"Error importing CSV file: {e}") def create_widgets(self): """Creates the widget interface for the application.""" # Control Frame self.control_frame = ttk.Frame(self.master) self.control_frame.pack(side="top", fill="x", padx=5, pady=5) ttk.Label(self.control_frame, text="Draw Date:").pack(side="left", padx=5) self.date_picker = ttk.Combobox( self.control_frame, values=self.get_valid_dates(), state="readonly" ) self.date_picker.pack(side="left", padx=5) self.date_picker.set(self.get_valid_dates()[0]) self.run_button = ttk.Button( self.control_frame, text="Run", command=self.run_test ) self.run_button.pack(side="left", padx=5) self.stop_button = ttk.Button( self.control_frame, text="Stop", command=self.stop_test ) self.stop_button.pack(side="left", padx=5) self.log_file_button = ttk.Button( self.control_frame, text="Export CSV", command=self.save_log ) self.log_file_button.pack(side="left", padx=5) self.bit_source_button = ttk.Button( self.control_frame, text=f"Source: {self.bit_source}", command=self.toggle_bit_source, ) self.bit_source_button.pack(side="left", padx=5) # Disable toggle button if no USB device is available if not self.device_available: self.bit_source_button.config(state="disabled") # Subtitle subtitle = "Time: 21:00-21:10 CET, Paris, France" self.subtitle_label = ttk.Label( self.master, text=f"EuroMillions RNG Correlation\n{subtitle}", font=("Arial", 10), ) self.subtitle_label.pack(side="top", pady=5) # Input Frame for Drawn Numbers self.input_frame = ttk.Frame(self.master) self.input_frame.pack(side="top", fill="x", padx=5, pady=5) ttk.Label(self.input_frame, text="Drawn Numbers:").grid(row=0, column=0, padx=5) self.drawn_normal_numbers = [ ttk.Entry(self.input_frame, width=5) for _ in range(5) ] for i, entry in enumerate(self.drawn_normal_numbers): entry.grid(row=0, column=i + 1, padx=5) ttk.Label(self.input_frame, text="Lucky Numbers:").grid(row=0, column=6, padx=5) self.drawn_lucky_numbers = [ ttk.Entry(self.input_frame, width=5) for _ in range(2) ] for i, entry in enumerate(self.drawn_lucky_numbers): entry.grid(row=0, column=i + 7, padx=5) # "Fetch Numbers" Button self.fetch_button = ttk.Button( self.input_frame, text="Fetch Numbers", command=self.fetch_and_fill_drawn_numbers, ) self.fetch_button.grid(row=0, column=9, padx=5) # Button to check the ranks self.check_button = ttk.Button( self.input_frame, text="Check Ranks", command=self.check_drawn_numbers ) self.check_button.grid(row=0, column=10, padx=5) # Add "Predicted:" labels ttk.Label(self.input_frame, text="Predicted:").grid(row=1, column=0, padx=5) self.predicted_normal_labels = [ ttk.Label(self.input_frame, text="") for _ in range(5) ] for i in range(5): self.predicted_normal_labels[i].grid(row=1, column=i + 1, padx=5) ttk.Label(self.input_frame, text="Predicted:").grid(row=1, column=6, padx=5) self.predicted_lucky_labels = [ ttk.Label(self.input_frame, text="") for _ in range(2) ] for i in range(2): self.predicted_lucky_labels[i].grid(row=1, column=i + 7, padx=5) # Create labels for normal numbers ranks and scores (displaying results below the input) ttk.Label(self.input_frame, text="Rank:").grid(row=2, column=0, padx=5) self.normal_ranks_labels = [ ttk.Label(self.input_frame, text="") for _ in range(5) ] self.normal_scores_labels = [ ttk.Label(self.input_frame, text="") for _ in range(5) ] for i in range(5): self.normal_ranks_labels[i].grid(row=2, column=i + 1, padx=5) self.normal_scores_labels[i].grid(row=3, column=i + 1, padx=5) # Create labels for lucky numbers ranks and scores ttk.Label(self.input_frame, text="Rank:").grid(row=2, column=6, padx=5) self.lucky_ranks_labels = [ ttk.Label(self.input_frame, text="") for _ in range(2) ] self.lucky_scores_labels = [ ttk.Label(self.input_frame, text="") for _ in range(2) ] for i in range(2): self.lucky_ranks_labels[i].grid(row=2, column=i + 7, padx=5) self.lucky_scores_labels[i].grid(row=3, column=i + 7, padx=5) # Create "Score:" labels below the "Rank:" labels for lucky numbers ttk.Label(self.input_frame, text="Score:").grid(row=3, column=6, padx=5) ttk.Label(self.input_frame, text="Score:").grid(row=3, column=0, padx=5) # Tables self.scroll_frame = Frame(self.master) self.scroll_frame.pack(side="top", fill="x", padx=5, pady=5) self.create_tables() # Add a "Import CSV" button to load a CSV file self.import_button = ttk.Button( self.control_frame, text="Import CSV", command=self.import_csv_dialog ) self.import_button.pack(side="left", padx=5) def check_drawn_numbers(self): """Check the ranks of the entered drawn numbers and predicted numbers.""" try: # Get the entered numbers normal_numbers = [ int(entry.get()) for entry in self.drawn_normal_numbers if entry.get().isdigit() ] lucky_numbers = [ int(entry.get()) for entry in self.drawn_lucky_numbers if entry.get().isdigit() ] # Check if all numbers are valid if len(normal_numbers) != 5 or len(lucky_numbers) != 2: print("Please enter exactly 5 normal numbers and 2 lucky numbers.") return # Ensure self.highest contains no invalid values self.highest = np.nan_to_num(self.highest) # Create sorted lists of indices based on the highest scores normal_ranks = np.argsort( -self.highest[:50] ) # Normal numbers lucky_ranks = np.argsort( -self.highest[50:] ) # Lucky numbers # Prepare results for normal and lucky numbers normal_scores = [] normal_ranks_list = [] for num in normal_numbers: if 1 <= num <= 50: index = num - 1 # Convert to zero-based index rank_indices = np.where(normal_ranks == index)[0] if rank_indices.size > 0: # Check if the index exists in normal_ranks rank = rank_indices[0] + 1 # Find rank (1-based) score = int(self.highest[index]) normal_scores.append(score) normal_ranks_list.append(rank) else: normal_scores.append("Rank not found") normal_ranks_list.append("N/A") else: normal_scores.append("Invalid") normal_ranks_list.append("Invalid") lucky_scores = [] lucky_ranks_list = [] for num in lucky_numbers: if 1 <= num <= 12: index = 50 + num - 1 # Convert to zero-based index in lucky numbers rank_indices = np.where(lucky_ranks == (num - 1))[0] if rank_indices.size > 0: # Check if the index exists in lucky_ranks rank = rank_indices[0] + 1 # Find rank (1-based) score = int(self.highest[index]) lucky_scores.append(score) lucky_ranks_list.append(rank) else: lucky_scores.append("Rank not found") lucky_ranks_list.append("N/A") else: lucky_scores.append("Invalid") lucky_ranks_list.append("Invalid") # Calculate the predicted numbers predicted_normal_indices = normal_ranks[:5] predicted_lucky_indices = lucky_ranks[:2] predicted_normal_numbers = [idx + 1 for idx in predicted_normal_indices] predicted_normal_scores = [int(self.highest[idx]) for idx in predicted_normal_indices] predicted_lucky_numbers = [idx + 1 for idx in predicted_lucky_indices] predicted_lucky_scores = [int(self.highest[50 + idx]) for idx in predicted_lucky_indices] # Display the predicted numbers and their scores for i, label in enumerate(self.predicted_normal_labels): label.config(text=f"{predicted_normal_numbers[i]} ({predicted_normal_scores[i]})") for i, label in enumerate(self.predicted_lucky_labels): label.config(text=f"{predicted_lucky_numbers[i]} ({predicted_lucky_scores[i]})") # Display the ranks and scores for drawn numbers for i in range(5): self.normal_ranks_labels[i].config(text=normal_ranks_list[i]) self.normal_scores_labels[i].config(text=normal_scores[i]) for i in range(2): self.lucky_ranks_labels[i].config(text=lucky_ranks_list[i]) self.lucky_scores_labels[i].config(text=lucky_scores[i]) except Exception as e: print(f"Error checking numbers: {e}") # Update Normal Numbers Ranks and Scores in respective labels for i, (num, rank, score) in enumerate( zip(normal_numbers, normal_ranks_list, normal_scores) ): # Just show the rank and score without color or bold formatting self.normal_ranks_labels[i].config(text=str(rank)) self.normal_scores_labels[i].config(text=str(score)) # Update Lucky Numbers Ranks and Scores in respective labels for i, (num, rank, score) in enumerate( zip(lucky_numbers, lucky_ranks_list, lucky_scores) ): # Just show the rank and score without color or bold formatting self.lucky_ranks_labels[i].config(text=str(rank)) self.lucky_scores_labels[i].config(text=str(score)) except Exception as e: print(f"Error checking ranks: {e}") def fetch_and_fill_drawn_numbers(self): """Fetch drawn numbers from the website and fill them in the input fields.""" try: # Get the selected date from the combobox selected_date = self.date_picker.get() if not selected_date: print("No date selected.") return # Fetch the drawn numbers from the website drawn_numbers = self.fetch_drawn_numbers_from_web(selected_date) # Update the fields with fetched numbers if drawn_numbers: # Ensure we got numbers before updating fields normal_numbers, lucky_numbers = drawn_numbers for i in range(5): self.drawn_normal_numbers[i].delete(0, "end") self.drawn_normal_numbers[i].insert(0, str(normal_numbers[i])) for i in range(2): self.drawn_lucky_numbers[i].delete(0, "end") self.drawn_lucky_numbers[i].insert(0, str(lucky_numbers[i])) print("Fetched and updated drawn numbers successfully.") else: print("Failed to fetch numbers. Please try again or enter manually.") except Exception as e: print(f"Error fetching drawn numbers: {e}") def fetch_drawn_numbers_from_web(self, selected_date): """ Scrape drawn numbers from the website based on the selected date. Args: selected_date (str): The date to fetch the drawn numbers for. Returns: tuple: Normal numbers and lucky numbers scraped from the website. """ try: # Convert the string date to a datetime object date_obj = datetime.strptime(selected_date, "%Y-%m-%d") formatted_date = date_obj.strftime("%d-%m-%Y") url = f"https://www.lottery.co.uk/euromillions/results-{formatted_date}" response = requests.get(url, timeout=5) # Added timeout if response.status_code != 200: raise Exception( f"Failed to fetch webpage. Status code: {response.status_code}" ) soup = BeautifulSoup(response.content, "html.parser") balls = soup.select("#ballsAscending span") if len(balls) < 7: raise ValueError("Unable to find all drawn numbers on the webpage.") # Extract numbers normal_numbers = [int(balls[i].text) for i in range(5)] lucky_numbers = [int(balls[i].text) for i in range(5, 7)] return normal_numbers, lucky_numbers except ValueError as ve: print(f"ValueError: {ve}") return None except requests.RequestException as re: print(f"RequestException: {re}") return None except Exception as e: print(f"Error scraping website for drawn numbers: {e}") return None def import_csv_dialog(self): """Open a dialog to select a CSV file for import.""" filename = askopenfilename(filetypes=[("CSV Files", "*.csv")]) if filename: self.import_csv(filename) def toggle_bit_source(self): """Toggle between USB Device and Algorithmic Random Bit sources.""" if not self.device_available: print("FTDI USB device is not available. Cannot toggle to 'FTDI USB Device'.") return self.bit_source = "Algorithmic" if self.bit_source == "FTDI USB Device" else "FTDI USB Device" self.bit_source_button.config(text=f"Source: {self.bit_source}") def create_tables(self): """Create 1 scrollable table for all normal numbers and 1 for lucky numbers.""" self.labels = [] # Store all labels for updating table_frame = Frame(self.scroll_frame) table_frame.pack(side="top", fill="x", pady=10) # Create 1 scrollable table for all normal numbers (50 numbers) frame = Frame(table_frame, relief="ridge", borderwidth=2) frame.pack(side="top", padx=5) ttk.Label(frame, text="Normal Numbers", font=("Arial", 10, "bold")).grid( row=0, column=0, columnspan=4, pady=5 ) # Column Headers for Normal Numbers headers = ["Number", "Lowest", "Highest", "Last"] for col_idx, header in enumerate(headers): header_label = ttk.Label(frame, text=header, font=("Arial", 10, "bold")) header_label.grid(row=1, column=col_idx, padx=5, pady=5) # Create a canvas with a scrollbar for scrolling the table rows canvas = Canvas(frame, height=115) canvas.grid(row=2, column=0, columnspan=4, padx=5, pady=5) scrollbar = ttk.Scrollbar(frame, orient="vertical", command=canvas.yview) scrollbar.grid(row=2, column=4, sticky="ns") canvas.configure(yscrollcommand=scrollbar.set) canvas_frame = Frame(canvas) canvas.create_window((0, 0), window=canvas_frame, anchor="nw") # Store normal number rows as labels self.normal_number_rows = [] # Add rows for normal numbers (50 total) for num_idx in range(50): row = Frame(canvas_frame) row.grid(row=num_idx, column=0, sticky="w", padx=5, pady=2) # Align the row content labels properly under the column headers ttk.Label(row, text=f"Num {num_idx + 1}", width=16, anchor="w").grid( row=0, column=0, padx=5 ) lowest_label = ttk.Label(row, text="N/A", width=16, anchor="w") lowest_label.grid(row=0, column=1, padx=5) highest_label = ttk.Label(row, text="N/A", width=16, anchor="w") highest_label.grid(row=0, column=2, padx=5) last_label = ttk.Label(row, text="N/A", width=16, anchor="w") last_label.grid(row=0, column=3, padx=5) self.normal_number_rows.append((lowest_label, highest_label, last_label)) # Update canvas scrolling region for normal numbers canvas_frame.update_idletasks() canvas.config(scrollregion=canvas.bbox("all")) # Lucky Numbers Table (below the normal numbers table) frame = Frame(table_frame, relief="ridge", borderwidth=2) frame.pack(side="top", padx=5, pady=10) ttk.Label(frame, text="Lucky Numbers", font=("Arial", 10, "bold")).grid( row=0, column=0, columnspan=4, pady=5 ) # Column Headers for Lucky Numbers for col_idx, header in enumerate(headers): ttk.Label(frame, text=header, font=("Arial", 10, "bold")).grid( row=1, column=col_idx, padx=5, pady=5 ) # Create a canvas with a scrollbar for lucky numbers canvas = Canvas(frame, height=115) canvas.grid(row=2, column=0, columnspan=4, padx=5, pady=5) scrollbar = ttk.Scrollbar(frame, orient="vertical", command=canvas.yview) scrollbar.grid(row=2, column=4, sticky="ns") canvas.configure(yscrollcommand=scrollbar.set) canvas_frame = Frame(canvas) canvas.create_window((0, 0), window=canvas_frame, anchor="nw") # Store lucky number rows as labels self.lucky_number_rows = [] # Add rows for lucky numbers (12 total) for num_idx in range(12): row = Frame(canvas_frame) row.grid(row=num_idx, column=0, sticky="w", padx=5, pady=2) # Align the row content labels properly under the column headers ttk.Label(row, text=f"Lucky {num_idx + 1}", width=16, anchor="w").grid( row=0, column=0, padx=5 ) lowest_label = ttk.Label(row, text="N/A", width=16, anchor="w") lowest_label.grid(row=0, column=1, padx=5) highest_label = ttk.Label(row, text="N/A", width=16, anchor="w") highest_label.grid(row=0, column=2, padx=5) last_label = ttk.Label(row, text="N/A", width=16, anchor="w") last_label.grid(row=0, column=3, padx=5) self.lucky_number_rows.append((lowest_label, highest_label, last_label)) # Update canvas scrolling region for lucky numbers canvas_frame.update_idletasks() canvas.config(scrollregion=canvas.bbox("all")) def update_table(self): """Update the statistics table in the GUI.""" current_time = time.time() # Get the current time in seconds if not hasattr(self, "last_ui_update_time"): self.last_ui_update_time = current_time if current_time - self.last_ui_update_time >= 1: self.last_ui_update_time = current_time # Update the rows for i, (lowest_label, highest_label, last_label) in enumerate( self.normal_number_rows ): lowest_label.config(text=str(int(self.lowest[i]))) # Convert to integer highest_label.config(text=str(int(self.highest[i]))) # Convert to integer last_label.config(text=str(int(self.last[i]))) # Convert to integer # Do the same for lucky numbers (if needed) for i, (lowest_label, highest_label, last_label) in enumerate( self.lucky_number_rows ): lowest_label.config( text=str(int(self.lowest[50 + i])) ) # Convert to integer highest_label.config( text=str(int(self.highest[50 + i])) ) # Convert to integer last_label.config(text=str(int(self.last[50 + i]))) # Convert to integer def run_test(self): """Start the data collection process.""" if self.running: return self.running = True threading.Thread(target=self.collect_data, daemon=True).start() def stop_test(self): """Stop the data collection process.""" self.running = False def collect_data(self): """Collect data from the random bit source.""" try: if self.bit_source == "USB Device": self.ftdi = initialize_ftdi() except RuntimeError as e: print("No FTDI device available:", e) self.running = False return self.start_time = datetime.now() end_time = self.start_time + TEST_TIME last_saved_second = -1 # Track the last saved second while self.running and datetime.now() < end_time: if self.bit_source == "USB Device": bits = read_bits_from_ftdi(self.ftdi, num_bits=BITS_PER_SECOND) else: bits = np.random.choice([0, 1], size=BITS_PER_SECOND).tolist() for bit in bits: self.update_statistics(bit) self.round_robin_index = (self.round_robin_index + 1) % TOTAL_NUMBERS # Check if we have moved to the next full second current_second = int((datetime.now() - self.start_time).total_seconds()) if current_second > last_saved_second: last_saved_second = current_second # Record the sums only for the full second self.timestamps.append(current_second) self.sum_history.append(self.last.copy()) self.update_table() def update_statistics(self, bit): """Update statistics based on the received bit.""" value = 1 if bit == 1 else -1 index = self.round_robin_index self.sums[index] = int(self.sums[index] + value) # Ensure integer sum self.last[index] = int(self.sums[index]) # Ensure last is an integer self.highest[index] = int( max(self.highest[index], self.sums[index]) ) # Ensure highest is an integer self.lowest[index] = int( min(self.lowest[index], self.sums[index]) ) # Ensure lowest is an integer def save_log(self): """Save the collected data to a CSV file.""" if not self.start_time or not self.timestamps: print("No data to save.") return selected_date = self.date_picker.get() formatted_date = datetime.strptime(selected_date, "%Y-%m-%d").strftime( "%Y-%m-%d" ) log_filename = f"EuroMillions_Log_{formatted_date}_{self.start_time.strftime('%Y%m%d_%H%M%S')}.csv" try: with open(log_filename, "w", encoding="utf-8") as log_file: # Added encoding # Write the header log_file.write(f"Correlated Date: {formatted_date}\n") log_file.write("Correlated Time: 21:00-21:10 CET\n") log_file.write("Correlated Place: Paris, France\n") log_file.write(f"Test Start Time: {self.start_time}\n") log_file.write(f"Bit Source: {self.bit_source}\n\n") # Write column headers: First 50 are normal numbers, the last 12 are lucky numbers headers = ( ["Time (s)"] + [f"Num {i+1}" for i in range(50)] + [f"Lucky {i+1}" for i in range(12)] ) log_file.write(", ".join(headers) + "\n") # Write the recorded data for i, timestamp in enumerate(self.timestamps): row = ( [str(timestamp)] + [str(self.sum_history[i][j]) for j in range(50)] + [str(self.sum_history[i][50 + j]) for j in range(12)] ) log_file.write(", ".join(row) + "\n") # Write Lowest, Highest, and Last for each number at the end of the log file log_file.write("\nLowest, Highest, Last Values:\n") for i in range(50): # For normal numbers (1-50) log_file.write( f"Num {i+1}: {int(self.lowest[i])}, {int(self.highest[i])}, {int(self.last[i])}\n" ) for i in range(12): # For lucky numbers (51-62) log_file.write( f"Lucky {i+1}: {int(self.lowest[50 + i])}, {int(self.highest[50 + i])}, {int(self.last[50 + i])}\n" ) print(f"Data exported saved successfully: {log_filename}") except IOError as e: print(f"Error exporting data: {e}") # Main if __name__ == "__main__": root = tk.Tk() root.geometry("725x610") app = EuroMillionsTracker(root) root.mainloop()
Editor is loading...
Leave a Comment