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