Coverage for /usr/local/lib/python3.11/dist-packages/pyrocko/gui/snuffler/marker_editor.py: 59%
490 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-10-06 15:01 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-10-06 15:01 +0000
1# https://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6from ..qt_compat import qc, qg, qw, QPixmapCache
8from .marker import EventMarker, PhaseMarker
9from ..util import make_QPolygonF
10from pyrocko.plot.beachball import mt2beachball, BeachballError
11from pyrocko.moment_tensor import kagan_angle
12from pyrocko.plot import tango_colors
13from pyrocko import orthodrome
14from pyrocko.util import time_to_str
16import logging
18from .marker import g_color_b
21def faint(c):
22 return tuple(255 - (255 - x) // 5 for x in c)
25g_color_b_faint = [faint(c) for c in g_color_b]
28def noop(x=None):
29 return x
32qc.QString = str
33qc.QVariant = noop
36def toFloat(val):
37 try:
38 return float(val), True
39 except (ValueError, TypeError):
40 return 9e99, False
43logger = logging.getLogger('pyrocko.gui.snuffler.marker_editor')
45_header_data = [
46 'T', 'Time', 'M', 'Label', 'Depth [km]', 'Lat', 'Lon', 'Kind', 'Dist [km]',
47 'NSLCs', 'Polarity', 'Kagan Angle [deg]', 'Event Hash', 'MT']
49_column_mapping = dict(zip(_header_data, range(len(_header_data))))
51_string_header = (_column_mapping['Time'], _column_mapping['Label'])
53_header_sizes = [70] * len(_header_data)
54_header_sizes[0] = 40
55_header_sizes[1] = 190
56_header_sizes[-1] = 20
59class BeachballWidget(qw.QWidget):
61 def __init__(self, moment_tensor, color, *args, **kwargs):
62 qw.QWidget.__init__(self, *args, **kwargs)
63 self.color = color
64 self.moment_tensor = moment_tensor
65 self.setGeometry(0, 0, 100, 100)
66 self.setAttribute(qc.Qt.WA_TranslucentBackground)
68 self.flipy = qg.QTransform()
69 self.flipy.translate(0, self.height())
70 self.flipy.scale(1, -1)
72 def paintEvent(self, e):
73 center = e.rect().center()
74 painter = qg.QPainter(self)
75 painter.save()
76 painter.setWorldTransform(self.flipy)
77 try:
78 data = mt2beachball(
79 self.moment_tensor, size=self.height()/2.2,
80 position=(center.x(), center.y()),
81 color_t=self.color, color_p=qc.Qt.white, edgecolor=qc.Qt.black)
82 for pdata in data:
83 paths, fill, edges, thickness = pdata
85 pen = qg.QPen(edges)
86 pen.setWidthF(3)
87 if fill != 'none':
88 brush = qg.QBrush(fill)
89 painter.setBrush(brush)
91 polygon = qg.QPolygonF()
92 polygon = make_QPolygonF(*paths.T)
93 painter.setRenderHint(qg.QPainter.Antialiasing)
94 painter.setPen(pen)
95 painter.drawPolygon(polygon)
96 except BeachballError as e:
97 logger.exception(e)
98 finally:
99 painter.restore()
101 def to_qpixmap(self):
102 try:
103 return self.grab(self.rect())
104 except AttributeError:
105 return qg.QPixmap().grabWidget(self, self.rect())
108class MarkerItemDelegate(qw.QStyledItemDelegate):
110 # Takes care of how table entries are displayed
112 def __init__(self, *args, **kwargs):
113 qw.QStyledItemDelegate.__init__(self, *args, **kwargs)
114 self.c_alignment = qc.Qt.AlignHCenter
115 self.bbcache = QPixmapCache()
117 def paint(self, painter, option, index):
118 mcolor = self.color_from_index(index)
120 if index.column() == _column_mapping['MT']:
121 mt = self.bb_data_from_index(index)
122 if mt:
123 key = ''.join([str(round(x, 1)) for x in mt.m6()])
124 pixmap = self.bbcache.cached(key)
125 if pixmap:
126 pixmap = pixmap.scaledToHeight(option.rect.height())
127 else:
128 pixmap = BeachballWidget(
129 moment_tensor=mt,
130 color=qg.QColor(*tango_colors['scarletred3'])
131 ).to_qpixmap()
132 self.bbcache.insert(key, pixmap)
133 a, b, c, d = option.rect.getRect()
134 painter.save()
135 painter.setRenderHint(qg.QPainter.Antialiasing)
136 painter.drawPixmap(int(round(a+d/2.)), b, d, d, pixmap)
137 painter.restore()
139 else:
140 if index.column() == 0:
141 option.state = option.state & ~qw.QStyle.State_Selected
143 qw.QStyledItemDelegate.paint(self, painter, option, index)
145 marker = self.parent().model().get_marker(index)
147 if marker.active:
149 painter.save()
151 rect = option.rect
152 x1, y1, x2, y2 = rect.getCoords()
153 y1 += 1
154 pen = painter.pen()
155 pen.setWidth(2)
156 pen.setColor(mcolor)
157 painter.setPen(pen)
158 painter.drawLine(qc.QLineF(x1, y1, x2, y1))
159 painter.drawLine(qc.QLineF(x1, y2, x2, y2))
160 painter.restore()
162 def marker_from_index(self, index):
163 tv = self.parent()
164 pv = tv.pile_viewer
165 tvm = tv.model()
166 if isinstance(tvm, qc.QSortFilterProxyModel):
167 return pv.markers[tvm.mapToSource(index).row()]
168 else:
169 return pv.markers[index.row()]
171 def bb_data_from_index(self, index):
172 marker = self.marker_from_index(index)
173 if isinstance(marker, EventMarker):
174 return marker.get_event().moment_tensor
175 else:
176 return None
178 def color_from_index(self, index):
179 marker = self.marker_from_index(index)
180 return qg.QColor(*marker.select_color(g_color_b))
183class MarkerSortFilterProxyModel(qc.QSortFilterProxyModel):
185 # Proxy object between view and model to handle sorting
187 def __init__(self, *args, **kwargs):
188 qc.QSortFilterProxyModel.__init__(self, *args, **kwargs)
189 self.setSortRole(qc.Qt.UserRole)
190 self.sort(1, qc.Qt.AscendingOrder)
192 def get_marker(self, index):
193 return self.sourceModel().get_marker(self.mapToSource(index))
196class MarkerTableView(qw.QTableView):
197 def __init__(self, *args, **kwargs):
198 sortable = kwargs.pop('sortable', True)
199 qw.QTableView.__init__(self, *args, **kwargs)
200 self.setSelectionBehavior(qw.QAbstractItemView.SelectRows)
201 self.setHorizontalScrollMode(qw.QAbstractItemView.ScrollPerPixel)
202 self.setEditTriggers(qw.QAbstractItemView.DoubleClicked)
203 self.setSortingEnabled(sortable)
204 self.setStyleSheet(
205 'QTableView{selection-background-color: \
206 rgba(130, 130, 130, 100% );}')
208 self.sortByColumn(1, qc.Qt.AscendingOrder)
209 self.setAlternatingRowColors(True)
211 self.setShowGrid(False)
212 self.verticalHeader().hide()
213 self.pile_viewer = None
215 self.clicked.connect(self.table_clicked)
216 self.doubleClicked.connect(self.table_double_clicked)
218 self.header_menu = qw.QMenu(self)
220 show_initially = ['Type', 'Time', 'Magnitude']
221 self.menu_labels = ['Type', 'Time', 'Magnitude', 'Label', 'Depth [km]',
222 'Latitude/Longitude', 'Kind', 'Distance [km]',
223 'NSLCs', 'Polarity', 'Kagan Angle [deg]',
224 'Event Hash', 'MT']
226 self.menu_items = dict(zip(
227 self.menu_labels, [0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13]))
229 self.editable_columns = [2, 3, 4, 5, 6, 7]
231 self.column_actions = {}
232 for hd in self.menu_labels:
233 a = qw.QAction(hd, self.header_menu)
234 a.triggered.connect(
235 self.toggle_columns)
236 a.setCheckable(True)
237 a.setChecked(hd in show_initially)
238 self.header_menu.addAction(a)
239 self.column_actions[hd] = a
241 a = qw.QAction('Numbering', self.header_menu)
242 a.setCheckable(True)
243 a.setChecked(False)
244 a.triggered.connect(
245 self.toggle_numbering)
246 self.header_menu.addAction(a)
248 header = self.horizontalHeader()
249 header.setContextMenuPolicy(qc.Qt.CustomContextMenu)
250 header.customContextMenuRequested.connect(
251 self.show_context_menu)
253 self.right_click_menu = qw.QMenu(self)
254 print_action = qw.QAction('Print Table', self.right_click_menu)
255 print_action.triggered.connect(self.print_menu)
256 self.right_click_menu.addAction(print_action)
258 def wheelEvent(self, wheel_event):
259 if wheel_event.modifiers() & qc.Qt.ControlModifier:
260 height = self.rowAt(self.height())
261 ci = self.indexAt(
262 qc.QPoint(self.viewport().rect().x(), height))
263 v = self.verticalHeader()
265 wheel_delta = wheel_event.angleDelta().y()
267 v.setDefaultSectionSize(
268 max(12, v.defaultSectionSize()+wheel_delta//60))
269 self.scrollTo(ci)
270 if v.isVisible():
271 self.toggle_numbering(False)
272 self.toggle_numbering(True)
274 else:
275 super(MarkerTableView, self).wheelEvent(wheel_event)
277 def set_viewer(self, viewer):
278 '''
279 Set connected pile viewer and hook up signals.
280 '''
282 self.pile_viewer = viewer
284 def keyPressEvent(self, key_event):
285 # Propagate key_event to pile_viewer, unless up/down pressed
287 if key_event.key() in [qc.Qt.Key_Up, qc.Qt.Key_Down]:
288 qw.QTableView.keyPressEvent(self, key_event)
289 self.pile_viewer.go_to_selection()
290 else:
291 self.pile_viewer.keyPressEvent(key_event)
293 def table_clicked(self, model_index):
294 # Ignore mouse clicks
295 pass
297 def contextMenuEvent(self, event):
298 self.right_click_menu.popup(qg.QCursor.pos())
300 def toggle_numbering(self, want):
301 if want:
302 self.verticalHeader().show()
303 else:
304 self.verticalHeader().hide()
306 def print_menu(self):
307 from ..qt_compat import qprint
308 printer = qprint.QPrinter(qprint.QPrinter.ScreenResolution)
309 printer.setOutputFormat(qprint.QPrinter.NativeFormat)
310 printer_dialog = qprint.QPrintDialog(printer, self)
311 if printer_dialog.exec_() == qw.QDialog.Accepted:
313 scrollbarpolicy = self.verticalScrollBarPolicy()
314 self.setVerticalScrollBarPolicy(qc.Qt.ScrollBarAlwaysOff)
315 rect = printer.pageRect()
316 painter = qg.QPainter()
317 painter.begin(printer)
318 xscale = rect.width() / (self.width()*1.1)
319 yscale = rect.height() / (self.height() * 1.1)
320 scale = min(xscale, yscale)
321 painter.translate(rect.x() + rect.width()/2.,
322 rect.y() + rect.height()/2.)
323 painter.scale(scale, scale)
324 painter.translate(-self.width()/2., -self.height()/2.)
325 painter.setRenderHints(qg.QPainter.HighQualityAntialiasing |
326 qg.QPainter.TextAntialiasing)
327 self.render(painter)
328 painter.end()
329 self.setVerticalScrollBarPolicy(scrollbarpolicy)
331 def table_double_clicked(self, model_index):
332 if model_index.column() in self.editable_columns:
333 return
334 else:
335 self.pile_viewer.go_to_selection()
337 def show_context_menu(self, point):
338 '''
339 Pop-up menu to toggle column visibility.
340 '''
342 self.header_menu.popup(self.mapToGlobal(point))
344 def toggle_columns(self):
345 '''
346 Toggle columns depending in checked state.
347 '''
349 width = 0
350 for header, ca in self.column_actions.items():
351 hide = not ca.isChecked()
352 self.setColumnHidden(self.menu_items[header], hide)
353 if header == 'Latitude/Longitude':
354 self.setColumnHidden(self.menu_items[header]+1, hide)
355 if not hide:
356 width += _header_sizes[self.menu_labels.index(header)]
358 self.parent().setMinimumWidth(width)
360 def update_viewport(self):
361 self.viewport().update()
364class MarkerTableModel(qc.QAbstractTableModel):
366 def __init__(self, *args, **kwargs):
367 qc.QAbstractTableModel.__init__(self, *args, **kwargs)
368 self.pile_viewer = None
369 self.distances = {}
370 self.kagan_angles = {}
371 self.row_count = 0
372 self.proxy_filter = None
374 def sourceModel(self):
375 return self
377 def set_viewer(self, viewer):
378 '''
379 Set connected pile viewer and hook up signals.
380 '''
382 self.pile_viewer = viewer
383 self.pile_viewer.begin_markers_add.connect(
384 self.begin_markers_add)
385 self.pile_viewer.end_markers_add.connect(
386 self.end_markers_add)
387 self.pile_viewer.begin_markers_remove.connect(
388 self.begin_markers_remove)
389 self.pile_viewer.end_markers_remove.connect(
390 self.end_markers_remove)
392 def rowCount(self, parent=None):
393 if not self.pile_viewer:
394 return 0
395 return len(self.pile_viewer.get_markers())
397 def columnCount(self, parent=None):
398 return len(_column_mapping)
400 def begin_markers_add(self, istart, istop):
401 self.beginInsertRows(qc.QModelIndex(), istart, istop)
403 def end_markers_add(self):
404 self.endInsertRows()
406 def begin_markers_remove(self, istart, istop):
407 self.beginRemoveRows(qc.QModelIndex(), istart, istop)
409 def end_markers_remove(self):
410 self.endRemoveRows()
411 self.marker_table_view.updateGeometries()
413 def headerData(self, col, orientation, role):
414 '''
415 Get header data entry.
416 '''
418 if orientation == qc.Qt.Horizontal:
419 if role == qc.Qt.DisplayRole:
420 return qc.QVariant(_header_data[col])
421 elif role == qc.Qt.SizeHintRole:
422 return qc.QSize(10, 20)
424 elif orientation == qc.Qt.Vertical:
425 if role == qc.Qt.DisplayRole:
426 return qc.QVariant(str(col))
428 else:
429 return qc.QVariant()
431 def get_marker(self, index):
432 return self.pile_viewer.markers[index.row()]
434 def data(self, index, role):
435 '''
436 Get model data entry.
437 '''
439 if not self.pile_viewer:
440 return qc.QVariant()
442 marker = self.pile_viewer.markers[index.row()]
443 column = index.column()
445 if role == qc.Qt.BackgroundRole:
446 if marker.active or column == _column_mapping['T']:
447 return qg.QBrush(
448 qg.QColor(*marker.select_color(g_color_b_faint)))
450 if role == qc.Qt.ForegroundRole:
451 if marker.active or column == _column_mapping['T']:
452 return qg.QBrush(
453 qg.QColor(*marker.select_color(g_color_b)))
455 elif role in (qc.Qt.DisplayRole, qc.Qt.UserRole):
457 v = None
458 if column == _column_mapping['Time']:
459 if role == qc.Qt.UserRole:
460 v = marker.tmin
461 else:
462 v = time_to_str(marker.tmin)
464 elif column == _column_mapping['T']:
465 if isinstance(marker, EventMarker):
466 v = u'\u25ce'
467 elif isinstance(marker, PhaseMarker):
468 v = marker.get_label()
470 elif column == _column_mapping['M']:
471 if isinstance(marker, EventMarker):
472 e = marker.get_event()
473 if e.moment_tensor is not None:
474 v = round(e.moment_tensor.magnitude, 1)
475 elif e.magnitude is not None:
476 v = round(e.magnitude, 1)
478 elif column == _column_mapping['Label']:
479 if isinstance(marker, EventMarker):
480 v = marker.label()
481 elif isinstance(marker, PhaseMarker):
482 v = marker.get_label()
484 elif column == _column_mapping['Depth [km]']:
485 if isinstance(marker, EventMarker):
486 d = marker.get_event().depth
487 if d is not None:
488 v = round(marker.get_event().depth/1000., 1)
490 elif column == _column_mapping['Lat']:
491 if isinstance(marker, EventMarker):
492 v = round(marker.get_event().effective_lat, 2)
494 elif column == _column_mapping['Lon']:
495 if isinstance(marker, EventMarker):
496 v = round(marker.get_event().effective_lon, 2)
498 elif column == _column_mapping['Kind']:
499 v = marker.kind
501 elif column == _column_mapping['Dist [km]']:
502 active_event = self.pile_viewer.get_active_event()
503 if isinstance(marker, EventMarker) \
504 and active_event is not None:
506 dist = orthodrome.distance_accurate50m(
507 marker.get_event(),
508 active_event)
510 v = dist
511 if role == qc.Qt.DisplayRole:
512 v = '%.5g' % (v/1000.)
514 elif column == _column_mapping['NSLCs']:
515 strs = []
516 for nslc_id in marker.get_nslc_ids():
517 strs.append('.'.join(nslc_id))
518 v = '|'.join(strs)
520 elif column == _column_mapping['Kagan Angle [deg]']:
521 active_event = self.pile_viewer.get_active_event()
522 if isinstance(marker, EventMarker) \
523 and active_event is not None \
524 and active_event.moment_tensor is not None \
525 and marker.get_event().moment_tensor is not None:
527 v = kagan_angle(
528 active_event.moment_tensor,
529 marker.get_event().moment_tensor)
531 if role == qc.Qt.DisplayRole:
532 v = '%.1f' % v
534 elif column == _column_mapping['MT']:
535 return qc.QVariant()
537 elif column == _column_mapping['Event Hash']:
538 if isinstance(marker, (EventMarker, PhaseMarker)):
539 v = marker.get_event_hash()
540 else:
541 return qc.QVariant()
543 elif column == _column_mapping['Polarity']:
544 if isinstance(marker, (PhaseMarker)):
545 v = marker.get_polarity_symbol()
546 else:
547 return qc.QVariant()
549 return qc.QVariant(v)
551 return qc.QVariant()
553 def handle_active_event_changed(self):
554 nmarkers = self.rowCount()
555 istart = self.index(0, _column_mapping['Dist [km]'])
556 istop = self.index(nmarkers-1, _column_mapping['Dist [km]'])
557 self.dataChanged.emit(istart, istop)
559 istart = self.index(0, _column_mapping['Kagan Angle [deg]'])
560 istop = self.index(nmarkers-1, _column_mapping['Kagan Angle [deg]'])
561 self.dataChanged.emit(istart, istop)
563 def done(self, index):
564 self.dataChanged.emit(index, index)
565 return True
567 def setData(self, index, value, role):
568 '''
569 Set model data entry.
570 '''
572 if role == qc.Qt.EditRole:
573 imarker = index.row()
574 marker = self.pile_viewer.markers[imarker]
575 if index.column() in [_column_mapping[c] for c in [
576 'M', 'Lat', 'Lon', 'Depth [km]']]:
578 if not isinstance(marker, EventMarker):
579 return False
580 else:
581 if index.column() == _column_mapping['M']:
582 valuef, valid = toFloat(value)
583 if valid:
584 e = marker.get_event()
585 if e.moment_tensor is None:
586 e.magnitude = valuef
587 else:
588 e.moment_tensor.magnitude = valuef
589 return self.done(index)
591 if index.column() in [_column_mapping['Lon'],
592 _column_mapping['Lat'],
593 _column_mapping['Depth [km]']]:
594 if isinstance(marker, EventMarker):
595 valuef, valid = toFloat(value)
596 if valid:
597 if index.column() == _column_mapping['Lat']:
598 marker.get_event().lat = valuef
599 elif index.column() == _column_mapping['Lon']:
600 marker.get_event().lon = valuef
601 elif index.column() == _column_mapping[
602 'Depth [km]']:
603 marker.get_event().depth = valuef*1000.
604 return self.done(index)
606 if index.column() == _column_mapping['Label']:
607 values = str(value)
608 if values != '':
609 if isinstance(marker, EventMarker):
610 marker.get_event().set_name(values)
611 return self.done(index)
613 if isinstance(marker, PhaseMarker):
614 marker.set_phasename(values)
615 return self.done(index)
617 return False
619 def flags(self, index):
620 '''
621 Set flags for cells which the user can edit.
622 '''
624 if index.column() not in self.marker_table_view.editable_columns:
625 return qc.Qt.ItemFlags(33)
626 else:
627 if isinstance(self.pile_viewer.markers[index.row()], EventMarker):
628 if index.column() in self.marker_table_view.editable_columns:
629 return qc.Qt.ItemFlags(35)
630 if index.column() == _column_mapping['Label']:
631 return qc.Qt.ItemFlags(35)
632 return qc.Qt.ItemFlags(33)
635class MarkerEditor(qw.QFrame):
637 def __init__(self, *args, **kwargs):
638 sortable = kwargs.pop('sortable', True)
639 qw.QFrame.__init__(self, *args, **kwargs)
640 layout = qw.QGridLayout()
641 layout.setContentsMargins(0, 0, 0, 0)
642 self.setLayout(layout)
643 self.marker_table_view = MarkerTableView(self, sortable=sortable)
645 self.delegate = MarkerItemDelegate(self.marker_table_view)
646 self.marker_table_view.setItemDelegate(self.delegate)
648 self.marker_model = MarkerTableModel()
649 self.marker_model.marker_table_view = self.marker_table_view
651 if sortable:
652 self.proxy_filter = MarkerSortFilterProxyModel()
653 self.proxy_filter.setDynamicSortFilter(True)
654 self.proxy_filter.setSourceModel(self.marker_model)
655 self.marker_model.proxy_filter = self.proxy_filter
657 self.marker_table_view.setModel(self.proxy_filter)
658 else:
659 self.proxy_filter = None
660 self.marker_table_view.setModel(self.marker_model)
662 header = self.marker_table_view.horizontalHeader()
663 for i_s, s in enumerate(_header_sizes):
664 header.setSectionResizeMode(i_s, qw.QHeaderView.Interactive)
665 header.resizeSection(i_s, s)
667 header.setStretchLastSection(True)
669 if self.proxy_filter:
670 self.selection_model = qc.QItemSelectionModel(self.proxy_filter)
671 else:
672 self.selection_model = qc.QItemSelectionModel(self.marker_model)
674 self.marker_table_view.setSelectionModel(self.selection_model)
675 self.selection_model.selectionChanged.connect(
676 self.set_selected_markers)
678 layout.addWidget(self.marker_table_view, 0, 0)
680 self.pile_viewer = None
681 self._size_hint = qc.QSize(1, 1)
683 def set_viewer(self, viewer):
684 '''
685 Set the pile viewer and connect signals.
686 '''
688 self.pile_viewer = viewer
689 self.marker_model.set_viewer(viewer)
690 self.marker_table_view.set_viewer(viewer)
691 self.pile_viewer.marker_selection_changed.connect(
692 self.update_selection_model)
694 self.pile_viewer.active_event_marker_changed.connect(
695 self.marker_model.handle_active_event_changed)
697 # self.pile_viewer.active_event_marker_changed.connect(
698 # self.marker_table_view.update_viewport)
700 self.marker_table_view.toggle_columns()
702 def set_selected_markers(self, selected, deselected):
703 '''
704 Update selection in viewer to reflect changes in table data.
705 '''
707 if self.proxy_filter:
708 def to_source(x):
709 ind = self.proxy_filter.index(x, 0)
710 return self.proxy_filter.mapToSource(ind).row()
711 else:
712 def to_source(x):
713 return x
715 markers = self.pile_viewer.markers
717 for rsel in selected:
718 for i in range(rsel.top(), rsel.bottom()+1):
719 marker = markers[to_source(i)]
720 if not marker.selected:
721 marker.selected = True
722 self.pile_viewer.n_selected_markers += 1
724 for rsel in deselected:
725 for i in range(rsel.top(), rsel.bottom()+1):
726 marker = markers[to_source(i)]
727 if marker.selected:
728 marker.selected = False
729 self.pile_viewer.n_selected_markers -= 1
731 self.pile_viewer.update()
733 def get_marker_model(self):
734 '''
735 Get the attached Qt table data model.
737 :returns: :py:class:`MarkerTableModel` object
738 '''
740 return self.marker_model
742 def update_selection_model(self, indices):
743 '''
744 Set currently selected table rows.
746 :param indices: begin and end+1 indices of contiguous selection chunks
747 :type indices: list of tuples
748 '''
749 self.selection_model.clearSelection()
750 selections = qc.QItemSelection()
751 selection_flags = qc.QItemSelectionModel.SelectionFlags(int(
752 qc.QItemSelectionModel.Select |
753 qc.QItemSelectionModel.Rows |
754 qc.QItemSelectionModel.Current))
756 for chunk in indices:
757 mi_start = self.marker_model.index(chunk[0], 0)
758 mi_stop = self.marker_model.index(chunk[1]-1, 0)
759 if self.proxy_filter:
760 row_selection = self.proxy_filter.mapSelectionFromSource(
761 qc.QItemSelection(mi_start, mi_stop))
762 else:
763 row_selection = qc.QItemSelection(mi_start, mi_stop)
764 selections.merge(row_selection, selection_flags)
766 if len(indices) != 0:
767 if self.proxy_filter:
768 self.marker_table_view.scrollTo(
769 self.proxy_filter.mapFromSource(mi_start))
770 else:
771 self.marker_table_view.scrollTo(mi_start)
773 self.marker_table_view.setCurrentIndex(mi_start)
774 self.selection_model.setCurrentIndex(
775 mi_start, selection_flags)
777 self.selection_model.select(selections, selection_flags)