1# https://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6import copy
8import math
9import numpy as num
10from scipy.interpolate import interp1d
12from pyrocko import automap, plot, util
13from pyrocko.geometry import d2r
14from pyrocko.gui.qt_compat import qg, qw, qc
15from pyrocko.gui.util import tmin_effective, tmax_effective, get_app # noqa
18def get_err_palette():
19 err_palette = qg.QPalette()
20 err_palette.setColor(qg.QPalette.Text, qg.QColor(255, 200, 200))
21 return err_palette
24def get_palette():
25 return qw.QApplication.palette()
28def errorize(widget):
29 widget.setStyleSheet('''
30 QLineEdit {
31 background: rgb(200, 150, 150);
32 }''')
35def de_errorize(widget):
36 if isinstance(widget, qw.QWidget):
37 widget.setStyleSheet('')
40def strings_to_combobox(list_of_str):
41 cb = qw.QComboBox()
42 for i, s in enumerate(list_of_str):
43 cb.insertItem(i, s)
45 return cb
48def string_choices_to_combobox(cls):
49 return strings_to_combobox(cls.choices)
52def time_or_none_to_str(t):
53 if t is None:
54 return ''
55 else:
56 return util.time_to_str(t)
59def cover_region(lat, lon, delta, step=None, avoid_poles=False):
60 if step is None:
61 step = plot.nice_value(delta / 10.)
63 assert step <= 20.
65 def fl_major(x):
66 return math.floor(x / step) * step
68 def ce_major(x):
69 return math.ceil(x / step) * step
71 if avoid_poles:
72 lat_min_lim = -90. + step
73 lat_max_lim = 90. - step
74 else:
75 lat_min_lim = -90.
76 lat_max_lim = 90.
78 lat_min = max(lat_min_lim, fl_major(lat - delta))
79 lat_max = min(lat_max_lim, ce_major(lat + delta))
81 lon_closed = False
82 if abs(lat)+delta < 89.:
83 factor = 1.0 / math.cos((abs(lat)+delta) * d2r)
84 lon_min = fl_major(lon - delta * factor)
85 lon_max = ce_major(lon + delta * factor)
86 if lon_max >= lon_min + 360. - step*1e-5:
87 lon_min, lon_max = -180., 180. - step
88 lon_closed = True
89 else:
90 lon_min, lon_max = -180., 180. - step
91 lon_closed = True
93 return lat_min, lat_max, lon_min, lon_max, lon_closed
96qfiledialog_options = qw.QFileDialog.DontUseNativeDialog | \
97 qw.QFileDialog.DontUseSheet
100def _paint_cpt_rect(painter, cpt, rect):
101 rect.adjust(+5, +2, -5, -2)
103 rect_cpt = copy.deepcopy(rect)
104 rect_cpt.setWidth(int(rect.width() * 0.9) - 2)
106 rect_c_nan = copy.deepcopy(rect)
107 rect_c_nan.setLeft(rect.left() + rect_cpt.width() + 4)
108 rect_c_nan.setWidth(int(rect.width() * 0.1) - 2)
110 levels = num.zeros(len(cpt.levels) * 2 + 4)
111 colors = num.ones((levels.shape[0], 4)) * 255
113 for il, level in enumerate(cpt.levels):
114 levels[il*2+2] = level.vmin + (
115 level.vmax - level.vmin) / rect_cpt.width() # ow interp errors
116 levels[il*2+3] = level.vmax
118 colors[il*2+2, :3] = level.color_min
119 colors[il*2+3, :3] = level.color_max
121 level_range = levels[-3] - levels[2]
122 levels[0], levels[1] = levels[2] - level_range * 0.05, levels[2]
123 levels[-2], levels[-1] = levels[-3], levels[-3] + level_range * 0.05
125 if cpt.color_below:
126 colors[:2, :3] = cpt.color_below
127 else:
128 colors[:2] = (0, 0, 0, 0)
130 if cpt.color_above:
131 colors[-2:, :3] = cpt.color_above
132 else:
133 colors[-2:] = (0, 0, 0, 0)
135 levels_interp = num.linspace(levels[0], levels[-1], rect_cpt.width())
136 interpolator = interp1d(levels, colors.T)
138 colors_interp = interpolator(
139 levels_interp).T.astype(num.uint8).tobytes()
141 colors_interp = num.tile(
142 colors_interp, rect_cpt.height())
144 img = qg.QImage(
145 colors_interp, rect_cpt.width(), rect_cpt.height(),
146 qg.QImage.Format_RGBA8888)
148 painter.drawImage(rect_cpt, img)
150 c = cpt.color_nan
151 qcolor_nan = qg.QColor(*c if c is not None else (0, 0, 0))
152 qcolor_nan.setAlpha(255 if c is not None else 0)
154 painter.fillRect(rect_c_nan, qcolor_nan)
157class CPTStyleDelegate(qw.QItemDelegate):
159 def __init__(self, parent=None):
160 qw.QItemDelegate.__init__(self, parent)
162 def paint(self, painter, option, index):
163 data = index.model().data(index, qc.Qt.UserRole)
165 if isinstance(data, automap.CPT):
166 painter.save()
167 rect = option.rect
168 _paint_cpt_rect(painter, data, rect)
169 painter.restore()
171 else:
172 qw.QItemDelegate.paint(self, painter, option, index)
175class CPTComboBox(qw.QComboBox):
176 def __init__(self):
177 super().__init__()
179 self.setItemDelegate(CPTStyleDelegate(parent=self))
180 self.setInsertPolicy(qw.QComboBox.InsertAtBottom)
182 def paintEvent(self, e):
183 data = self.itemData(self.currentIndex(), qc.Qt.UserRole)
185 if isinstance(data, automap.CPT):
186 spainter = qw.QStylePainter(self)
187 spainter.setPen(self.palette().color(qg.QPalette.Text))
189 opt = qw.QStyleOptionComboBox()
190 self.initStyleOption(opt)
191 spainter.drawComplexControl(qw.QStyle.CC_ComboBox, opt)
193 painter = qg.QPainter(self)
194 painter.save()
196 rect = spainter.style().subElementRect(
197 qw.QStyle.SE_ComboBoxFocusRect, opt, self)
199 _paint_cpt_rect(painter, data, rect)
201 painter.restore()
203 else:
204 qw.QComboBox.paintEvent(self, e)
207class MyDockWidgetTitleBarButton(qw.QPushButton):
209 def __init__(self, *args, **kwargs):
210 qw.QPushButton.__init__(self, *args, **kwargs)
211 self.setFlat(True)
212 self.setSizePolicy(
213 qw.QSizePolicy.Fixed, qw.QSizePolicy.Fixed)
215 def sizeHint(self):
216 s = qw.QPushButton.sizeHint(self)
217 return qc.QSize(s.height(), s.height())
220class MyDockWidgetTitleBarButtonToggle(MyDockWidgetTitleBarButton):
222 toggled = qc.pyqtSignal(bool)
224 def __init__(self, text_checked, text_unchecked, *args, **kwargs):
225 MyDockWidgetTitleBarButton.__init__(
226 self, text_checked, *args, **kwargs)
228 self._checked = True
229 self._text_checked = text_checked
230 self._text_unchecked = text_unchecked
231 self.update_text()
232 self.clicked.connect(self.toggle)
234 def set_checked(self, checked):
235 self._checked = checked
236 self.update_text()
238 def toggle(self):
239 self._checked = not self._checked
240 self.update_text()
241 self.toggled.emit(self._checked)
243 def update_text(self):
244 if self._checked:
245 self.setText(self._text_checked)
246 else:
247 self.setText(self._text_unchecked)
250class MyDockWidgetTitleBarLabel(qw.QLabel):
252 def event(self, ev):
253 ev.ignore()
254 return qw.QLabel.event(self, ev)
257class MyDockWidgetTitleBar(qw.QFrame):
259 def __init__(self, title, title_controls=[]):
260 qw.QFrame.__init__(self)
262 lab = MyDockWidgetTitleBarLabel('<strong>%s</strong>' % title)
263 lab.setSizePolicy(
264 qw.QSizePolicy.Expanding, qw.QSizePolicy.Minimum)
266 button_hide = MyDockWidgetTitleBarButton('-')
267 button_hide.setStatusTip('Hide Panel')
269 layout = qw.QGridLayout()
270 layout.setSpacing(0)
271 layout.addWidget(lab, 0, 0)
272 layout.addWidget(button_hide, 0, 1)
273 for i, button in enumerate(title_controls):
274 layout.addWidget(button, 0, 2 + i)
276 self.setLayout(layout)
277 self.setBackgroundRole(qg.QPalette.Mid)
278 self.setAutoFillBackground(True)
279 self.button_hide = button_hide
281 def event(self, ev):
282 ev.ignore()
283 return qw.QFrame.event(self, ev)
286class MyDockWidget(qw.QDockWidget):
288 def __init__(self, name, parent, title_controls=[], **kwargs):
289 qw.QDockWidget.__init__(self, name, parent, **kwargs)
291 self.setFeatures(
292 qw.QDockWidget.DockWidgetClosable
293 | qw.QDockWidget.DockWidgetMovable
294 | qw.QDockWidget.DockWidgetFloatable
295 | qw.QDockWidget.DockWidgetClosable)
297 self._visible = False
298 self._blocked = False
300 tb = MyDockWidgetTitleBar(name, title_controls)
301 tb.button_hide.clicked.connect(self.hide)
302 self.setTitleBarWidget(tb)
303 self.titlebar = tb
305 def setVisible(self, visible):
306 self._visible = visible
307 if not self._blocked:
308 qw.QDockWidget.setVisible(self, self._visible)
310 def show(self):
311 self.setVisible(True)
313 def hide(self):
314 self.setVisible(False)
316 def setBlocked(self, blocked):
317 self._blocked = blocked
318 if blocked:
319 qw.QDockWidget.setVisible(self, False)
320 else:
321 qw.QDockWidget.setVisible(self, self._visible)
323 def block(self):
324 self.setBlocked(True)
326 def unblock(self):
327 self.setBlocked(False)
330class MyDoubleSpinBox(qw.QDoubleSpinBox):
331 def __init__(self, *args, **kwargs):
332 qw.QDoubleSpinBox.__init__(self, *args, **kwargs)
334 self.setLocale(qc.QLocale(' English'))