1# http://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6from .qt_compat import qc, qg, qw, QPixmapCache
8from .util import EventMarker, PhaseMarker, make_QPolygonF
9from pyrocko.plot.beachball import mt2beachball, BeachballError
10from pyrocko.moment_tensor import kagan_angle
11from pyrocko.plot import tango_colors
12from pyrocko import orthodrome
13from pyrocko.util import time_to_str
15import logging
17from .marker import g_color_b
20def faint(c):
21 return tuple(255 - (255 - x) // 5 for x in c)
24g_color_b_faint = [faint(c) for c in g_color_b]
27def noop(x=None):
28 return x
31qc.QString = str
32qc.QVariant = noop
35def toFloat(val):
36 try:
37 return float(val), True
38 except (ValueError, TypeError):
39 return 9e99, False
42logger = logging.getLogger('pyrocko.gui.marker_editor')
44_header_data = [
45 'T', 'Time', 'M', 'Label', 'Depth [km]', 'Lat', 'Lon', 'Kind', 'Dist [km]',
46 'NSLCs', 'Polarity', 'Kagan Angle [deg]', 'Event Hash', 'MT']
48_column_mapping = dict(zip(_header_data, range(len(_header_data))))
50_string_header = (_column_mapping['Time'], _column_mapping['Label'])
52_header_sizes = [70] * len(_header_data)
53_header_sizes[0] = 40
54_header_sizes[1] = 190
55_header_sizes[-1] = 20
58class BeachballWidget(qw.QWidget):
60 def __init__(self, moment_tensor, color, *args, **kwargs):
61 qw.QWidget.__init__(self, *args, **kwargs)
62 self.color = color
63 self.moment_tensor = moment_tensor
64 self.setGeometry(0, 0, 100, 100)
65 self.setAttribute(qc.Qt.WA_TranslucentBackground)
67 self.flipy = qg.QTransform()
68 self.flipy.translate(0, self.height())
69 self.flipy.scale(1, -1)
71 def paintEvent(self, e):
72 center = e.rect().center()
73 painter = qg.QPainter(self)
74 painter.save()
75 painter.setWorldTransform(self.flipy)
76 try:
77 data = mt2beachball(
78 self.moment_tensor, size=self.height()/2.2,
79 position=(center.x(), center.y()),
80 color_t=self.color, color_p=qc.Qt.white, edgecolor=qc.Qt.black)
81 for pdata in data:
82 paths, fill, edges, thickness = pdata
84 pen = qg.QPen(edges)
85 pen.setWidthF(3)
86 if fill != 'none':
87 brush = qg.QBrush(fill)
88 painter.setBrush(brush)
90 polygon = qg.QPolygonF()
91 polygon = make_QPolygonF(*paths.T)
92 painter.setRenderHint(qg.QPainter.Antialiasing)
93 painter.setPen(pen)
94 painter.drawPolygon(polygon)
95 except BeachballError as e:
96 logger.exception(e)
97 finally:
98 painter.restore()
100 def to_qpixmap(self):
101 try:
102 return self.grab(self.rect())
103 except AttributeError:
104 return qg.QPixmap().grabWidget(self, self.rect())
107class MarkerItemDelegate(qw.QStyledItemDelegate):
109 # Takes care of how table entries are displayed
111 def __init__(self, *args, **kwargs):
112 qw.QStyledItemDelegate.__init__(self, *args, **kwargs)
113 self.c_alignment = qc.Qt.AlignHCenter
114 self.bbcache = QPixmapCache()
116 def paint(self, painter, option, index):
117 mcolor = self.color_from_index(index)
119 if index.column() == _column_mapping['MT']:
120 mt = self.bb_data_from_index(index)
121 if mt:
122 key = ''.join([str(round(x, 1)) for x in mt.m6()])
123 pixmap = self.bbcache.cached(key)
124 if pixmap:
125 pixmap = pixmap.scaledToHeight(option.rect.height())
126 else:
127 pixmap = BeachballWidget(
128 moment_tensor=mt,
129 color=qg.QColor(*tango_colors['scarletred3'])
130 ).to_qpixmap()
131 self.bbcache.insert(key, pixmap)
132 a, b, c, d = option.rect.getRect()
133 painter.save()
134 painter.setRenderHint(qg.QPainter.Antialiasing)
135 painter.drawPixmap(a+d/2., b, d, d, pixmap)
136 painter.restore()
138 else:
139 if index.column() == 0:
140 option.state = option.state & ~qw.QStyle.State_Selected
142 qw.QStyledItemDelegate.paint(self, painter, option, index)
144 marker = self.parent().model().get_marker(index)
146 if marker.active:
148 painter.save()
150 rect = option.rect
151 x1, y1, x2, y2 = rect.getCoords()
152 y1 += 1
153 pen = painter.pen()
154 pen.setWidth(2)
155 pen.setColor(mcolor)
156 painter.setPen(pen)
157 painter.drawLine(qc.QLineF(x1, y1, x2, y1))
158 painter.drawLine(qc.QLineF(x1, y2, x2, y2))
159 painter.restore()
161 def marker_from_index(self, index):
162 tv = self.parent()
163 pv = tv.pile_viewer
164 tvm = tv.model()
165 if isinstance(tvm, qc.QSortFilterProxyModel):
166 return pv.markers[tvm.mapToSource(index).row()]
167 else:
168 return pv.markers[index.row()]
170 def bb_data_from_index(self, index):
171 marker = self.marker_from_index(index)
172 if isinstance(marker, EventMarker):
173 return marker.get_event().moment_tensor
174 else:
175 return None
177 def color_from_index(self, index):
178 marker = self.marker_from_index(index)
179 return qg.QColor(*marker.select_color(g_color_b))
182class MarkerSortFilterProxyModel(qc.QSortFilterProxyModel):
184 # Proxy object between view and model to handle sorting
186 def __init__(self, *args, **kwargs):
187 qc.QSortFilterProxyModel.__init__(self, *args, **kwargs)
188 self.setSortRole(qc.Qt.UserRole)
189 self.sort(1, qc.Qt.AscendingOrder)
191 def get_marker(self, index):
192 return self.sourceModel().get_marker(self.mapToSource(index))
195class MarkerTableView(qw.QTableView):
196 def __init__(self, *args, **kwargs):
197 sortable = kwargs.pop('sortable', True)
198 qw.QTableView.__init__(self, *args, **kwargs)
199 self.setSelectionBehavior(qw.QAbstractItemView.SelectRows)
200 self.setHorizontalScrollMode(qw.QAbstractItemView.ScrollPerPixel)
201 self.setEditTriggers(qw.QAbstractItemView.DoubleClicked)
202 self.setSortingEnabled(sortable)
203 self.setStyleSheet(
204 'QTableView{selection-background-color: \
205 rgba(130, 130, 130, 100% );}')
207 self.sortByColumn(1, qc.Qt.AscendingOrder)
208 self.setAlternatingRowColors(True)
210 self.setShowGrid(False)
211 self.verticalHeader().hide()
212 self.pile_viewer = None
214 self.clicked.connect(self.table_clicked)
215 self.doubleClicked.connect(self.table_double_clicked)
217 self.header_menu = qw.QMenu(self)
219 show_initially = ['Type', 'Time', 'Magnitude']
220 self.menu_labels = ['Type', 'Time', 'Magnitude', 'Label', 'Depth [km]',
221 'Latitude/Longitude', 'Kind', 'Distance [km]',
222 'NSLCs', 'Polarity', 'Kagan Angle [deg]',
223 'Event Hash', 'MT']
225 self.menu_items = dict(zip(
226 self.menu_labels, [0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13]))
228 self.editable_columns = [2, 3, 4, 5, 6, 7]
230 self.column_actions = {}
231 for hd in self.menu_labels:
232 a = qw.QAction(hd, self.header_menu)
233 a.triggered.connect(
234 self.toggle_columns)
235 a.setCheckable(True)
236 a.setChecked(hd in show_initially)
237 self.header_menu.addAction(a)
238 self.column_actions[hd] = a
240 a = qw.QAction('Numbering', self.header_menu)
241 a.setCheckable(True)
242 a.setChecked(False)
243 a.triggered.connect(
244 self.toggle_numbering)
245 self.header_menu.addAction(a)
247 header = self.horizontalHeader()
248 header.setContextMenuPolicy(qc.Qt.CustomContextMenu)
249 header.customContextMenuRequested.connect(
250 self.show_context_menu)
252 self.right_click_menu = qw.QMenu(self)
253 print_action = qw.QAction('Print Table', self.right_click_menu)
254 print_action.triggered.connect(self.print_menu)
255 self.right_click_menu.addAction(print_action)
257 def wheelEvent(self, wheel_event):
258 if wheel_event.modifiers() & qc.Qt.ControlModifier:
259 height = self.rowAt(self.height())
260 ci = self.indexAt(
261 qc.QPoint(self.viewport().rect().x(), height))
262 v = self.verticalHeader()
264 wheel_delta = wheel_event.angleDelta().y()
266 v.setDefaultSectionSize(
267 max(12, v.defaultSectionSize()+wheel_delta//60))
268 self.scrollTo(ci)
269 if v.isVisible():
270 self.toggle_numbering(False)
271 self.toggle_numbering(True)
273 else:
274 super(MarkerTableView, self).wheelEvent(wheel_event)
276 def set_viewer(self, viewer):
277 '''
278 Set connected pile viewer and hook up signals.
279 '''
281 self.pile_viewer = viewer
283 def keyPressEvent(self, key_event):
284 # Propagate key_event to pile_viewer, unless up/down pressed
286 if key_event.key() in [qc.Qt.Key_Up, qc.Qt.Key_Down]:
287 qw.QTableView.keyPressEvent(self, key_event)
288 self.pile_viewer.go_to_selection()
289 else:
290 self.pile_viewer.keyPressEvent(key_event)
292 def table_clicked(self, model_index):
293 # Ignore mouse clicks
294 pass
296 def contextMenuEvent(self, event):
297 self.right_click_menu.popup(qg.QCursor.pos())
299 def toggle_numbering(self, want):
300 if want:
301 self.verticalHeader().show()
302 else:
303 self.verticalHeader().hide()
305 def print_menu(self):
306 from .qt_compat import qprint
307 printer = qprint.QPrinter(qprint.QPrinter.ScreenResolution)
308 printer.setOutputFormat(qprint.QPrinter.NativeFormat)
309 printer_dialog = qprint.QPrintDialog(printer, self)
310 if printer_dialog.exec_() == qw.QDialog.Accepted:
312 scrollbarpolicy = self.verticalScrollBarPolicy()
313 self.setVerticalScrollBarPolicy(qc.Qt.ScrollBarAlwaysOff)
314 rect = printer.pageRect()
315 painter = qg.QPainter()
316 painter.begin(printer)
317 xscale = rect.width() / (self.width()*1.1)
318 yscale = rect.height() / (self.height() * 1.1)
319 scale = min(xscale, yscale)
320 painter.translate(rect.x() + rect.width()/2.,
321 rect.y() + rect.height()/2.)
322 painter.scale(scale, scale)
323 painter.translate(-self.width()/2., -self.height()/2.)
324 painter.setRenderHints(qg.QPainter.HighQualityAntialiasing |
325 qg.QPainter.TextAntialiasing)
326 self.render(painter)
327 painter.end()
328 self.setVerticalScrollBarPolicy(scrollbarpolicy)
330 def table_double_clicked(self, model_index):
331 if model_index.column() in self.editable_columns:
332 return
333 else:
334 self.pile_viewer.go_to_selection()
336 def show_context_menu(self, point):
337 '''
338 Pop-up menu to toggle column visibility.
339 '''
341 self.header_menu.popup(self.mapToGlobal(point))
343 def toggle_columns(self):
344 '''
345 Toggle columns depending in checked state.
346 '''
348 width = 0
349 for header, ca in self.column_actions.items():
350 hide = not ca.isChecked()
351 self.setColumnHidden(self.menu_items[header], hide)
352 if header == 'Latitude/Longitude':
353 self.setColumnHidden(self.menu_items[header]+1, hide)
354 if not hide:
355 width += _header_sizes[self.menu_labels.index(header)]
357 self.parent().setMinimumWidth(width)
359 def update_viewport(self):
360 self.viewport().update()
363class MarkerTableModel(qc.QAbstractTableModel):
365 def __init__(self, *args, **kwargs):
366 qc.QAbstractTableModel.__init__(self, *args, **kwargs)
367 self.pile_viewer = None
368 self.distances = {}
369 self.kagan_angles = {}
370 self.row_count = 0
371 self.proxy_filter = None
373 def sourceModel(self):
374 return self
376 def set_viewer(self, viewer):
377 '''
378 Set connected pile viewer and hook up signals.
379 '''
381 self.pile_viewer = viewer
382 self.pile_viewer.begin_markers_add.connect(
383 self.begin_markers_add)
384 self.pile_viewer.end_markers_add.connect(
385 self.end_markers_add)
386 self.pile_viewer.begin_markers_remove.connect(
387 self.begin_markers_remove)
388 self.pile_viewer.end_markers_remove.connect(
389 self.end_markers_remove)
391 def rowCount(self, parent=None):
392 if not self.pile_viewer:
393 return 0
394 return len(self.pile_viewer.get_markers())
396 def columnCount(self, parent=None):
397 return len(_column_mapping)
399 def begin_markers_add(self, istart, istop):
400 self.beginInsertRows(qc.QModelIndex(), istart, istop)
402 def end_markers_add(self):
403 self.endInsertRows()
405 def begin_markers_remove(self, istart, istop):
406 self.beginRemoveRows(qc.QModelIndex(), istart, istop)
408 def end_markers_remove(self):
409 self.endRemoveRows()
410 self.marker_table_view.updateGeometries()
412 def headerData(self, col, orientation, role):
413 '''
414 Get header data entry.
415 '''
417 if orientation == qc.Qt.Horizontal:
418 if role == qc.Qt.DisplayRole:
419 return qc.QVariant(_header_data[col])
420 elif role == qc.Qt.SizeHintRole:
421 return qc.QSize(10, 20)
423 elif orientation == qc.Qt.Vertical:
424 if role == qc.Qt.DisplayRole:
425 return qc.QVariant(str(col))
427 else:
428 return qc.QVariant()
430 def get_marker(self, index):
431 return self.pile_viewer.markers[index.row()]
433 def data(self, index, role):
434 '''
435 Get model data entry.
436 '''
438 if not self.pile_viewer:
439 return qc.QVariant()
441 marker = self.pile_viewer.markers[index.row()]
442 column = index.column()
444 if role == qc.Qt.BackgroundRole:
445 if marker.active or column == _column_mapping['T']:
446 return qg.QBrush(
447 qg.QColor(*marker.select_color(g_color_b_faint)))
449 if role == qc.Qt.ForegroundRole:
450 if marker.active or column == _column_mapping['T']:
451 return qg.QBrush(
452 qg.QColor(*marker.select_color(g_color_b)))
454 elif role in (qc.Qt.DisplayRole, qc.Qt.UserRole):
456 v = None
457 if column == _column_mapping['Time']:
458 if role == qc.Qt.UserRole:
459 v = marker.tmin
460 else:
461 v = time_to_str(marker.tmin)
463 elif column == _column_mapping['T']:
464 if isinstance(marker, EventMarker):
465 v = u'\u25ce'
466 elif isinstance(marker, PhaseMarker):
467 v = marker.get_label()
469 elif column == _column_mapping['M']:
470 if isinstance(marker, EventMarker):
471 e = marker.get_event()
472 if e.moment_tensor is not None:
473 v = round(e.moment_tensor.magnitude, 1)
474 elif e.magnitude is not None:
475 v = round(e.magnitude, 1)
477 elif column == _column_mapping['Label']:
478 if isinstance(marker, EventMarker):
479 v = marker.label()
480 elif isinstance(marker, PhaseMarker):
481 v = marker.get_label()
483 elif column == _column_mapping['Depth [km]']:
484 if isinstance(marker, EventMarker):
485 d = marker.get_event().depth
486 if d is not None:
487 v = round(marker.get_event().depth/1000., 1)
489 elif column == _column_mapping['Lat']:
490 if isinstance(marker, EventMarker):
491 v = round(marker.get_event().effective_lat, 2)
493 elif column == _column_mapping['Lon']:
494 if isinstance(marker, EventMarker):
495 v = round(marker.get_event().effective_lon, 2)
497 elif column == _column_mapping['Kind']:
498 v = marker.kind
500 elif column == _column_mapping['Dist [km]']:
501 active_event = self.pile_viewer.get_active_event()
502 if isinstance(marker, EventMarker) \
503 and active_event is not None:
505 dist = orthodrome.distance_accurate50m(
506 marker.get_event(),
507 active_event)
509 v = dist
510 if role == qc.Qt.DisplayRole:
511 v = '%.5g' % (v/1000.)
513 elif column == _column_mapping['NSLCs']:
514 strs = []
515 for nslc_id in marker.get_nslc_ids():
516 strs.append('.'.join(nslc_id))
517 v = '|'.join(strs)
519 elif column == _column_mapping['Kagan Angle [deg]']:
520 active_event = self.pile_viewer.get_active_event()
521 if isinstance(marker, EventMarker) \
522 and active_event is not None \
523 and active_event.moment_tensor is not None \
524 and marker.get_event().moment_tensor is not None:
526 v = kagan_angle(
527 active_event.moment_tensor,
528 marker.get_event().moment_tensor)
530 if role == qc.Qt.DisplayRole:
531 v = '%.1f' % v
533 elif column == _column_mapping['MT']:
534 return qc.QVariant()
536 elif column == _column_mapping['Event Hash']:
537 if isinstance(marker, (EventMarker, PhaseMarker)):
538 v = marker.get_event_hash()
539 else:
540 return qc.QVariant()
542 elif column == _column_mapping['Polarity']:
543 if isinstance(marker, (PhaseMarker)):
544 v = marker.get_polarity_symbol()
545 else:
546 return qc.QVariant()
548 return qc.QVariant(v)
550 return qc.QVariant()
552 def handle_active_event_changed(self):
553 nmarkers = self.rowCount()
554 istart = self.index(0, _column_mapping['Dist [km]'])
555 istop = self.index(nmarkers-1, _column_mapping['Dist [km]'])
556 self.dataChanged.emit(istart, istop)
558 istart = self.index(0, _column_mapping['Kagan Angle [deg]'])
559 istop = self.index(nmarkers-1, _column_mapping['Kagan Angle [deg]'])
560 self.dataChanged.emit(istart, istop)
562 def done(self, index):
563 self.dataChanged.emit(index, index)
564 return True
566 def setData(self, index, value, role):
567 '''
568 Set model data entry.
569 '''
571 if role == qc.Qt.EditRole:
572 imarker = index.row()
573 marker = self.pile_viewer.markers[imarker]
574 if index.column() in [_column_mapping[c] for c in [
575 'M', 'Lat', 'Lon', 'Depth [km]']]:
577 if not isinstance(marker, EventMarker):
578 return False
579 else:
580 if index.column() == _column_mapping['M']:
581 valuef, valid = toFloat(value)
582 if valid:
583 e = marker.get_event()
584 if e.moment_tensor is None:
585 e.magnitude = valuef
586 else:
587 e.moment_tensor.magnitude = valuef
588 return self.done(index)
590 if index.column() in [_column_mapping['Lon'],
591 _column_mapping['Lat'],
592 _column_mapping['Depth [km]']]:
593 if isinstance(marker, EventMarker):
594 valuef, valid = toFloat(value)
595 if valid:
596 if index.column() == _column_mapping['Lat']:
597 marker.get_event().lat = valuef
598 elif index.column() == _column_mapping['Lon']:
599 marker.get_event().lon = valuef
600 elif index.column() == _column_mapping[
601 'Depth [km]']:
602 marker.get_event().depth = valuef*1000.
603 return self.done(index)
605 if index.column() == _column_mapping['Label']:
606 values = str(value)
607 if values != '':
608 if isinstance(marker, EventMarker):
609 marker.get_event().set_name(values)
610 return self.done(index)
612 if isinstance(marker, PhaseMarker):
613 marker.set_phasename(values)
614 return self.done(index)
616 return False
618 def flags(self, index):
619 '''
620 Set flags for cells which the user can edit.
621 '''
623 if index.column() not in self.marker_table_view.editable_columns:
624 return qc.Qt.ItemFlags(33)
625 else:
626 if isinstance(self.pile_viewer.markers[index.row()], EventMarker):
627 if index.column() in self.marker_table_view.editable_columns:
628 return qc.Qt.ItemFlags(35)
629 if index.column() == _column_mapping['Label']:
630 return qc.Qt.ItemFlags(35)
631 return qc.Qt.ItemFlags(33)
634class MarkerEditor(qw.QFrame):
636 def __init__(self, *args, **kwargs):
637 sortable = kwargs.pop('sortable', True)
638 qw.QFrame.__init__(self, *args, **kwargs)
639 layout = qw.QGridLayout()
640 layout.setContentsMargins(0, 0, 0, 0)
641 self.setLayout(layout)
642 self.marker_table_view = MarkerTableView(self, sortable=sortable)
644 self.delegate = MarkerItemDelegate(self.marker_table_view)
645 self.marker_table_view.setItemDelegate(self.delegate)
647 self.marker_model = MarkerTableModel()
648 self.marker_model.marker_table_view = self.marker_table_view
650 if sortable:
651 self.proxy_filter = MarkerSortFilterProxyModel()
652 self.proxy_filter.setDynamicSortFilter(True)
653 self.proxy_filter.setSourceModel(self.marker_model)
654 self.marker_model.proxy_filter = self.proxy_filter
656 self.marker_table_view.setModel(self.proxy_filter)
657 else:
658 self.proxy_filter = None
659 self.marker_table_view.setModel(self.marker_model)
661 header = self.marker_table_view.horizontalHeader()
662 for i_s, s in enumerate(_header_sizes):
663 header.setSectionResizeMode(i_s, qw.QHeaderView.Interactive)
664 header.resizeSection(i_s, s)
666 header.setStretchLastSection(True)
668 if self.proxy_filter:
669 self.selection_model = qc.QItemSelectionModel(self.proxy_filter)
670 else:
671 self.selection_model = qc.QItemSelectionModel(self.marker_model)
673 self.marker_table_view.setSelectionModel(self.selection_model)
674 self.selection_model.selectionChanged.connect(
675 self.set_selected_markers)
677 layout.addWidget(self.marker_table_view, 0, 0)
679 self.pile_viewer = None
680 self._size_hint = qc.QSize(1, 1)
682 def set_viewer(self, viewer):
683 '''
684 Set the pile viewer and connect signals.
685 '''
687 self.pile_viewer = viewer
688 self.marker_model.set_viewer(viewer)
689 self.marker_table_view.set_viewer(viewer)
690 self.pile_viewer.marker_selection_changed.connect(
691 self.update_selection_model)
693 self.pile_viewer.active_event_marker_changed.connect(
694 self.marker_model.handle_active_event_changed)
696 # self.pile_viewer.active_event_marker_changed.connect(
697 # self.marker_table_view.update_viewport)
699 self.marker_table_view.toggle_columns()
701 def set_selected_markers(self, selected, deselected):
702 '''
703 Update selection in viewer to reflect changes in table data.
704 '''
706 if self.proxy_filter:
707 def to_source(x):
708 ind = self.proxy_filter.index(x, 0)
709 return self.proxy_filter.mapToSource(ind).row()
710 else:
711 def to_source(x):
712 return x
714 markers = self.pile_viewer.markers
716 for rsel in selected:
717 for i in range(rsel.top(), rsel.bottom()+1):
718 marker = markers[to_source(i)]
719 if not marker.selected:
720 marker.selected = True
721 self.pile_viewer.n_selected_markers += 1
723 for rsel in deselected:
724 for i in range(rsel.top(), rsel.bottom()+1):
725 marker = markers[to_source(i)]
726 if marker.selected:
727 marker.selected = False
728 self.pile_viewer.n_selected_markers -= 1
730 self.pile_viewer.update()
732 def get_marker_model(self):
733 '''
734 Get the attached Qt table data model.
736 :returns: :py:class:`MarkerTableModel` object
737 '''
739 return self.marker_model
741 def update_selection_model(self, indices):
742 '''
743 Set currently selected table rows.
745 :param indices: begin and end+1 indices of contiguous selection chunks
746 :type indices: list of tuples
747 '''
748 self.selection_model.clearSelection()
749 selections = qc.QItemSelection()
750 selection_flags = qc.QItemSelectionModel.SelectionFlags(int(
751 qc.QItemSelectionModel.Select |
752 qc.QItemSelectionModel.Rows |
753 qc.QItemSelectionModel.Current))
755 for chunk in indices:
756 mi_start = self.marker_model.index(chunk[0], 0)
757 mi_stop = self.marker_model.index(chunk[1]-1, 0)
758 if self.proxy_filter:
759 row_selection = self.proxy_filter.mapSelectionFromSource(
760 qc.QItemSelection(mi_start, mi_stop))
761 else:
762 row_selection = qc.QItemSelection(mi_start, mi_stop)
763 selections.merge(row_selection, selection_flags)
765 if len(indices) != 0:
766 if self.proxy_filter:
767 self.marker_table_view.scrollTo(
768 self.proxy_filter.mapFromSource(mi_start))
769 else:
770 self.marker_table_view.scrollTo(mi_start)
772 self.marker_table_view.setCurrentIndex(mi_start)
773 self.selection_model.setCurrentIndex(
774 mi_start, selection_flags)
776 self.selection_model.select(selections, selection_flags)