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 copy
10import math
11import numpy as num
12from scipy.interpolate import interp1d
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
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
26def get_palette():
27 return qw.QApplication.palette()
30def errorize(widget):
31 widget.setStyleSheet('''
32 QLineEdit {
33 background: rgb(200, 150, 150);
34 }''')
37def de_errorize(widget):
38 if isinstance(widget, qw.QWidget):
39 widget.setStyleSheet('')
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)
47 return cb
50def string_choices_to_combobox(cls):
51 return strings_to_combobox(cls.choices)
54def time_or_none_to_str(t):
55 if t is None:
56 return ''
57 else:
58 return util.time_to_str(t)
61def cover_region(lat, lon, delta, step=None, avoid_poles=False):
62 if step is None:
63 step = plot.nice_value(delta / 10.)
65 assert step <= 20.
67 def fl_major(x):
68 return math.floor(x / step) * step
70 def ce_major(x):
71 return math.ceil(x / step) * step
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.
80 lat_min = max(lat_min_lim, fl_major(lat - delta))
81 lat_max = min(lat_max_lim, ce_major(lat + delta))
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
95 return lat_min, lat_max, lon_min, lon_max, lon_closed
98qfiledialog_options = qw.QFileDialog.DontUseNativeDialog | \
99 qw.QFileDialog.DontUseSheet
102def _paint_cpt_rect(painter, cpt, rect):
103 rect.adjust(+5, +2, -5, -2)
105 rect_cpt = copy.deepcopy(rect)
106 rect_cpt.setWidth(int(rect.width() * 0.9) - 2)
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)
112 levels = num.zeros(len(cpt.levels) * 2 + 4)
113 colors = num.ones((levels.shape[0], 4)) * 255
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
120 colors[il*2+2, :3] = level.color_min
121 colors[il*2+3, :3] = level.color_max
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
127 if cpt.color_below:
128 colors[:2, :3] = cpt.color_below
129 else:
130 colors[:2] = (0, 0, 0, 0)
132 if cpt.color_above:
133 colors[-2:, :3] = cpt.color_above
134 else:
135 colors[-2:] = (0, 0, 0, 0)
137 levels_interp = num.linspace(levels[0], levels[-1], rect_cpt.width())
138 interpolator = interp1d(levels, colors.T)
140 colors_interp = interpolator(
141 levels_interp).T.astype(num.uint8).tobytes()
143 colors_interp = num.tile(
144 colors_interp, rect_cpt.height())
146 img = qg.QImage(
147 colors_interp, rect_cpt.width(), rect_cpt.height(),
148 qg.QImage.Format_RGBA8888)
150 painter.drawImage(rect_cpt, img)
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)
156 painter.fillRect(rect_c_nan, qcolor_nan)
159class CPTStyleDelegate(qw.QItemDelegate):
161 def __init__(self, parent=None):
162 qw.QItemDelegate.__init__(self, parent)
164 def paint(self, painter, option, index):
165 data = index.model().data(index, qc.Qt.UserRole)
167 if isinstance(data, automap.CPT):
168 painter.save()
169 rect = option.rect
170 _paint_cpt_rect(painter, data, rect)
171 painter.restore()
173 else:
174 qw.QItemDelegate.paint(self, painter, option, index)
177class CPTComboBox(qw.QComboBox):
178 def __init__(self):
179 super().__init__()
181 self.setItemDelegate(CPTStyleDelegate(parent=self))
182 self.setInsertPolicy(qw.QComboBox.InsertAtBottom)
184 def paintEvent(self, e):
185 data = self.itemData(self.currentIndex(), qc.Qt.UserRole)
187 if isinstance(data, automap.CPT):
188 spainter = qw.QStylePainter(self)
189 spainter.setPen(self.palette().color(qg.QPalette.Text))
191 opt = qw.QStyleOptionComboBox()
192 self.initStyleOption(opt)
193 spainter.drawComplexControl(qw.QStyle.CC_ComboBox, opt)
195 painter = qg.QPainter(self)
196 painter.save()
198 rect = spainter.style().subElementRect(
199 qw.QStyle.SE_ComboBoxFocusRect, opt, self)
201 _paint_cpt_rect(painter, data, rect)
203 painter.restore()
205 else:
206 qw.QComboBox.paintEvent(self, e)
209class MyDockWidgetTitleBarButton(qw.QPushButton):
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)
217 def sizeHint(self):
218 s = qw.QPushButton.sizeHint(self)
219 return qc.QSize(s.height(), s.height())
222class MyDockWidgetTitleBarButtonToggle(MyDockWidgetTitleBarButton):
224 toggled = qc.pyqtSignal(bool)
226 def __init__(self, text_checked, text_unchecked, *args, **kwargs):
227 MyDockWidgetTitleBarButton.__init__(
228 self, text_checked, *args, **kwargs)
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)
236 def set_checked(self, checked):
237 self._checked = checked
238 self.update_text()
240 def toggle(self):
241 self._checked = not self._checked
242 self.update_text()
243 self.toggled.emit(self._checked)
245 def update_text(self):
246 if self._checked:
247 self.setText(self._text_checked)
248 else:
249 self.setText(self._text_unchecked)
252class MyDockWidgetTitleBarLabel(qw.QLabel):
254 def event(self, ev):
255 ev.ignore()
256 return qw.QLabel.event(self, ev)
259class MyDockWidgetTitleBar(qw.QFrame):
261 def __init__(self, title, title_controls=[]):
262 qw.QFrame.__init__(self)
264 lab = MyDockWidgetTitleBarLabel('<strong>%s</strong>' % title)
265 lab.setSizePolicy(
266 qw.QSizePolicy.Expanding, qw.QSizePolicy.Minimum)
268 button_hide = MyDockWidgetTitleBarButton('-')
269 button_hide.setStatusTip('Hide Panel')
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)
278 self.setLayout(layout)
279 self.setBackgroundRole(qg.QPalette.Mid)
280 self.setAutoFillBackground(True)
281 self.button_hide = button_hide
283 def event(self, ev):
284 ev.ignore()
285 return qw.QFrame.event(self, ev)
288class MyDockWidget(qw.QDockWidget):
290 def __init__(self, name, parent, title_controls=[], **kwargs):
291 qw.QDockWidget.__init__(self, name, parent, **kwargs)
293 self.setFeatures(
294 qw.QDockWidget.DockWidgetClosable
295 | qw.QDockWidget.DockWidgetMovable
296 | qw.QDockWidget.DockWidgetFloatable
297 | qw.QDockWidget.DockWidgetClosable)
299 self._visible = False
300 self._blocked = False
302 tb = MyDockWidgetTitleBar(name, title_controls)
303 tb.button_hide.clicked.connect(self.hide)
304 self.setTitleBarWidget(tb)
305 self.titlebar = tb
307 def setVisible(self, visible):
308 self._visible = visible
309 if not self._blocked:
310 qw.QDockWidget.setVisible(self, self._visible)
312 def show(self):
313 self.setVisible(True)
315 def hide(self):
316 self.setVisible(False)
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)
325 def block(self):
326 self.setBlocked(True)
328 def unblock(self):
329 self.setBlocked(False)
332class MyDoubleSpinBox(qw.QDoubleSpinBox):
333 def __init__(self, *args, **kwargs):
334 qw.QDoubleSpinBox.__init__(self, *args, **kwargs)
336 self.setLocale(qc.QLocale(' English'))