# http://pyrocko.org - GPLv3 # # The Pyrocko Developers, 21st Century # ---|P------/S----------~Lg----------
QItemSelectionModel, QItemSelection, QPixmapCache, use_pyqt5
try: return float(val), True except ValueError: return 9e99, False
else: def toDateTime(val): return val.toDateTime()
def isDateTime(val): return val.type() == qc.QVariant.DateTime
def toFloat(val): return val.toFloat()
def toString(val): return str(val).encode('utf-8')
'T', 'Time', 'M', 'Label', 'Depth [km]', 'Lat', 'Lon', 'Kind', 'Dist [km]', 'NSLCs', 'Polarity', 'Kagan Angle [deg]', 'Event Hash', 'MT']
qw.QWidget.__init__(self, *args, **kwargs) self.color = color self.moment_tensor = moment_tensor self.setGeometry(0, 0, 100, 100) self.setAttribute(qc.Qt.WA_TranslucentBackground)
self.flipy = qg.QTransform() self.flipy.translate(0, self.height()) self.flipy.scale(1, -1)
center = e.rect().center() painter = qg.QPainter(self) painter.save() painter.setWorldTransform(self.flipy) try: data = mt2beachball( self.moment_tensor, size=self.height()/2.2, position=(center.x(), center.y()), color_t=self.color, color_p=qc.Qt.white, edgecolor=qc.Qt.black) for pdata in data: paths, fill, edges, thickness = pdata
pen = qg.QPen(edges) pen.setWidthF(3) if fill != 'none': brush = qg.QBrush(fill) painter.setBrush(brush)
polygon = qg.QPolygonF() polygon = make_QPolygonF(*paths.T) painter.setRenderHint(qg.QPainter.Antialiasing) painter.setPen(pen) painter.drawPolygon(polygon) except BeachballError as e: logger.exception(e) finally: painter.restore()
try: return self.grab(self.rect()) except AttributeError: return qg.QPixmap().grabWidget(self, self.rect())
'''Takes care of the table's style.'''
mt = self.bb_data_from_index(index) if mt: key = ''.join([str(round(x, 1)) for x in mt.m6()]) pixmap = self.bbcache.cached(key) if pixmap: pixmap = pixmap.scaledToHeight(option.rect.height()) else: pixmap = BeachballWidget( moment_tensor=mt, color=qg.QColor(*tango_colors['scarletred3']) ).to_qpixmap() self.bbcache.insert(key, pixmap) a, b, c, d = option.rect.getRect() painter.save() painter.setRenderHint(qg.QPainter.Antialiasing) painter.drawPixmap(a+d/2., b, d, d, pixmap) painter.restore()
else:
self.parent().model().mapToSource(index).row() == iactive:
'yyyy-MM-dd HH:mm:ss.zzz') else:
marker = self.marker_from_index(index) if isinstance(marker, EventMarker): return marker.get_event().moment_tensor else: return None
'''Sorts the table's columns.'''
return toString(left.data()) < toString(right.data()) else: return toFloat(left.data())[0] < toFloat(right.data())[0]
'''Set and format header data.'''
elif orientation == qc.Qt.Vertical: if role == qc.Qt.DisplayRole: return qc.QVariant(str(col))
else: return qc.QVariant()
'QTableView{selection-background-color: \ rgba(130, 130, 130, 100% );}')
'Latitude/Longitude', 'Kind', 'Distance [km]', 'NSLCs', 'Polarity', 'Kagan Angle [deg]', 'Event Hash', 'MT']
self.menu_labels, [0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13]))
self.toggle_columns)
self.toggle_numbering)
self.show_context_menu)
if wheel_event.modifiers() & qc.Qt.ControlModifier: height = self.rowAt(self.height()) ci = self.indexAt( qc.QPoint(self.viewport().rect().x(), height)) v = self.verticalHeader()
if use_pyqt5: wheel_delta = wheel_event.angleDelta().y() else: wheel_delta = wheel_event.delta()
v.setDefaultSectionSize( max(12, v.defaultSectionSize()+wheel_delta//60)) self.scrollTo(ci) if v.isVisible(): self.toggle_numbering(False) self.toggle_numbering(True)
else: super(MarkerTableView, self).wheelEvent(wheel_event)
'''Set a pile_viewer and connect to signals.'''
'''Propagate ``key_event`` to pile_viewer, unless up/down pressed.''' if key_event.key() in [qc.Qt.Key_Up, qc.Qt.Key_Down]: qw.QTableView.keyPressEvent(self, key_event) self.pile_viewer.go_to_selection() else: self.pile_viewer.keyPressEvent(key_event)
'''Ignore mouse clicks.''' pass
self.right_click_menu.popup(qg.QCursor.pos())
if want: self.verticalHeader().show() else: self.verticalHeader().hide()
from .qt_compat import qprint printer = qprint.QPrinter(qprint.QPrinter.ScreenResolution) printer.setOutputFormat(qprint.QPrinter.NativeFormat) printer_dialog = qprint.QPrintDialog(printer, self) if printer_dialog.exec_() == qw.QDialog.Accepted:
scrollbarpolicy = self.verticalScrollBarPolicy() self.setVerticalScrollBarPolicy(qc.Qt.ScrollBarAlwaysOff) rect = printer.pageRect() painter = qg.QPainter() painter.begin(printer) xscale = rect.width() / (self.width()*1.1) yscale = rect.height() / (self.height() * 1.1) scale = min(xscale, yscale) painter.translate(rect.x() + rect.width()/2., rect.y() + rect.height()/2.) painter.scale(scale, scale) painter.translate(-self.width()/2., -self.height()/2.) painter.setRenderHints(qg.QPainter.HighQualityAntialiasing | qg.QPainter.TextAntialiasing) self.render(painter) painter.end() self.setVerticalScrollBarPolicy(scrollbarpolicy)
if model_index.column() in self.editable_columns: return else: self.pile_viewer.go_to_selection()
'''Pop-up menu to toggle columns in the :py:class:`MarkerTableView`.'''
self.header_menu.popup(self.mapToGlobal(point))
'''Toggle columns depending in checked state. '''
self.model().sourceModel().update_distances_and_angles( [[self.active_event_index]], want_distances=want_distances, want_angles=want_angles)
'''Set a pile_viewer and connect to signals.'''
self.markers_added)
self.markers_removed)
self.update_distances_and_angles)
'''Insert rows into table.'''
'''Remove rows from table.'''
'''Set data in each of the table's cell.'''
return qc.QVariant()
qc.QDateTime.fromMSecsSinceEpoch(marker.tmin*1000))
s = round(e.moment_tensor.magnitude, 1) s = round(e.magnitude, 1)
elif column == _column_mapping['Label']: if isinstance(marker, EventMarker): s = marker.label() elif isinstance(marker, PhaseMarker): s = marker.get_label()
elif column == _column_mapping['Depth [km]']: if isinstance(marker, EventMarker): d = marker.get_event().depth if d is not None: s = round(marker.get_event().depth/1000., 1)
elif column == _column_mapping['Lat']: if isinstance(marker, EventMarker): s = round(marker.get_event().lat, 2)
elif column == _column_mapping['Lon']: if isinstance(marker, EventMarker): s = round(marker.get_event().lon, 2)
elif column == _column_mapping['Kind']: s = marker.kind
elif column == _column_mapping['Dist [km]']: if marker in self.distances: s = self.distances[marker]
elif column == _column_mapping['NSLCs']: strs = [] for nslc_id in marker.get_nslc_ids(): strs.append('.'.join(nslc_id)) s = '|'.join(strs)
elif column == _column_mapping['Kagan Angle [deg]']: if marker in self.kagan_angles: s = round(self.kagan_angles[marker], 1)
elif column == _column_mapping['MT']: return qc.QVariant()
elif column == _column_mapping['Event Hash']: if isinstance(marker, (EventMarker, PhaseMarker)): s = marker.get_event_hash() else: return qc.QVariant()
elif column == _column_mapping['Polarity']: if isinstance(marker, (PhaseMarker)): s = marker.get_polarity_symbol() else: return qc.QVariant()
want_distances=False): '''Calculate and update distances and kagan angles between events.
:param indices: list of lists of indices (optional)
Ideally, indices are consecutive for best performance.''' not self.marker_table_view.isColumnHidden( _column_mapping['Kagan Angle [deg]']) not self.marker_table_view.isColumnHidden( _column_mapping['Dist [km]'])
indices = indices or [[]] indices = [i for ii in indices for i in ii]
if len(indices) != 1: return
if self.last_active_event == self.pile_viewer.get_active_event(): return else: self.last_active_event = self.pile_viewer.get_active_event()
markers = self.pile_viewer.markers nmarkers = len(markers) omarker = markers[indices[0]] if not isinstance(omarker, EventMarker): return else: oevent = omarker.get_event()
emarkers = [m for m in markers if isinstance(m, EventMarker)] if len(emarkers) < 2: return else: events = [em.get_event() for em in emarkers] nevents = len(events)
if want_distances: lats = num.zeros(nevents) lons = num.zeros(nevents) for i in range(nevents): lats[i] = events[i].effective_lat lons[i] = events[i].effective_lon
olats = num.zeros(nevents) olons = num.zeros(nevents) olats[:] = oevent.effective_lat olons[:] = oevent.effective_lon dists = orthodrome.distance_accurate50m_numpy( lats, lons, olats, olons) dists /= 1000.
dists = [round(x, 1) for x in dists] self.distances = dict(list(zip(emarkers, dists)))
if want_angles: if oevent.moment_tensor: for em in emarkers: e = em.get_event() if e.moment_tensor: a = kagan_angle(oevent.moment_tensor, e.moment_tensor) self.kagan_angles[em] = a else: self.kagan_angles = {}
istart = self.index(0, _column_mapping['Dist [km]']) istop = self.index(nmarkers-1, _column_mapping['Kagan Angle [deg]'])
self.dataChanged.emit( istart, istop)
self.dataChanged.emit(index, index) return True
'''Manipulate :py:class:`EventMarker` instances.''' if role == qc.Qt.EditRole: imarker = index.row() marker = self.pile_viewer.markers[imarker] if index.column() in [_column_mapping[c] for c in [ 'M', 'Lat', 'Lon', 'Depth [km]']]:
if not isinstance(marker, EventMarker): return False else: if index.column() == _column_mapping['M']: valuef, valid = toFloat(value) if valid: e = marker.get_event() if e.moment_tensor is None: e.magnitude = valuef else: e.moment_tensor.magnitude = valuef return self.done(index)
if index.column() in [_column_mapping['Lon'], _column_mapping['Lat'], _column_mapping['Depth [km]']]: if isinstance(marker, EventMarker): valuef, valid = toFloat(value) if valid: if index.column() == _column_mapping['Lat']: marker.get_event().lat = valuef elif index.column() == _column_mapping['Lon']: marker.get_event().lon = valuef elif index.column() == _column_mapping[ 'Depth [km]']: marker.get_event().depth = valuef*1000. return self.done(index)
if index.column() == _column_mapping['Label']: values = str(toString(value)) if values != '': if isinstance(marker, EventMarker): marker.get_event().set_name(values) return self.done(index)
if isinstance(marker, PhaseMarker): marker.set_phasename(values) return self.done(index)
return False
'''Set flags for cells which the user can edit.'''
else:
else: header.setResizeMode(i_s, qw.QHeaderView.Interactive)
self.set_selected_markers)
'''Set a pile_viewer and connect to signals.'''
self.update_selection_model)
self.marker_table_view.set_active_event_index)
''' set markers selected in viewer at selection in table.'''
self.pile_viewer.markers[ self.proxy_filter.mapToSource(i).row()])
'''Return :py:class:`MarkerTableModel` instance'''
'''Adopt marker selections done in the pile_viewer in the tableview.
:param indices: list of indices of selected markers.''' (QItemSelectionModel.Select | QItemSelectionModel.Rows | QItemSelectionModel.Current))
QItemSelection(mi_start, mi_stop))
mi_start)) mi_start, selection_flags)
|