3642264d6ac8245bc1a04557877a265645d49801
[munter.git] / munter / gui.py
1 # -*- coding: utf-8 -*-
2
3
4 """
5 GUI implementation
6 """
7
8 import wx
9 from . import munter
10
11 class MainFrame(wx.Frame):
12 """
13 The main wx.Frame
14 """
15
16 def __init__(self, *args, **kw):
17 super(MainFrame, self).__init__(*args, **kw)
18
19 self.SetTitle('Munter.py')
20 self.SetSize(600, 400)
21
22 self.props = self.init_props()
23
24 self.pnl = wx.Panel(self)
25
26 st = wx.StaticText(self.pnl, label="Munter.py")
27 font = st.GetFont()
28 font.PointSize += 10
29 font = font.Bold()
30
31 st.SetFont(font)
32
33 sizer = wx.BoxSizer(wx.HORIZONTAL)
34 sizer.Add(st, wx.SizerFlags().Border(wx.TOP|wx.LEFT, 0))
35
36 # text entry fields
37 self.st_distance = wx.StaticText(self.pnl, label="Distance: ")
38 self.te_distance = wx.TextCtrl(self.pnl, value="0", size=(140, -1))
39
40 self.st_elevation = wx.StaticText(self.pnl, label="Elevation: ")
41 self.te_elevation = wx.TextCtrl(self.pnl, value="0", size=(140, -1))
42
43 self.st_fitness = wx.StaticText(self.pnl, label="Fitness: ")
44 rb_fitness_choices = ['slow', 'average', 'fast']
45 rb_fitness_default = 'average'
46 self.rb_fitness = wx.ComboBox(self.pnl, choices=rb_fitness_choices,
47 value=rb_fitness_default, style=wx.CB_READONLY)
48
49 self.st_travel_mode = wx.StaticText(self.pnl, label="Travel Mode: ")
50 rb_travel_mode_choices = ['uphill', 'flat', 'downhill', 'bushwhacking']
51 rb_travel_mode_default = 'uphill'
52 self.rb_travel_mode = wx.ComboBox(self.pnl,
53 choices=rb_travel_mode_choices,
54 value=rb_travel_mode_default, style=wx.CB_READONLY)
55
56 self.st_units = wx.StaticText(self.pnl, label="Units: ")
57 rb_units_choices = ['imperial', 'metric']
58 rb_units_default = 'imperial'
59
60 self.rb_units = []
61 for choice in range(len(rb_units_choices)):
62 label = rb_units_choices[choice]
63 style = wx.RB_GROUP if not choice else 0
64 self.rb_units.append(wx.RadioButton(self.pnl, label=label, style=style))
65
66 # static text
67 self.st_mtc = wx.StaticText(self.pnl, label="",
68 style=wx.ALIGN_CENTRE_HORIZONTAL)
69
70 st_mtc_font = st.GetFont()
71 st_mtc_font.PointSize += 10
72 self.st_mtc.SetFont(st_mtc_font)
73
74 # buttons
75 self.b_reset = wx.Button(self.pnl, wx.NewId(), '&Reset', (-1, -1),
76 wx.DefaultSize)
77
78 # bindings
79 self.pnl.Bind(wx.EVT_TEXT, self.update_distance, self.te_distance)
80 self.pnl.Bind(wx.EVT_TEXT, self.update_elevation, self.te_elevation)
81 self.rb_fitness.Bind(wx.EVT_COMBOBOX, self.update_fitness)
82 self.rb_travel_mode.Bind(wx.EVT_COMBOBOX, self.update_travel_mode)
83 self.b_reset.Bind(wx.EVT_BUTTON, self.reset)
84
85 for cb in self.rb_units:
86 cb.Bind(wx.EVT_RADIOBUTTON, self.update_units)
87
88 # layout
89 b = 5
90 w = 100
91
92 static_line = wx.StaticLine(self.pnl, wx.NewId(), style=wx.LI_HORIZONTAL)
93
94 hsizer_distance = wx.BoxSizer(wx.HORIZONTAL)
95 hsizer_distance.Add(self.st_distance, 0, wx.RIGHT, b)
96 hsizer_distance.Add(self.te_distance, 1, wx.GROW, b)
97 hsizer_distance.SetItemMinSize(self.st_distance, (w, -1))
98
99 hsizer_elevation = wx.BoxSizer(wx.HORIZONTAL)
100 hsizer_elevation.Add(self.st_elevation, 0, wx.RIGHT, b)
101 hsizer_elevation.Add(self.te_elevation, 1, wx.GROW, b)
102 hsizer_elevation.SetItemMinSize(self.st_elevation, (w, -1))
103
104 hsizer_fitness = wx.BoxSizer(wx.HORIZONTAL)
105 hsizer_fitness.Add(self.st_fitness, 0, wx.RIGHT, b)
106 hsizer_fitness.Add(self.rb_fitness, 1, wx.GROW, b)
107 hsizer_fitness.SetItemMinSize(self.st_fitness, (w, -1))
108
109 hsizer_travel_mode = wx.BoxSizer(wx.HORIZONTAL)
110 hsizer_travel_mode.Add(self.st_travel_mode, 0, wx.RIGHT, b)
111 hsizer_travel_mode.Add(self.rb_travel_mode, 1, wx.GROW, b)
112 hsizer_travel_mode.SetItemMinSize(self.st_travel_mode, (w, -1))
113
114 hsizer_units = wx.BoxSizer(wx.HORIZONTAL)
115 hsizer_units.Add(self.st_units, 0, wx.RIGHT, b)
116 for cb in range(len(self.rb_units)):
117 hsizer_units.Add(self.rb_units[cb], cb + 1, wx.GROW, b)
118 hsizer_units.SetItemMinSize(self.st_units, (w, -1))
119
120 hsizer_mtc = wx.BoxSizer(wx.HORIZONTAL)
121 hsizer_mtc.Add(self.st_mtc, 1, wx.GROW, b)
122 hsizer_mtc.SetItemMinSize(self.st_mtc, (w, -1))
123
124 hsizer5 = wx.BoxSizer(wx.HORIZONTAL)
125 hsizer5.Add(self.b_reset, 0)
126
127 b = 5
128 vsizer1 = wx.BoxSizer(wx.VERTICAL)
129 vsizer1.Add(sizer, 0, wx.EXPAND | wx.ALL, b*b)
130 vsizer1.Add(hsizer_distance, 0, wx.EXPAND | wx.ALL, b)
131 vsizer1.Add(hsizer_elevation, 0, wx.EXPAND | wx.ALL, b)
132 vsizer1.Add(hsizer_fitness, 0, wx.EXPAND | wx.ALL, b)
133 vsizer1.Add(hsizer_travel_mode, 0, wx.EXPAND | wx.ALL, b)
134 vsizer1.Add(hsizer_units, 0, wx.EXPAND | wx.ALL, b)
135 vsizer1.Add(hsizer_mtc, 0, wx.EXPAND | wx.ALL, b)
136 vsizer1.Add(static_line, 0, wx.GROW | wx.ALL, b)
137 vsizer1.Add(hsizer5, 0, wx.ALIGN_RIGHT | wx.ALL, b)
138
139 self.pnl.SetSizerAndFit(vsizer1)
140 self.pnl.SetClientSize(vsizer1.GetSize())
141 self.update_mtc()
142
143 def init_props(self):
144 props = dict()
145 props['distance'] = 0
146 props['elevation'] = 0
147 props['fitness'] = 'average'
148 props['units'] = 'imperial'
149 props['travel_mode'] = 'uphill'
150 return props
151
152 def update_distance(self, event):
153 value = self.te_distance.GetValue()
154 if value:
155 try:
156 new_val = float(value)
157 self.props['distance'] = new_val
158 except:
159 # reset GUI to last-accepted val
160 self.te_distance.SetValue(str(self.props['distance']))
161 pass
162 self.update_mtc()
163
164 def update_elevation(self, event):
165 value = self.te_elevation.GetValue()
166 if value:
167 try:
168 new_val = int(value)
169 self.props['elevation'] = new_val
170 except:
171 # reset GUI to last-accepted val
172 self.te_elevation.SetValue(str(self.props['elevation']))
173 pass
174 self.update_mtc()
175
176 def update_fitness(self, event):
177 value = self.rb_fitness.GetValue()
178 if value:
179 self.props['fitness'] = value
180 self.update_mtc()
181
182 def update_travel_mode(self, event):
183 value = self.rb_travel_mode.GetValue()
184 if value:
185 self.props['travel_mode'] = value
186 self.update_mtc()
187
188 def update_units(self, event):
189 rb = event.GetEventObject()
190 value = rb.GetLabel()
191 if value:
192 self.props['units'] = value
193 self.update_mtc()
194
195 def update_mtc(self):
196 if (self.props['distance'] is None) or (self.props['elevation'] is None):
197 return
198
199 est = munter.time_calc(self.props['distance'],
200 self.props['elevation'],
201 self.props['fitness'],
202 self.props['travel_mode'],
203 self.props['units'])
204
205 hours = int(est['time'])
206 minutes = int((est['time'] - hours) * 60)
207 self.st_mtc.SetLabel("{human_time}".format(
208 human_time="{hours} hours {minutes} minutes".format(
209 hours=hours,
210 minutes=minutes)))
211
212 self.pnl.Layout()
213
214 def reset(self, event):
215 self.props = self.init_props()
216 self.te_distance.SetValue(str(self.props['distance']))
217 self.te_elevation.SetValue(str(self.props['elevation']))
218 self.rb_fitness.SetValue(str(self.props['fitness']))
219 self.rb_travel_mode.SetValue(str(self.props['travel_mode']))
220 # leave units as the user selected
221 self.update_mtc()
222
223 def startup():
224 app = wx.App()
225 frm = MainFrame(None)
226 frm.Show()
227
228 app.MainLoop()