1# https://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

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

5 

6from __future__ import absolute_import, print_function, division 

7 

8import copy 

9 

10import math 

11import numpy as num 

12from scipy.interpolate import interp1d 

13 

14from pyrocko import automap, plot, util 

15from pyrocko.geometry import d2r 

16from pyrocko.gui.qt_compat import qg, qw, qc 

17from pyrocko.gui.util import tmin_effective, tmax_effective, get_app # noqa 

18 

19 

20def get_err_palette(): 

21 err_palette = qg.QPalette() 

22 err_palette.setColor(qg.QPalette.Text, qg.QColor(255, 200, 200)) 

23 return err_palette 

24 

25 

26def get_palette(): 

27 return qw.QApplication.palette() 

28 

29 

30def errorize(widget): 

31 widget.setStyleSheet(''' 

32 QLineEdit { 

33 background: rgb(200, 150, 150); 

34 }''') 

35 

36 

37def de_errorize(widget): 

38 if isinstance(widget, qw.QWidget): 

39 widget.setStyleSheet('') 

40 

41 

42def strings_to_combobox(list_of_str): 

43 cb = qw.QComboBox() 

44 for i, s in enumerate(list_of_str): 

45 cb.insertItem(i, s) 

46 

47 return cb 

48 

49 

50def string_choices_to_combobox(cls): 

51 return strings_to_combobox(cls.choices) 

52 

53 

54def time_or_none_to_str(t): 

55 if t is None: 

56 return '' 

57 else: 

58 return util.time_to_str(t) 

59 

60 

61def cover_region(lat, lon, delta, step=None, avoid_poles=False): 

62 if step is None: 

63 step = plot.nice_value(delta / 10.) 

64 

65 assert step <= 20. 

66 

67 def fl_major(x): 

68 return math.floor(x / step) * step 

69 

70 def ce_major(x): 

71 return math.ceil(x / step) * step 

72 

73 if avoid_poles: 

74 lat_min_lim = -90. + step 

75 lat_max_lim = 90. - step 

76 else: 

77 lat_min_lim = -90. 

78 lat_max_lim = 90. 

79 

80 lat_min = max(lat_min_lim, fl_major(lat - delta)) 

81 lat_max = min(lat_max_lim, ce_major(lat + delta)) 

82 

83 lon_closed = False 

84 if abs(lat)+delta < 89.: 

85 factor = 1.0 / math.cos((abs(lat)+delta) * d2r) 

86 lon_min = fl_major(lon - delta * factor) 

87 lon_max = ce_major(lon + delta * factor) 

88 if lon_max >= lon_min + 360. - step*1e-5: 

89 lon_min, lon_max = -180., 180. - step 

90 lon_closed = True 

91 else: 

92 lon_min, lon_max = -180., 180. - step 

93 lon_closed = True 

94 

95 return lat_min, lat_max, lon_min, lon_max, lon_closed 

96 

97 

98qfiledialog_options = qw.QFileDialog.DontUseNativeDialog | \ 

99 qw.QFileDialog.DontUseSheet 

100 

101 

102def _paint_cpt_rect(painter, cpt, rect): 

103 rect.adjust(+5, +2, -5, -2) 

104 

105 rect_cpt = copy.deepcopy(rect) 

106 rect_cpt.setWidth(int(rect.width() * 0.9) - 2) 

107 

108 rect_c_nan = copy.deepcopy(rect) 

109 rect_c_nan.setLeft(rect.left() + rect_cpt.width() + 4) 

110 rect_c_nan.setWidth(int(rect.width() * 0.1) - 2) 

111 

112 levels = num.zeros(len(cpt.levels) * 2 + 4) 

113 colors = num.ones((levels.shape[0], 4)) * 255 

114 

115 for il, level in enumerate(cpt.levels): 

116 levels[il*2+2] = level.vmin + ( 

117 level.vmax - level.vmin) / rect_cpt.width() # ow interp errors 

118 levels[il*2+3] = level.vmax 

119 

120 colors[il*2+2, :3] = level.color_min 

121 colors[il*2+3, :3] = level.color_max 

122 

123 level_range = levels[-3] - levels[2] 

124 levels[0], levels[1] = levels[2] - level_range * 0.05, levels[2] 

125 levels[-2], levels[-1] = levels[-3], levels[-3] + level_range * 0.05 

126 

127 if cpt.color_below: 

128 colors[:2, :3] = cpt.color_below 

129 else: 

130 colors[:2] = (0, 0, 0, 0) 

131 

132 if cpt.color_above: 

133 colors[-2:, :3] = cpt.color_above 

134 else: 

135 colors[-2:] = (0, 0, 0, 0) 

136 

137 levels_interp = num.linspace(levels[0], levels[-1], rect_cpt.width()) 

138 interpolator = interp1d(levels, colors.T) 

139 

140 colors_interp = interpolator( 

141 levels_interp).T.astype(num.uint8).tobytes() 

142 

143 colors_interp = num.tile( 

144 colors_interp, rect_cpt.height()) 

145 

146 img = qg.QImage( 

147 colors_interp, rect_cpt.width(), rect_cpt.height(), 

148 qg.QImage.Format_RGBA8888) 

149 

150 painter.drawImage(rect_cpt, img) 

151 

152 c = cpt.color_nan 

153 qcolor_nan = qg.QColor(*c if c is not None else (0, 0, 0)) 

154 qcolor_nan.setAlpha(255 if c is not None else 0) 

155 

156 painter.fillRect(rect_c_nan, qcolor_nan) 

157 

158 

159class CPTStyleDelegate(qw.QItemDelegate): 

160 

161 def __init__(self, parent=None): 

162 qw.QItemDelegate.__init__(self, parent) 

163 

164 def paint(self, painter, option, index): 

165 data = index.model().data(index, qc.Qt.UserRole) 

166 

167 if isinstance(data, automap.CPT): 

168 painter.save() 

169 rect = option.rect 

170 _paint_cpt_rect(painter, data, rect) 

171 painter.restore() 

172 

173 else: 

174 qw.QItemDelegate.paint(self, painter, option, index) 

175 

176 

177class CPTComboBox(qw.QComboBox): 

178 def __init__(self): 

179 super().__init__() 

180 

181 self.setItemDelegate(CPTStyleDelegate(parent=self)) 

182 self.setInsertPolicy(qw.QComboBox.InsertAtBottom) 

183 

184 def paintEvent(self, e): 

185 data = self.itemData(self.currentIndex(), qc.Qt.UserRole) 

186 

187 if isinstance(data, automap.CPT): 

188 spainter = qw.QStylePainter(self) 

189 spainter.setPen(self.palette().color(qg.QPalette.Text)) 

190 

191 opt = qw.QStyleOptionComboBox() 

192 self.initStyleOption(opt) 

193 spainter.drawComplexControl(qw.QStyle.CC_ComboBox, opt) 

194 

195 painter = qg.QPainter(self) 

196 painter.save() 

197 

198 rect = spainter.style().subElementRect( 

199 qw.QStyle.SE_ComboBoxFocusRect, opt, self) 

200 

201 _paint_cpt_rect(painter, data, rect) 

202 

203 painter.restore() 

204 

205 else: 

206 qw.QComboBox.paintEvent(self, e) 

207 

208 

209class MyDockWidgetTitleBarButton(qw.QPushButton): 

210 

211 def __init__(self, *args, **kwargs): 

212 qw.QPushButton.__init__(self, *args, **kwargs) 

213 self.setFlat(True) 

214 self.setSizePolicy( 

215 qw.QSizePolicy.Fixed, qw.QSizePolicy.Fixed) 

216 

217 def sizeHint(self): 

218 s = qw.QPushButton.sizeHint(self) 

219 return qc.QSize(s.height(), s.height()) 

220 

221 

222class MyDockWidgetTitleBarButtonToggle(MyDockWidgetTitleBarButton): 

223 

224 toggled = qc.pyqtSignal(bool) 

225 

226 def __init__(self, text_checked, text_unchecked, *args, **kwargs): 

227 MyDockWidgetTitleBarButton.__init__( 

228 self, text_checked, *args, **kwargs) 

229 

230 self._checked = True 

231 self._text_checked = text_checked 

232 self._text_unchecked = text_unchecked 

233 self.update_text() 

234 self.clicked.connect(self.toggle) 

235 

236 def set_checked(self, checked): 

237 self._checked = checked 

238 self.update_text() 

239 

240 def toggle(self): 

241 self._checked = not self._checked 

242 self.update_text() 

243 self.toggled.emit(self._checked) 

244 

245 def update_text(self): 

246 if self._checked: 

247 self.setText(self._text_checked) 

248 else: 

249 self.setText(self._text_unchecked) 

250 

251 

252class MyDockWidgetTitleBarLabel(qw.QLabel): 

253 

254 def event(self, ev): 

255 ev.ignore() 

256 return qw.QLabel.event(self, ev) 

257 

258 

259class MyDockWidgetTitleBar(qw.QFrame): 

260 

261 def __init__(self, title, title_controls=[]): 

262 qw.QFrame.__init__(self) 

263 

264 lab = MyDockWidgetTitleBarLabel('<strong>%s</strong>' % title) 

265 lab.setSizePolicy( 

266 qw.QSizePolicy.Expanding, qw.QSizePolicy.Minimum) 

267 

268 button_hide = MyDockWidgetTitleBarButton('-') 

269 button_hide.setStatusTip('Hide Panel') 

270 

271 layout = qw.QGridLayout() 

272 layout.setSpacing(0) 

273 layout.addWidget(lab, 0, 0) 

274 layout.addWidget(button_hide, 0, 1) 

275 for i, button in enumerate(title_controls): 

276 layout.addWidget(button, 0, 2 + i) 

277 

278 self.setLayout(layout) 

279 self.setBackgroundRole(qg.QPalette.Mid) 

280 self.setAutoFillBackground(True) 

281 self.button_hide = button_hide 

282 

283 def event(self, ev): 

284 ev.ignore() 

285 return qw.QFrame.event(self, ev) 

286 

287 

288class MyDockWidget(qw.QDockWidget): 

289 

290 def __init__(self, name, parent, title_controls=[], **kwargs): 

291 qw.QDockWidget.__init__(self, name, parent, **kwargs) 

292 

293 self.setFeatures( 

294 qw.QDockWidget.DockWidgetClosable 

295 | qw.QDockWidget.DockWidgetMovable 

296 | qw.QDockWidget.DockWidgetFloatable 

297 | qw.QDockWidget.DockWidgetClosable) 

298 

299 self._visible = False 

300 self._blocked = False 

301 

302 tb = MyDockWidgetTitleBar(name, title_controls) 

303 tb.button_hide.clicked.connect(self.hide) 

304 self.setTitleBarWidget(tb) 

305 self.titlebar = tb 

306 

307 def setVisible(self, visible): 

308 self._visible = visible 

309 if not self._blocked: 

310 qw.QDockWidget.setVisible(self, self._visible) 

311 

312 def show(self): 

313 self.setVisible(True) 

314 

315 def hide(self): 

316 self.setVisible(False) 

317 

318 def setBlocked(self, blocked): 

319 self._blocked = blocked 

320 if blocked: 

321 qw.QDockWidget.setVisible(self, False) 

322 else: 

323 qw.QDockWidget.setVisible(self, self._visible) 

324 

325 def block(self): 

326 self.setBlocked(True) 

327 

328 def unblock(self): 

329 self.setBlocked(False) 

330 

331 

332class MyDoubleSpinBox(qw.QDoubleSpinBox): 

333 def __init__(self, *args, **kwargs): 

334 qw.QDoubleSpinBox.__init__(self, *args, **kwargs) 

335 

336 self.setLocale(qc.QLocale(' English'))