1# https://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6from __future__ import absolute_import, print_function, division
8import vtk
10from pyrocko.guts import Bool, List, String, StringChoice, Float, get_elements
11from pyrocko.gui.qt_compat import qw, qc
12from pyrocko import util
15from .base import Element, ElementState
16from .. import common
18guts_prefix = 'sparrow'
21class HudPositionChoice(StringChoice):
22 choices = ['bottom', 'bottom-left', 'bottom-right',
23 'top', 'top-left', 'top-right']
26class HudState(ElementState):
27 visible = Bool.T(default=True)
28 variables = List.T(String.T(optional=True))
29 template = String.T()
30 position = HudPositionChoice.T(default='bottom')
31 lightness = Float.T(default=1.0)
32 fontsize = Float.T(default=0.05)
34 def create(self):
35 element = HudElement()
36 return element
39def none_or(f):
40 def g(x):
41 if x is None:
42 return ''
43 else:
44 return f(x)
46 return g
49class Stringer(object):
50 def __init__(self, d):
51 self._d = d
52 self._formatters = {
53 'date': none_or(lambda v: util.time_to_str(v, format='%Y-%m-%d')),
54 'datetime': none_or(lambda v: util.time_to_str(v))}
56 def __getitem__(self, key):
57 key = key.split('|', 1)
58 if len(key) == 2:
59 key, formatter = key[0], self._formatters.get(key[1], str)
60 else:
61 key, formatter = key[0], str
63 if key in self._d:
64 return formatter(self._d[key])
65 else:
66 return '{' + key + '}'
69class HudElement(Element):
71 def __init__(self):
72 Element.__init__(self)
73 self._controls = None
74 self._actor = None
75 self._listeners2 = []
77 def get_name(self):
78 return 'HUD'
80 def bind_state(self, state):
81 Element.bind_state(self, state)
83 for var in ['visible', 'lightness', 'fontsize', 'template',
84 'position']:
85 self.register_state_listener3(self.update, state, var)
87 self.register_state_listener3(self.update_bindings, state, 'variables')
89 def unbind_state(self):
90 self._listeners.clear()
91 self._listeners2.clear()
92 self._state = None
94 def set_parent(self, parent):
95 self._parent = parent
96 self._parent.add_panel(
97 self.get_name(),
98 self._get_controls(),
99 visible=True,
100 title_controls=[
101 self.get_title_control_remove(),
102 self.get_title_control_visible()])
104 self.register_state_listener3(
105 self.update, self._parent.gui_state, 'size')
107 self.update_bindings()
108 self.update()
110 def unset_parent(self):
111 self.unbind_state()
112 if self._parent:
113 if self._actor is not None:
114 self._parent.remove_actor_2d(self._actor)
116 if self._controls is not None:
117 self._parent.remove_panel(self._controls)
118 self._controls = None
120 self._parent.update_view()
121 self._parent = None
123 def update_bindings(self, *args):
124 while self._listeners2:
125 listener_ref = self._listeners2.pop()
126 listener_ref.release()
128 for variable in self._state.variables:
129 self._listeners2.append(
130 self._parent.state.add_listener(self.update, variable))
132 def update(self, *args):
133 state = self._state
134 if not self._state:
135 return
136 pstate = self._parent.state
138 if self._actor is None:
139 self._actor = vtk.vtkTextActor()
141 actor = self._actor
143 vs = [
144 get_elements(pstate, variable)[0]
145 for variable in self._state.variables]
147 s = Stringer(d=dict((str(i), v) for (i, v) in enumerate(vs)))
148 actor.SetInput(self._state.template.format_map(s))
150 sx, sy = self._parent.gui_state.size
151 cx = 0.5 * sx
152 # cy = 0.5 * sy
153 off = 0.1 * sy
154 pos = {
155 'top': (cx, sy - off, 1, 2),
156 'top-left': (off, sy - off, 0, 2),
157 'top-right': (sx - off, sy - off, 2, 2),
158 'bottom': (cx, off, 1, 0),
159 'bottom-left': (off, off, 0, 0),
160 'bottom-right': (sx - off, off, 2, 0)}
161 x, y, hj, vj = pos[state.position]
163 actor.SetPosition(x, y)
164 # actor.SetPosition2(200, 100)
165 prop = actor.GetTextProperty()
166 prop.SetFontSize(int(round(state.fontsize*sy)))
168 lightness = state.lightness
169 prop.SetColor(lightness*0.8, lightness*0.8, lightness*0.7)
170 prop.SetJustification(hj)
171 prop.SetVerticalJustification(vj)
173 if state.visible:
174 self._parent.add_actor_2d(actor)
175 else:
176 self._parent.remove_actor_2d(actor)
178 self._parent.update_view()
180 def _get_controls(self):
181 if not self._controls:
182 from ..state import state_bind_lineedit, \
183 state_bind_combobox, state_bind_slider
185 frame = qw.QFrame()
186 layout = qw.QGridLayout()
187 frame.setLayout(layout)
189 layout.addWidget(qw.QLabel('Template'), 0, 0)
190 le = qw.QLineEdit()
191 layout.addWidget(le, 0, 1)
192 state_bind_lineedit(self, self._state, 'template', le)
194 cb = common.string_choices_to_combobox(HudPositionChoice)
195 layout.addWidget(qw.QLabel('Position'), 1, 0)
196 layout.addWidget(cb, 1, 1)
197 state_bind_combobox(self, self._state, 'position', cb)
199 layout.addWidget(qw.QLabel('Lightness'), 2, 0)
201 slider = qw.QSlider(qc.Qt.Horizontal)
202 slider.setSizePolicy(
203 qw.QSizePolicy(
204 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed))
205 slider.setMinimum(0)
206 slider.setMaximum(1000)
207 layout.addWidget(slider, 2, 1)
209 state_bind_slider(
210 self, self._state, 'lightness', slider, factor=0.001)
212 layout.addWidget(qw.QLabel('Fontsize'), 3, 0)
214 slider = qw.QSlider(qc.Qt.Horizontal)
215 slider.setSizePolicy(
216 qw.QSizePolicy(
217 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed))
218 slider.setMinimum(0)
219 slider.setMaximum(1000)
220 layout.addWidget(slider, 3, 1)
222 state_bind_slider(
223 self, self._state, 'fontsize', slider, factor=0.001)
225 layout.addWidget(qw.QFrame(), 4, 0)
227 self._controls = frame
229 return self._controls
232__all__ = [
233 'HudElement',
234 'HudState']