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.gui.talkie import TalkieConnectionOwner
11from pyrocko import util
14from .base import Element, ElementState
15from .. import common
17guts_prefix = 'sparrow'
20class HudPositionChoice(StringChoice):
21 choices = ['bottom', 'bottom-left', 'bottom-right',
22 'top', 'top-left', 'top-right']
25class HudState(ElementState):
26 visible = Bool.T(default=True)
27 variables = List.T(String.T(optional=True))
28 template = String.T()
29 position = HudPositionChoice.T(default='bottom')
30 lightness = Float.T(default=1.0)
31 fontsize = Float.T(default=0.05)
33 def create(self):
34 element = HudElement()
35 return element
38def none_or(f):
39 def g(x):
40 if x is None:
41 return ''
42 else:
43 return f(x)
45 return g
48class Stringer(object):
49 def __init__(self, d):
50 self._d = d
51 self._formatters = {
52 'date': none_or(lambda v: util.time_to_str(v, format='%Y-%m-%d')),
53 'datetime': none_or(lambda v: util.time_to_str(v))}
55 def __getitem__(self, key):
56 key = key.split('|', 1)
57 if len(key) == 2:
58 key, formatter = key[0], self._formatters.get(key[1], str)
59 else:
60 key, formatter = key[0], str
62 if key in self._d:
63 return formatter(self._d[key])
64 else:
65 return '{' + key + '}'
68class HudElement(Element):
70 def __init__(self):
71 Element.__init__(self)
72 self._controls = None
73 self._actor = None
74 self._connections2 = TalkieConnectionOwner()
76 def get_name(self):
77 return 'HUD'
79 def bind_state(self, state):
80 Element.bind_state(self, state)
82 self.talkie_connect(
83 state,
84 ['visible', 'lightness', 'fontsize', 'template', 'position'],
85 self.update)
87 self.talkie_connect(state, 'variables', self.update_bindings)
89 def unbind_state(self):
90 self._connections2.talkie_disconnect_all()
91 Element.unbind_state(self)
93 def set_parent(self, parent):
94 self._parent = parent
95 self._parent.add_panel(
96 self.get_name(),
97 self._get_controls(),
98 visible=True,
99 title_controls=[
100 self.get_title_control_remove(),
101 self.get_title_control_visible()])
103 self.talkie_connect(
104 self._parent.gui_state, 'size', self.update)
106 self.update_bindings()
107 self.update()
109 def unset_parent(self):
110 self.unbind_state()
111 if self._parent:
112 if self._actor is not None:
113 self._parent.remove_actor_2d(self._actor)
115 if self._controls is not None:
116 self._parent.remove_panel(self._controls)
117 self._controls = None
119 self._parent.update_view()
120 self._parent = None
122 def update_bindings(self, *args):
123 self._connections2.talkie_disconnect_all()
125 self._connections2.talkie_connect(
126 self._parent.state, self._state.variables, self.update)
128 def update(self, *args):
129 state = self._state
130 if not self._state:
131 return
132 pstate = self._parent.state
134 if self._actor is None:
135 self._actor = vtk.vtkTextActor()
137 actor = self._actor
139 vs = [
140 get_elements(pstate, variable)[0]
141 for variable in self._state.variables]
143 s = Stringer(d=dict((str(i), v) for (i, v) in enumerate(vs)))
144 actor.SetInput(self._state.template.format_map(s))
146 sx, sy = self._parent.gui_state.size
147 cx = 0.5 * sx
148 # cy = 0.5 * sy
149 off = 0.1 * sy
150 pos = {
151 'top': (cx, sy - off, 1, 2),
152 'top-left': (off, sy - off, 0, 2),
153 'top-right': (sx - off, sy - off, 2, 2),
154 'bottom': (cx, off, 1, 0),
155 'bottom-left': (off, off, 0, 0),
156 'bottom-right': (sx - off, off, 2, 0)}
157 x, y, hj, vj = pos[state.position]
159 actor.SetPosition(x, y)
160 # actor.SetPosition2(200, 100)
161 prop = actor.GetTextProperty()
162 prop.SetFontSize(int(round(state.fontsize*sy)))
164 lightness = state.lightness
165 prop.SetColor(lightness*0.8, lightness*0.8, lightness*0.7)
166 prop.SetJustification(hj)
167 prop.SetVerticalJustification(vj)
169 if state.visible:
170 self._parent.add_actor_2d(actor)
171 else:
172 self._parent.remove_actor_2d(actor)
174 self._parent.update_view()
176 def _get_controls(self):
177 if not self._controls:
178 from ..state import state_bind_lineedit, \
179 state_bind_combobox, state_bind_slider
181 frame = qw.QFrame()
182 layout = qw.QGridLayout()
183 frame.setLayout(layout)
185 layout.addWidget(qw.QLabel('Template'), 0, 0)
186 le = qw.QLineEdit()
187 layout.addWidget(le, 0, 1)
188 state_bind_lineedit(self, self._state, 'template', le)
190 cb = common.string_choices_to_combobox(HudPositionChoice)
191 layout.addWidget(qw.QLabel('Position'), 1, 0)
192 layout.addWidget(cb, 1, 1)
193 state_bind_combobox(self, self._state, 'position', cb)
195 layout.addWidget(qw.QLabel('Lightness'), 2, 0)
197 slider = qw.QSlider(qc.Qt.Horizontal)
198 slider.setSizePolicy(
199 qw.QSizePolicy(
200 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed))
201 slider.setMinimum(0)
202 slider.setMaximum(1000)
203 layout.addWidget(slider, 2, 1)
205 state_bind_slider(
206 self, self._state, 'lightness', slider, factor=0.001)
208 layout.addWidget(qw.QLabel('Fontsize'), 3, 0)
210 slider = qw.QSlider(qc.Qt.Horizontal)
211 slider.setSizePolicy(
212 qw.QSizePolicy(
213 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed))
214 slider.setMinimum(0)
215 slider.setMaximum(1000)
216 layout.addWidget(slider, 3, 1)
218 state_bind_slider(
219 self, self._state, 'fontsize', slider, factor=0.001)
221 layout.addWidget(qw.QFrame(), 4, 0)
223 self._controls = frame
225 return self._controls
228__all__ = [
229 'HudElement',
230 'HudState']