-
Notifications
You must be signed in to change notification settings - Fork 1
/
moon_phase_gui.py
183 lines (150 loc) · 6.46 KB
/
moon_phase_gui.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
"""
Name: moon_phase_gui.py
Author: William A Loring
Created: 07-08-23
Purpose: Python moon phase program using ephem library
"""
import tkinter as tk
from tkinter import ttk
# pip install tkcalendar
from tkcalendar import Calendar
from base64 import b64decode
import moon_class
from moon_icon import moon_16
from moon_icon import moon_32
class MoonPhase:
def __init__(self) -> None:
# Create the main window
self.root = tk.Tk()
self.root.title("Moon Phase")
self.root.geometry("+100+100")
# self.root.iconbitmap("moon.ico")
small_icon = tk.PhotoImage(data=b64decode(moon_16))
large_icon = tk.PhotoImage(data=b64decode(moon_32))
self.root.iconphoto(False, large_icon, small_icon)
self.create_widgets()
# Create moonclass object to access methods and properties
# Default location is lat and lng of Scottsbluff, NE
self.mc = moon_class.MoonClass()
# Create observer
self.mc.get_observer()
# Display moon information based on current time when program starts
self.display_moon_phase()
# Run the main loop
self.root.mainloop()
# ----------------------- CREATE WIDGETS ----------------------------------#
def get_time(self, *args):
# Change date of observation based on selected date
# Get the Python date object from the from the calendar
cal_time = self.cal.selection_get()
# Uncomment the line below for debugging purposes
# print(cal_time)
# Set observer location and current time for moon phase calculation
self.mc.get_observer(cal_time)
self.display_moon_phase()
# ----------------------- DISPLAY MOON PHASE ------------------------------#
def display_moon_phase(self):
"""
Updates moon phase information displayed based on the selected date.
Inputs:
- self: The instance of the MoonPhase class.
Flow:
1. Get the selected date from the calendar widget
2. Try to retrieve the phase description and phase percentage
from the mc object (an instance of the MoonClass class).
3. Update the text of the moon_description_label widget
with the moon phase percentage.
4. Update the text of the moon_phase_label widget with the
moon phase description.
5. If an exception occurs, update the text of the moon_phase_label
widget with the error message.
Outputs:
- None. The method updates the text of the moon_description_label
and moon_phase_label widgets in the GUI.
"""
# Attempt to retrieve moon phase information
try:
# Retrieve moon phase description and percentage illumination
phase_description = self.mc.phase_description
moon_phase = self.mc.illumination
km_to_moon = self.mc.km_to_moon
miles_to_moon = self.mc.miles_to_moon
# Update the GUI label with the moon phase description
self.lbl_moon_phase.config(
text=f"{phase_description}"
)
# Update the GUI label with moon illumination percentage
self.lbl_moon_description.config(
text=f" Illumination: {moon_phase:.0f}%"
)
# Update the GUI label with the moon phase description
self.lbl_km_to_moon.config(
text=f" KM to Moon: {km_to_moon:,.0f}"
)
# Update the GUI label with the moon phase description
self.lbl_miles_to_moon.config(
text=f"Miles to Moon: {miles_to_moon:,.0f}"
)
# Handle exceptions and update label
except Exception as e:
self.lbl_moon_phase.config(text=f"Error: {e}")
# ----------------------- CREATE WIDGETS ----------------------------------#
def create_widgets(self):
"""Create frames"""
self._entry_frame = tk.LabelFrame(
self.root,
text="Choose Date",
relief=tk.GROOVE)
self._main_frame = tk.LabelFrame(
self.root,
text="Calculate Moon Phase",
relief=tk.GROOVE)
self.cal = Calendar(
self._entry_frame,
selectmode="day",
date_pattern="yyyy/mm/dd",
firstweekday="sunday"
)
self.btn_calculate = ttk.Button(
self._main_frame, text="Calculate Moon Phase",
command=self.get_time
)
# Fill the frame to the width of the window
self._entry_frame.pack(fill=tk.X)
self._main_frame.pack(fill=tk.X)
# Keep the frame size regardless of the widget sizes
self._entry_frame.pack_propagate(False)
self._main_frame.pack_propagate(False)
self.lbl_moon_phase = ttk.Label(self._main_frame)
self.lbl_moon_description = ttk.Label(self._main_frame)
self.lbl_km_to_moon = ttk.Label(self._main_frame)
self.lbl_miles_to_moon = ttk.Label(self._main_frame)
self.cal.grid(row=0, column=0)
self.btn_calculate.grid(row=1, column=0, sticky=tk.W)
self.lbl_moon_phase.grid(row=2, column=0, sticky=tk.W)
self.lbl_moon_description.grid(row=3, column=0, sticky=tk.W)
self.lbl_km_to_moon.grid(row=4, column=0, sticky=tk.W)
self.lbl_miles_to_moon.grid(row=5, column=0, sticky=tk.W)
# Set padding between frame and window
self._entry_frame.pack_configure(padx=10)
self._main_frame.pack_configure(padx=10, pady=(10))
for child in self._entry_frame.winfo_children():
child.grid_configure(padx=5, pady=3, ipadx=1, ipady=1)
for child in self._main_frame.winfo_children():
child.grid_configure(padx=5, pady=3, ipadx=1, ipady=1)
# Iterate over each row in the calendar widget
for row in self.cal._calendar:
# Iterate over each label in the current row
for lbl in row:
# Bind the double-click event to the
# display_moon_phase method
lbl.bind("<Double-1>", self.get_time)
# Either enter key will call the method
self.root.bind("<Return>", self.get_time)
self.root.bind("<KP_Enter>", self.get_time)
self.root.bind("<Escape>", self.quit)
# ------------------------- QUIT PROGRAM ----------------------------------#
def quit(self, *args):
self.root.destroy()
# Create program object to start program
moon_phase = MoonPhase()