1# https://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6import vtk
8from pyrocko.guts import Bool, List, String, StringChoice, Float, get_elements
9from pyrocko.gui.qt_compat import qw, qc
10from pyrocko import util
13from .base import Element, ElementState
14from .. import common
16guts_prefix = 'sparrow'
19class HudPositionChoice(StringChoice):
20 choices = ['bottom', 'bottom-left', 'bottom-right',
21 'top', 'top-left', 'top-right']
24class HudState(ElementState):
25 visible = Bool.T(default=True)
26 variables = List.T(String.T(optional=True))
27 template = String.T()
28 position = HudPositionChoice.T(default='bottom')
29 lightness = Float.T(default=1.0)
30 fontsize = Float.T(default=0.05)
32 def create(self):
33 element = HudElement()
34 return element
37def none_or(f):
38 def g(x):
39 if x is None:
40 return ''
41 else:
42 return f(x)
44 return g
47class Stringer(object):
48 def __init__(self, d):
49 self._d = d
50 self._formatters = {
51 'date': none_or(lambda v: util.time_to_str(v, format='%Y-%m-%d')),
52 'datetime': none_or(lambda v: util.time_to_str(v))}
54 def __getitem__(self, key):
55 key = key.split('|', 1)
56 if len(key) == 2:
57 key, formatter = key[0], self._formatters.get(key[1], str)
58 else:
59 key, formatter = key[0], str
61 if key in self._d:
62 return formatter(self._d[key])
63 else:
64 return '{' + key + '}'
67class HudElement(Element):
69 def __init__(self):
70 Element.__init__(self)
71 self._controls = None
72 self._actor = None
73 self._listeners2 = []
75 def get_name(self):
76 return 'HUD'
78 def bind_state(self, state):
79 Element.bind_state(self, state)
81 for var in ['visible', 'lightness', 'fontsize', 'template',
82 'position']:
83 self.register_state_listener3(self.update, state, var)
85 self.register_state_listener3(self.update_bindings, state, 'variables')
87 def unbind_state(self):
88 self._listeners.clear()
89 self._listeners2.clear()
90 self._state = None
92 def set_parent(self, parent):
93 self._parent = parent
94 self._parent.add_panel(
95 self.get_name(),
96 self._get_controls(),
97 visible=True,
98 title_controls=[
99 self.get_title_control_remove(),
100 self.get_title_control_visible()])
102 self.register_state_listener3(
103 self.update, self._parent.gui_state, 'size')
105 self.update_bindings()
106 self.update()
108 def unset_parent(self):
109 self.unbind_state()
110 if self._parent:
111 if self._actor is not None:
112 self._parent.remove_actor_2d(self._actor)
114 if self._controls is not None:
115 self._parent.remove_panel(self._controls)
116 self._controls = None
118 self._parent.update_view()
119 self._parent = None
121 def update_bindings(self, *args):
122 while self._listeners2:
123 listener_ref = self._listeners2.pop()
124 listener_ref.release()
126 for variable in self._state.variables:
127 self._listeners2.append(
128 self._parent.state.add_listener(self.update, variable))
130 def update(self, *args):
131 state = self._state
132 if not self._state:
133 return
134 pstate = self._parent.state
136 if self._actor is None:
137 self._actor = vtk.vtkTextActor()
139 actor = self._actor
141 vs = [
142 get_elements(pstate, variable)[0]
143 for variable in self._state.variables]
145 s = Stringer(d=dict((str(i), v) for (i, v) in enumerate(vs)))
146 actor.SetInput(self._state.template.format_map(s))
148 sx, sy = self._parent.gui_state.size
149 cx = 0.5 * sx
150 # cy = 0.5 * sy
151 off = 0.1 * sy
152 pos = {
153 'top': (cx, sy - off, 1, 2),
154 'top-left': (off, sy - off, 0, 2),
155 'top-right': (sx - off, sy - off, 2, 2),
156 'bottom': (cx, off, 1, 0),
157 'bottom-left': (off, off, 0, 0),
158 'bottom-right': (sx - off, off, 2, 0)}
159 x, y, hj, vj = pos[state.position]
161 actor.SetPosition(x, y)
162 # actor.SetPosition2(200, 100)
163 prop = actor.GetTextProperty()
164 prop.SetFontSize(int(round(state.fontsize*sy)))
166 lightness = state.lightness
167 prop.SetColor(lightness*0.8, lightness*0.8, lightness*0.7)
168 prop.SetJustification(hj)
169 prop.SetVerticalJustification(vj)
171 if state.visible:
172 self._parent.add_actor_2d(actor)
173 else:
174 self._parent.remove_actor_2d(actor)
176 self._parent.update_view()
178 def _get_controls(self):
179 if not self._controls:
180 from ..state import state_bind_lineedit, \
181 state_bind_combobox, state_bind_slider
183 frame = qw.QFrame()
184 layout = qw.QGridLayout()
185 frame.setLayout(layout)
187 layout.addWidget(qw.QLabel('Template'), 0, 0)
188 le = qw.QLineEdit()
189 layout.addWidget(le, 0, 1)
190 state_bind_lineedit(self, self._state, 'template', le)
192 cb = common.string_choices_to_combobox(HudPositionChoice)
193 layout.addWidget(qw.QLabel('Position'), 1, 0)
194 layout.addWidget(cb, 1, 1)
195 state_bind_combobox(self, self._state, 'position', cb)
197 layout.addWidget(qw.QLabel('Lightness'), 2, 0)
199 slider = qw.QSlider(qc.Qt.Horizontal)
200 slider.setSizePolicy(
201 qw.QSizePolicy(
202 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed))
203 slider.setMinimum(0)
204 slider.setMaximum(1000)
205 layout.addWidget(slider, 2, 1)
207 state_bind_slider(
208 self, self._state, 'lightness', slider, factor=0.001)
210 layout.addWidget(qw.QLabel('Fontsize'), 3, 0)
212 slider = qw.QSlider(qc.Qt.Horizontal)
213 slider.setSizePolicy(
214 qw.QSizePolicy(
215 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed))
216 slider.setMinimum(0)
217 slider.setMaximum(1000)
218 layout.addWidget(slider, 3, 1)
220 state_bind_slider(
221 self, self._state, 'fontsize', slider, factor=0.001)
223 layout.addWidget(qw.QFrame(), 4, 0)
225 self._controls = frame
227 return self._controls
230__all__ = [
231 'HudElement',
232 'HudState']