1# https://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

4# ---|P------/S----------~Lg---------- 

5 

6import vtk 

7 

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 

12 

13 

14from .base import Element, ElementState 

15from .. import common 

16 

17guts_prefix = 'sparrow' 

18 

19 

20class HudPositionChoice(StringChoice): 

21 choices = ['bottom', 'bottom-left', 'bottom-right', 

22 'top', 'top-left', 'top-right'] 

23 

24 

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) 

32 

33 def create(self): 

34 element = HudElement() 

35 return element 

36 

37 

38def none_or(f): 

39 def g(x): 

40 if x is None: 

41 return '' 

42 else: 

43 return f(x) 

44 

45 return g 

46 

47 

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))} 

54 

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 

61 

62 if key in self._d: 

63 return formatter(self._d[key]) 

64 else: 

65 return '{' + key + '}' 

66 

67 

68class HudElement(Element): 

69 

70 def __init__(self): 

71 Element.__init__(self) 

72 self._controls = None 

73 self._actor = None 

74 self._connections2 = TalkieConnectionOwner() 

75 

76 def get_name(self): 

77 return 'HUD' 

78 

79 def bind_state(self, state): 

80 Element.bind_state(self, state) 

81 

82 self.talkie_connect( 

83 state, 

84 ['visible', 'lightness', 'fontsize', 'template', 'position'], 

85 self.update) 

86 

87 self.talkie_connect(state, 'variables', self.update_bindings) 

88 

89 def unbind_state(self): 

90 self._connections2.talkie_disconnect_all() 

91 Element.unbind_state(self) 

92 

93 def set_parent(self, parent): 

94 self._parent = parent 

95 self._parent.add_panel( 

96 self.get_title_label(), 

97 self._get_controls(), 

98 visible=True, 

99 title_controls=[ 

100 self.get_title_control_remove(), 

101 self.get_title_control_visible()]) 

102 

103 self.talkie_connect( 

104 self._parent.gui_state, 'size', self.update) 

105 

106 self.update_bindings() 

107 self.update() 

108 

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) 

114 

115 if self._controls is not None: 

116 self._parent.remove_panel(self._controls) 

117 self._controls = None 

118 

119 self._parent.update_view() 

120 self._parent = None 

121 

122 def update_bindings(self, *args): 

123 self._connections2.talkie_disconnect_all() 

124 

125 self._connections2.talkie_connect( 

126 self._parent.state, self._state.variables, self.update) 

127 

128 def update(self, *args): 

129 state = self._state 

130 if not self._state: 

131 return 

132 pstate = self._parent.state 

133 

134 if self._actor is None: 

135 self._actor = vtk.vtkTextActor() 

136 

137 actor = self._actor 

138 

139 vs = [ 

140 get_elements(pstate, variable)[0] 

141 for variable in self._state.variables] 

142 

143 s = Stringer(d=dict((str(i), v) for (i, v) in enumerate(vs))) 

144 actor.SetInput(self._state.template.format_map(s)) 

145 

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] 

158 

159 actor.SetPosition(x, y) 

160 # actor.SetPosition2(200, 100) 

161 prop = actor.GetTextProperty() 

162 prop.SetFontSize(int(round(state.fontsize*sy))) 

163 

164 lightness = state.lightness 

165 prop.SetColor(lightness*0.8, lightness*0.8, lightness*0.7) 

166 prop.SetJustification(hj) 

167 prop.SetVerticalJustification(vj) 

168 

169 if state.visible: 

170 self._parent.add_actor_2d(actor) 

171 else: 

172 self._parent.remove_actor_2d(actor) 

173 

174 self._parent.update_view() 

175 

176 def _get_controls(self): 

177 if not self._controls: 

178 from ..state import state_bind_lineedit, \ 

179 state_bind_combobox, state_bind_slider 

180 

181 frame = qw.QFrame() 

182 layout = qw.QGridLayout() 

183 frame.setLayout(layout) 

184 

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) 

189 

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) 

194 

195 layout.addWidget(qw.QLabel('Lightness'), 2, 0) 

196 

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) 

204 

205 state_bind_slider( 

206 self, self._state, 'lightness', slider, factor=0.001) 

207 

208 layout.addWidget(qw.QLabel('Fontsize'), 3, 0) 

209 

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) 

217 

218 state_bind_slider( 

219 self, self._state, 'fontsize', slider, factor=0.001) 

220 

221 layout.addWidget(qw.QFrame(), 4, 0) 

222 

223 self._controls = frame 

224 

225 return self._controls 

226 

227 

228__all__ = [ 

229 'HudElement', 

230 'HudState']