1# http://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

4# ---|P------/S----------~Lg---------- 

5from __future__ import absolute_import, print_function 

6 

7import sys 

8import os 

9import time 

10import calendar 

11import datetime 

12import re 

13import math 

14import logging 

15import operator 

16import copy 

17from itertools import groupby 

18 

19import numpy as num 

20import pyrocko.model 

21import pyrocko.pile 

22import pyrocko.shadow_pile 

23import pyrocko.trace 

24import pyrocko.util 

25import pyrocko.plot 

26import pyrocko.gui.snuffling 

27import pyrocko.gui.snufflings 

28import pyrocko.gui.marker_editor 

29 

30from pyrocko.util import hpfloat, gmtime_x, mystrftime 

31 

32from .marker import associate_phases_to_events, MarkerOneNSLCRequired 

33 

34from .util import (ValControl, LinValControl, Marker, EventMarker, 

35 PhaseMarker, make_QPolygonF, draw_label, Label, 

36 Progressbars) 

37 

38from .qt_compat import qc, qg, qw, qgl, qsvg, use_pyqt5 

39 

40import scipy.stats as sstats 

41import platform 

42 

43try: 

44 newstr = unicode 

45except NameError: 

46 newstr = str 

47 

48 

49def fnpatch(x): 

50 if use_pyqt5: 

51 return x 

52 else: 

53 return x, None 

54 

55 

56if sys.version_info[0] >= 3: 

57 qc.QString = str 

58 

59qfiledialog_options = qw.QFileDialog.DontUseNativeDialog | \ 

60 qw.QFileDialog.DontUseSheet 

61 

62if platform.mac_ver() != ('', ('', '', ''), ''): 

63 macosx = True 

64else: 

65 macosx = False 

66 

67logger = logging.getLogger('pyrocko.gui.pile_viewer') 

68 

69 

70def detrend(x, y): 

71 slope, offset, _, _, _ = sstats.linregress(x, y) 

72 y_detrended = y - slope * x - offset 

73 return y_detrended, slope, offset 

74 

75 

76def retrend(x, y_detrended, slope, offset): 

77 return x * slope + y_detrended + offset 

78 

79 

80class Global(object): 

81 appOnDemand = None 

82 

83 

84class NSLC(object): 

85 def __init__(self, n, s, l=None, c=None): # noqa 

86 self.network = n 

87 self.station = s 

88 self.location = l 

89 self.channel = c 

90 

91 

92class m_float(float): 

93 

94 def __str__(self): 

95 if abs(self) >= 10000.: 

96 return '%g km' % round(self/1000., 0) 

97 elif abs(self) >= 1000.: 

98 return '%g km' % round(self/1000., 1) 

99 else: 

100 return '%.5g m' % self 

101 

102 def __lt__(self, other): 

103 if other is None: 

104 return True 

105 return float(self) < float(other) 

106 

107 def __gt__(self, other): 

108 if other is None: 

109 return False 

110 return float(self) > float(other) 

111 

112 

113def m_float_or_none(x): 

114 if x is None: 

115 return None 

116 else: 

117 return m_float(x) 

118 

119 

120def make_chunks(items): 

121 ''' 

122 Split a list of integers into sublists of consecutive elements. 

123 ''' 

124 return [list(map(operator.itemgetter(1), g)) for k, g in groupby( 

125 enumerate(items), (lambda x: x[1]-x[0]))] 

126 

127 

128class deg_float(float): 

129 

130 def __str__(self): 

131 return '%4.0f' % self 

132 

133 

134def deg_float_or_none(x): 

135 if x is None: 

136 return None 

137 else: 

138 return deg_float(x) 

139 

140 

141class sector_int(int): 

142 

143 def __str__(self): 

144 return '[%i]' % self 

145 

146 

147def num_to_html(num): 

148 snum = '%g' % num 

149 m = re.match(r'(.+)[eE]([+-]?\d+)$', snum) 

150 if m: 

151 snum = m.group(1) + ' &times; 10<sup>%i</sup>' % int(m.group(2)) 

152 

153 return snum 

154 

155 

156gap_lap_tolerance = 5. 

157 

158 

159class Timer(object): 

160 def __init__(self): 

161 self._start = None 

162 self._stop = None 

163 

164 def start(self): 

165 self._start = os.times() 

166 

167 def stop(self): 

168 self._stop = os.times() 

169 

170 def get(self): 

171 a = self._start 

172 b = self._stop 

173 if a is not None and b is not None: 

174 return tuple([b[i] - a[i] for i in range(5)]) 

175 else: 

176 return tuple([0.] * 5) 

177 

178 def __sub__(self, other): 

179 a = self.get() 

180 b = other.get() 

181 return tuple([a[i] - b[i] for i in range(5)]) 

182 

183 

184class Integrator(pyrocko.shadow_pile.ShadowPile): 

185 

186 def process(self, iblock, tmin, tmax, traces): 

187 for trace in traces: 

188 trace.ydata = trace.ydata - trace.ydata.mean() 

189 trace.ydata = num.cumsum(trace.ydata) 

190 

191 return traces 

192 

193 

194class ObjectStyle(object): 

195 def __init__(self, frame_pen, fill_brush): 

196 self.frame_pen = frame_pen 

197 self.fill_brush = fill_brush 

198 

199 

200box_styles = [] 

201box_alpha = 100 

202for color in 'orange skyblue butter chameleon chocolate plum ' \ 

203 'scarletred'.split(): 

204 

205 box_styles.append(ObjectStyle( 

206 qg.QPen(qg.QColor(*pyrocko.plot.tango_colors[color+'3'])), 

207 qg.QBrush(qg.QColor( 

208 *(pyrocko.plot.tango_colors[color+'1'] + (box_alpha,)))), 

209 )) 

210 

211sday = 60*60*24. # \ 

212smonth = 60*60*24*30. # | only used as approx. intervals... 

213syear = 60*60*24*365. # / 

214 

215acceptable_tincs = num.array([ 

216 1, 2, 5, 10, 20, 30, 60, 60*5, 60*10, 60*20, 60*30, 60*60, 60*60*3, 

217 60*60*6, 60*60*12, sday, smonth, syear], dtype=float) 

218 

219 

220working_system_time_range = \ 

221 pyrocko.util.working_system_time_range() 

222 

223initial_time_range = [] 

224 

225try: 

226 initial_time_range.append( 

227 calendar.timegm((1950, 1, 1, 0, 0, 0))) 

228except Exception: 

229 initial_time_range.append(working_system_time_range[0]) 

230 

231try: 

232 initial_time_range.append( 

233 calendar.timegm((time.gmtime().tm_year + 11, 1, 1, 0, 0, 0))) 

234except Exception: 

235 initial_time_range.append(working_system_time_range[1]) 

236 

237 

238def is_working_time(t): 

239 return working_system_time_range[0] <= t and \ 

240 t <= working_system_time_range[1] 

241 

242 

243def fancy_time_ax_format(inc): 

244 l0_fmt_brief = '' 

245 l2_fmt = '' 

246 l2_trig = 0 

247 if inc < 0.000001: 

248 l0_fmt = '.%n' 

249 l0_center = False 

250 l1_fmt = '%H:%M:%S' 

251 l1_trig = 6 

252 l2_fmt = '%b %d, %Y' 

253 l2_trig = 3 

254 elif inc < 0.001: 

255 l0_fmt = '.%u' 

256 l0_center = False 

257 l1_fmt = '%H:%M:%S' 

258 l1_trig = 6 

259 l2_fmt = '%b %d, %Y' 

260 l2_trig = 3 

261 elif inc < 1: 

262 l0_fmt = '.%r' 

263 l0_center = False 

264 l1_fmt = '%H:%M:%S' 

265 l1_trig = 6 

266 l2_fmt = '%b %d, %Y' 

267 l2_trig = 3 

268 elif inc < 60: 

269 l0_fmt = '%H:%M:%S' 

270 l0_center = False 

271 l1_fmt = '%b %d, %Y' 

272 l1_trig = 3 

273 elif inc < 3600: 

274 l0_fmt = '%H:%M' 

275 l0_center = False 

276 l1_fmt = '%b %d, %Y' 

277 l1_trig = 3 

278 elif inc < sday: 

279 l0_fmt = '%H:%M' 

280 l0_center = False 

281 l1_fmt = '%b %d, %Y' 

282 l1_trig = 3 

283 elif inc < smonth: 

284 l0_fmt = '%a %d' 

285 l0_fmt_brief = '%d' 

286 l0_center = True 

287 l1_fmt = '%b, %Y' 

288 l1_trig = 2 

289 elif inc < syear: 

290 l0_fmt = '%b' 

291 l0_center = True 

292 l1_fmt = '%Y' 

293 l1_trig = 1 

294 else: 

295 l0_fmt = '%Y' 

296 l0_center = False 

297 l1_fmt = '' 

298 l1_trig = 0 

299 

300 return l0_fmt, l0_fmt_brief, l0_center, l1_fmt, l1_trig, l2_fmt, l2_trig 

301 

302 

303def day_start(timestamp): 

304 tt = time.gmtime(int(timestamp)) 

305 tts = tt[0:3] + (0, 0, 0) + tt[6:9] 

306 return calendar.timegm(tts) 

307 

308 

309def month_start(timestamp): 

310 tt = time.gmtime(int(timestamp)) 

311 tts = tt[0:2] + (1, 0, 0, 0) + tt[6:9] 

312 return calendar.timegm(tts) 

313 

314 

315def year_start(timestamp): 

316 tt = time.gmtime(int(timestamp)) 

317 tts = tt[0:1] + (1, 1, 0, 0, 0) + tt[6:9] 

318 return calendar.timegm(tts) 

319 

320 

321def time_nice_value(inc0): 

322 if inc0 < acceptable_tincs[0]: 

323 return pyrocko.plot.nice_value(inc0) 

324 elif inc0 > acceptable_tincs[-1]: 

325 return pyrocko.plot.nice_value(inc0/syear)*syear 

326 else: 

327 i = num.argmin(num.abs(acceptable_tincs-inc0)) 

328 return acceptable_tincs[i] 

329 

330 

331class TimeScaler(pyrocko.plot.AutoScaler): 

332 def __init__(self): 

333 pyrocko.plot.AutoScaler.__init__(self) 

334 self.mode = 'min-max' 

335 

336 def make_scale(self, data_range): 

337 assert self.mode in ('min-max', 'off'), \ 

338 'mode must be "min-max" or "off" for TimeScaler' 

339 

340 data_min = min(data_range) 

341 data_max = max(data_range) 

342 is_reverse = (data_range[0] > data_range[1]) 

343 

344 mi, ma = data_min, data_max 

345 nmi = mi 

346 if self.mode != 'off': 

347 nmi = mi - self.space*(ma-mi) 

348 

349 nma = ma 

350 if self.mode != 'off': 

351 nma = ma + self.space*(ma-mi) 

352 

353 mi, ma = nmi, nma 

354 

355 if mi == ma and self.mode != 'off': 

356 mi -= 1.0 

357 ma += 1.0 

358 

359 mi = max(working_system_time_range[0], mi) 

360 ma = min(working_system_time_range[1], ma) 

361 

362 # make nice tick increment 

363 if self.inc is not None: 

364 inc = self.inc 

365 else: 

366 if self.approx_ticks > 0.: 

367 inc = time_nice_value((ma-mi)/self.approx_ticks) 

368 else: 

369 inc = time_nice_value((ma-mi)*10.) 

370 

371 if inc == 0.0: 

372 inc = 1.0 

373 

374 if is_reverse: 

375 return ma, mi, -inc 

376 else: 

377 return mi, ma, inc 

378 

379 def make_ticks(self, data_range): 

380 mi, ma, inc = self.make_scale(data_range) 

381 

382 is_reverse = False 

383 if inc < 0: 

384 mi, ma, inc = ma, mi, -inc 

385 is_reverse = True 

386 

387 ticks = [] 

388 

389 if inc < sday: 

390 mi_day = day_start(max(mi, working_system_time_range[0]+sday*1.5)) 

391 if inc < 0.001: 

392 mi_day = hpfloat(mi_day) 

393 

394 base = mi_day+num.ceil((mi-mi_day)/inc)*inc 

395 if inc < 0.001: 

396 base = hpfloat(base) 

397 

398 base_day = mi_day 

399 i = 0 

400 while True: 

401 tick = base+i*inc 

402 if tick > ma: 

403 break 

404 

405 tick_day = day_start(tick) 

406 if tick_day > base_day: 

407 base_day = tick_day 

408 base = base_day 

409 i = 0 

410 else: 

411 ticks.append(tick) 

412 i += 1 

413 

414 elif inc < smonth: 

415 mi_day = day_start(max(mi, working_system_time_range[0]+sday*1.5)) 

416 dt_base = datetime.datetime(*time.gmtime(mi_day)[:6]) 

417 delta = datetime.timedelta(days=int(round(inc/sday))) 

418 if mi_day == mi: 

419 dt_base += delta 

420 i = 0 

421 while True: 

422 current = dt_base + i*delta 

423 tick = calendar.timegm(current.timetuple()) 

424 if tick > ma: 

425 break 

426 ticks.append(tick) 

427 i += 1 

428 

429 elif inc < syear: 

430 mi_month = month_start(max( 

431 mi, working_system_time_range[0]+smonth*1.5)) 

432 

433 y, m = time.gmtime(mi_month)[:2] 

434 while True: 

435 tick = calendar.timegm((y, m, 1, 0, 0, 0)) 

436 m += 1 

437 if m > 12: 

438 y, m = y+1, 1 

439 

440 if tick > ma: 

441 break 

442 

443 if tick >= mi: 

444 ticks.append(tick) 

445 

446 else: 

447 mi_year = year_start(max( 

448 mi, working_system_time_range[0]+syear*1.5)) 

449 

450 incy = int(round(inc/syear)) 

451 y = int(num.ceil(time.gmtime(mi_year)[0]/incy)*incy) 

452 

453 while True: 

454 tick = calendar.timegm((y, 1, 1, 0, 0, 0)) 

455 y += incy 

456 if tick > ma: 

457 break 

458 if tick >= mi: 

459 ticks.append(tick) 

460 

461 if is_reverse: 

462 ticks.reverse() 

463 

464 return ticks, inc 

465 

466 

467def need_l1_tick(tt, ms, l1_trig): 

468 return (0, 1, 1, 0, 0, 0)[l1_trig:] == tt[l1_trig:6] and ms == 0.0 

469 

470 

471def tick_to_labels(tick, inc): 

472 tt, ms = gmtime_x(tick) 

473 l0_fmt, l0_fmt_brief, l0_center, l1_fmt, l1_trig, l2_fmt, l2_trig = \ 

474 fancy_time_ax_format(inc) 

475 

476 l0 = mystrftime(l0_fmt, tt, ms) 

477 l0_brief = mystrftime(l0_fmt_brief, tt, ms) 

478 l1, l2 = None, None 

479 if need_l1_tick(tt, ms, l1_trig): 

480 l1 = mystrftime(l1_fmt, tt, ms) 

481 if need_l1_tick(tt, ms, l2_trig): 

482 l2 = mystrftime(l2_fmt, tt, ms) 

483 

484 return l0, l0_brief, l0_center, l1, l2 

485 

486 

487def l1_l2_tick(tick, inc): 

488 tt, ms = gmtime_x(tick) 

489 l0_fmt, l0_fmt_brief, l0_center, l1_fmt, l1_trig, l2_fmt, l2_trig = \ 

490 fancy_time_ax_format(inc) 

491 

492 l1 = mystrftime(l1_fmt, tt, ms) 

493 l2 = mystrftime(l2_fmt, tt, ms) 

494 return l1, l2 

495 

496 

497class TimeAx(TimeScaler): 

498 def __init__(self, *args): 

499 TimeScaler.__init__(self, *args) 

500 

501 def drawit(self, p, xprojection, yprojection): 

502 pen = qg.QPen(qg.QColor(*pyrocko.plot.tango_colors['aluminium5']), 1) 

503 p.setPen(pen) 

504 font = qg.QFont() 

505 font.setBold(True) 

506 p.setFont(font) 

507 fm = p.fontMetrics() 

508 ticklen = 10 

509 pad = 10 

510 tmin, tmax = xprojection.get_in_range() 

511 ticks, inc = self.make_ticks((tmin, tmax)) 

512 l1_hits = 0 

513 l2_hits = 0 

514 

515 vmin, vmax = yprojection(0), yprojection(ticklen) 

516 uumin, uumax = xprojection.get_out_range() 

517 first_tick_with_label = None 

518 for tick in ticks: 

519 umin = xprojection(tick) 

520 

521 umin_approx_next = xprojection(tick+inc) 

522 umax = xprojection(tick) 

523 

524 pinc_approx = umin_approx_next - umin 

525 

526 p.drawLine(qc.QPointF(umin, vmin), qc.QPointF(umax, vmax)) 

527 l0, l0_brief, l0_center, l1, l2 = tick_to_labels(tick, inc) 

528 

529 if tick == 0.0 and tmax - tmin < 3600*24: 

530 # hide year at epoch (we assume that synthetic data is shown) 

531 if l2: 

532 l2 = None 

533 elif l1: 

534 l1 = None 

535 

536 if l0_center: 

537 ushift = (umin_approx_next-umin)/2. 

538 else: 

539 ushift = 0. 

540 

541 for l0x in (l0, l0_brief, ''): 

542 label0 = l0x 

543 rect0 = fm.boundingRect(label0) 

544 if rect0.width() <= pinc_approx*0.9: 

545 break 

546 

547 if uumin+pad < umin-rect0.width()/2.+ushift and \ 

548 umin+rect0.width()/2.+ushift < uumax-pad: 

549 

550 if first_tick_with_label is None: 

551 first_tick_with_label = tick 

552 p.drawText(qc.QPointF( 

553 umin-rect0.width()/2.+ushift, 

554 vmin+rect0.height()+ticklen), label0) 

555 

556 if l1: 

557 label1 = l1 

558 rect1 = fm.boundingRect(label1) 

559 if uumin+pad < umin-rect1.width()/2. and \ 

560 umin+rect1.width()/2. < uumax-pad: 

561 

562 p.drawText(qc.QPointF( 

563 umin-rect1.width()/2., 

564 vmin+rect0.height()+rect1.height()+ticklen), 

565 label1) 

566 

567 l1_hits += 1 

568 

569 if l2: 

570 label2 = l2 

571 rect2 = fm.boundingRect(label2) 

572 if uumin+pad < umin-rect2.width()/2. and \ 

573 umin+rect2.width()/2. < uumax-pad: 

574 

575 p.drawText(qc.QPointF( 

576 umin-rect2.width()/2., 

577 vmin+rect0.height()+rect1.height()+rect2.height() + 

578 ticklen), label2) 

579 

580 l2_hits += 1 

581 

582 if first_tick_with_label is None: 

583 first_tick_with_label = tmin 

584 

585 l1, l2 = l1_l2_tick(first_tick_with_label, inc) 

586 

587 if -3600.*25 < first_tick_with_label <= 3600.*25 and \ 

588 tmax - tmin < 3600*24: 

589 

590 # hide year at epoch (we assume that synthetic data is shown) 

591 if l2: 

592 l2 = None 

593 elif l1: 

594 l1 = None 

595 

596 if l1_hits == 0 and l1: 

597 label1 = l1 

598 rect1 = fm.boundingRect(label1) 

599 p.drawText(qc.QPointF( 

600 uumin+pad, 

601 vmin+rect0.height()+rect1.height()+ticklen), 

602 label1) 

603 

604 l1_hits += 1 

605 

606 if l2_hits == 0 and l2: 

607 label2 = l2 

608 rect2 = fm.boundingRect(label2) 

609 p.drawText(qc.QPointF( 

610 uumin+pad, 

611 vmin+rect0.height()+rect1.height()+rect2.height()+ticklen), 

612 label2) 

613 

614 v = yprojection(0) 

615 p.drawLine(qc.QPointF(uumin, v), qc.QPointF(uumax, v)) 

616 

617 

618class Projection(object): 

619 def __init__(self): 

620 self.xr = 0., 1. 

621 self.ur = 0., 1. 

622 

623 def set_in_range(self, xmin, xmax): 

624 if xmax == xmin: 

625 xmax = xmin + 1. 

626 

627 self.xr = xmin, xmax 

628 

629 def get_in_range(self): 

630 return self.xr 

631 

632 def set_out_range(self, umin, umax): 

633 if umax == umin: 

634 umax = umin + 1. 

635 

636 self.ur = umin, umax 

637 

638 def get_out_range(self): 

639 return self.ur 

640 

641 def __call__(self, x): 

642 umin, umax = self.ur 

643 xmin, xmax = self.xr 

644 return umin + (x-xmin)*((umax-umin)/(xmax-xmin)) 

645 

646 def clipped(self, x): 

647 umin, umax = self.ur 

648 xmin, xmax = self.xr 

649 return min(umax, max(umin, umin + (x-xmin)*((umax-umin)/(xmax-xmin)))) 

650 

651 def rev(self, u): 

652 umin, umax = self.ur 

653 xmin, xmax = self.xr 

654 return xmin + (u-umin)*((xmax-xmin)/(umax-umin)) 

655 

656 def copy(self): 

657 return copy.copy(self) 

658 

659 

660def add_radiobuttongroup(menu, menudef, obj, target, default=None): 

661 group = qw.QActionGroup(menu) 

662 menuitems = [] 

663 for name, v in menudef: 

664 k = qw.QAction(name, menu) 

665 group.addAction(k) 

666 menu.addAction(k) 

667 k.setCheckable(True) 

668 group.triggered.connect(target) 

669 menuitems.append((k, v)) 

670 if default is not None: 

671 if name.lower().replace(' ', '_') == default: 

672 k.setChecked(True) 

673 

674 if default is None: 

675 menuitems[0][0].setChecked(True) 

676 return menuitems 

677 

678 

679def sort_actions(menu): 

680 actions = menu.actions() 

681 for action in actions: 

682 menu.removeAction(action) 

683 actions.sort(key=lambda x: newstr(x.text())) 

684 

685 help_action = [a for a in actions if a.text() == 'Snuffler Controls'] 

686 if help_action: 

687 actions.insert(0, actions.pop(actions.index(help_action[0]))) 

688 for action in actions: 

689 menu.addAction(action) 

690 

691 

692fkey_map = dict(zip( 

693 (qc.Qt.Key_F1, qc.Qt.Key_F2, qc.Qt.Key_F3, qc.Qt.Key_F4, qc.Qt.Key_F5, 

694 qc.Qt.Key_F6, qc.Qt.Key_F7, qc.Qt.Key_F8, qc.Qt.Key_F9, qc.Qt.Key_F10, 

695 qc.Qt.Key_F11, qc.Qt.Key_F12), 

696 range(12))) 

697 

698 

699class PileViewerMainException(Exception): 

700 pass 

701 

702 

703def MakePileViewerMainClass(base): 

704 

705 class PileViewerMain(base): 

706 

707 want_input = qc.pyqtSignal() 

708 about_to_close = qc.pyqtSignal() 

709 pile_has_changed_signal = qc.pyqtSignal() 

710 tracks_range_changed = qc.pyqtSignal(int, int, int) 

711 

712 begin_markers_add = qc.pyqtSignal(int, int) 

713 end_markers_add = qc.pyqtSignal() 

714 begin_markers_remove = qc.pyqtSignal(int, int) 

715 end_markers_remove = qc.pyqtSignal() 

716 

717 marker_selection_changed = qc.pyqtSignal(list) 

718 active_event_marker_changed = qc.pyqtSignal() 

719 

720 def __init__(self, pile, ntracks_shown_max, panel_parent, *args): 

721 if base == qgl.QGLWidget: 

722 from OpenGL import GL # noqa 

723 

724 base.__init__( 

725 self, qgl.QGLFormat(qgl.QGL.SampleBuffers), *args) 

726 else: 

727 base.__init__(self, *args) 

728 

729 self.pile = pile 

730 self.ax_height = 80 

731 self.panel_parent = panel_parent 

732 

733 self.click_tolerance = 5 

734 

735 self.ntracks_shown_max = ntracks_shown_max 

736 self.initial_ntracks_shown_max = ntracks_shown_max 

737 self.ntracks = 0 

738 self.show_all = True 

739 self.shown_tracks_range = None 

740 self.track_start = None 

741 self.track_trange = None 

742 

743 self.lowpass = None 

744 self.highpass = None 

745 self.gain = 1.0 

746 self.rotate = 0.0 

747 self.picking_down = None 

748 self.picking = None 

749 self.floating_marker = None 

750 self.markers = pyrocko.pile.Sorted([], 'tmin') 

751 self.markers_deltat_max = 0. 

752 self.n_selected_markers = 0 

753 self.all_marker_kinds = (0, 1, 2, 3, 4, 5) 

754 self.visible_marker_kinds = self.all_marker_kinds 

755 self.active_event_marker = None 

756 self.ignore_releases = 0 

757 self.message = None 

758 self.reloaded = False 

759 self.pile_has_changed = False 

760 self.config = pyrocko.config.config('snuffler') 

761 

762 self.tax = TimeAx() 

763 self.setBackgroundRole(qg.QPalette.Base) 

764 self.setAutoFillBackground(True) 

765 poli = qw.QSizePolicy( 

766 qw.QSizePolicy.Expanding, 

767 qw.QSizePolicy.Expanding) 

768 

769 self.setSizePolicy(poli) 

770 self.setMinimumSize(300, 200) 

771 self.setFocusPolicy(qc.Qt.ClickFocus) 

772 

773 self.menu = qw.QMenu(self) 

774 

775 mi = qw.QAction('Open waveform files...', self.menu) 

776 self.menu.addAction(mi) 

777 mi.triggered.connect(self.open_waveforms) 

778 

779 mi = qw.QAction('Open waveform directory...', self.menu) 

780 self.menu.addAction(mi) 

781 mi.triggered.connect(self.open_waveform_directory) 

782 

783 mi = qw.QAction('Open station files...', self.menu) 

784 self.menu.addAction(mi) 

785 mi.triggered.connect(self.open_stations) 

786 

787 mi = qw.QAction('Open StationXML files...', self.menu) 

788 self.menu.addAction(mi) 

789 mi.triggered.connect(self.open_stations_xml) 

790 

791 mi = qw.QAction('Save markers...', self.menu) 

792 self.menu.addAction(mi) 

793 mi.triggered.connect(self.write_markers) 

794 

795 mi = qw.QAction('Save selected markers...', self.menu) 

796 self.menu.addAction(mi) 

797 mi.triggered.connect(self.write_selected_markers) 

798 

799 mi = qw.QAction('Open marker file...', self.menu) 

800 self.menu.addAction(mi) 

801 mi.triggered.connect(self.read_markers) 

802 

803 mi = qw.QAction('Open event file...', self.menu) 

804 self.menu.addAction(mi) 

805 mi.triggered.connect(self.read_events) 

806 

807 self.menu.addSeparator() 

808 

809 menudef = [ 

810 ('Individual Scale', 

811 lambda tr: tr.nslc_id), 

812 ('Common Scale', 

813 lambda tr: None), 

814 ('Common Scale per Station', 

815 lambda tr: (tr.network, tr.station)), 

816 ('Common Scale per Station Location', 

817 lambda tr: (tr.network, tr.station, tr.location)), 

818 ('Common Scale per Component', 

819 lambda tr: (tr.channel)), 

820 ] 

821 

822 self.menuitems_scaling = add_radiobuttongroup( 

823 self.menu, menudef, self, self.scalingmode_change, 

824 default=self.config.trace_scale) 

825 

826 self.scaling_key = self.menuitems_scaling[0][1] 

827 self.scaling_hooks = {} 

828 self.scalingmode_change() 

829 

830 self.menu.addSeparator() 

831 

832 menudef = [ 

833 ('Scaling based on Minimum and Maximum', 'minmax'), 

834 ('Scaling based on Mean +- 2 x Std. Deviation', 2), 

835 ('Scaling based on Mean +- 4 x Std. Deviation', 4), 

836 ] 

837 

838 self.menuitems_scaling_base = add_radiobuttongroup( 

839 self.menu, menudef, self, self.scaling_base_change) 

840 

841 self.scaling_base = self.menuitems_scaling_base[0][1] 

842 

843 self.menu.addSeparator() 

844 

845 def sector_dist(sta): 

846 if sta.dist_m is None: 

847 return None, None 

848 else: 

849 return ( 

850 sector_int(round((sta.azimuth+15.)/30.)), 

851 m_float(sta.dist_m)) 

852 

853 menudef = [ 

854 ('Sort by Names', 

855 lambda tr: ()), 

856 ('Sort by Distance', 

857 lambda tr: self.station_attrib( 

858 tr, 

859 lambda sta: (m_float_or_none(sta.dist_m),), 

860 lambda tr: (None,))), 

861 ('Sort by Azimuth', 

862 lambda tr: self.station_attrib( 

863 tr, 

864 lambda sta: (deg_float_or_none(sta.azimuth),), 

865 lambda tr: (None,))), 

866 ('Sort by Distance in 12 Azimuthal Blocks', 

867 lambda tr: self.station_attrib( 

868 tr, 

869 sector_dist, 

870 lambda tr: (None, None))), 

871 ('Sort by Backazimuth', 

872 lambda tr: self.station_attrib( 

873 tr, 

874 lambda sta: (deg_float_or_none(sta.backazimuth),), 

875 lambda tr: (None,))), 

876 ] 

877 self.menuitems_ssorting = add_radiobuttongroup( 

878 self.menu, menudef, self, self.s_sortingmode_change) 

879 

880 self._ssort = lambda tr: () 

881 

882 self.menuitem_distances_3d = qw.QAction('3D distances', self.menu) 

883 self.menuitem_distances_3d.setCheckable(True) 

884 self.menuitem_distances_3d.setChecked(False) 

885 self.menuitem_distances_3d.toggled.connect( 

886 self.distances_3d_changed) 

887 

888 self.menu.addAction(self.menuitem_distances_3d) 

889 

890 self.menu.addSeparator() 

891 

892 menudef = [ 

893 ('Subsort by Network, Station, Location, Channel', 

894 (lambda tr: self.ssort(tr) + tr.nslc_id, # gathering 

895 lambda a: a, # sorting 

896 lambda tr: tr.location)), # coloring 

897 ('Subsort by Network, Station, Channel, Location', 

898 (lambda tr: self.ssort(tr) + ( 

899 tr.network, tr.station, tr.channel, tr.location), 

900 lambda a: a, 

901 lambda tr: tr.channel)), 

902 ('Subsort by Station, Network, Channel, Location', 

903 (lambda tr: self.ssort(tr) + ( 

904 tr.station, tr.network, tr.channel, tr.location), 

905 lambda a: a, 

906 lambda tr: tr.channel)), 

907 ('Subsort by Location, Network, Station, Channel', 

908 (lambda tr: self.ssort(tr) + ( 

909 tr.location, tr.network, tr.station, tr.channel), 

910 lambda a: a, 

911 lambda tr: tr.channel)), 

912 ('Subsort by Channel, Network, Station, Location', 

913 (lambda tr: self.ssort(tr) + ( 

914 tr.channel, tr.network, tr.station, tr.location), 

915 lambda a: a, 

916 lambda tr: (tr.network, tr.station, tr.location))), 

917 ('Subsort by Network, Station, Channel (Grouped by Location)', 

918 (lambda tr: self.ssort(tr) + ( 

919 tr.network, tr.station, tr.channel), 

920 lambda a: a, 

921 lambda tr: tr.location)), 

922 ('Subsort by Station, Network, Channel (Grouped by Location)', 

923 (lambda tr: self.ssort(tr) + ( 

924 tr.station, tr.network, tr.channel), 

925 lambda a: a, 

926 lambda tr: tr.location)), 

927 ] 

928 

929 self.menuitems_sorting = add_radiobuttongroup( 

930 self.menu, menudef, self, self.sortingmode_change) 

931 

932 self.menu.addSeparator() 

933 

934 self.menuitem_antialias = qw.QAction('Antialiasing', self.menu) 

935 self.menuitem_antialias.setCheckable(True) 

936 self.menu.addAction(self.menuitem_antialias) 

937 

938 self.menuitem_liberal_fetch = qw.QAction( 

939 'Liberal Fetch Optimization', self.menu) 

940 self.menuitem_liberal_fetch.setCheckable(True) 

941 self.menu.addAction(self.menuitem_liberal_fetch) 

942 

943 self.menuitem_cliptraces = qw.QAction('Clip Traces', self.menu) 

944 self.menuitem_cliptraces.setCheckable(True) 

945 self.menuitem_cliptraces.setChecked(self.config.clip_traces) 

946 self.menu.addAction(self.menuitem_cliptraces) 

947 

948 self.menuitem_showboxes = qw.QAction('Show Boxes', self.menu) 

949 self.menuitem_showboxes.setCheckable(True) 

950 self.menuitem_showboxes.setChecked( 

951 self.config.show_boxes) 

952 self.menu.addAction(self.menuitem_showboxes) 

953 

954 self.menuitem_colortraces = qw.QAction('Color Traces', self.menu) 

955 self.menuitem_colortraces.setCheckable(True) 

956 self.menuitem_colortraces.setChecked(False) 

957 self.menu.addAction(self.menuitem_colortraces) 

958 

959 self.menuitem_showscalerange = qw.QAction( 

960 'Show Scale Ranges', self.menu) 

961 self.menuitem_showscalerange.setCheckable(True) 

962 self.menuitem_showscalerange.setChecked( 

963 self.config.show_scale_ranges) 

964 self.menu.addAction(self.menuitem_showscalerange) 

965 

966 self.menuitem_showscaleaxis = qw.QAction( 

967 'Show Scale Axes', self.menu) 

968 self.menuitem_showscaleaxis.setCheckable(True) 

969 self.menuitem_showscaleaxis.setChecked( 

970 self.config.show_scale_axes) 

971 self.menu.addAction(self.menuitem_showscaleaxis) 

972 

973 self.menuitem_showzeroline = qw.QAction( 

974 'Show Zero Lines', self.menu) 

975 self.menuitem_showzeroline.setCheckable(True) 

976 self.menu.addAction(self.menuitem_showzeroline) 

977 

978 self.menuitem_fixscalerange = qw.QAction( 

979 'Fix Scale Ranges', self.menu) 

980 self.menuitem_fixscalerange.setCheckable(True) 

981 self.menu.addAction(self.menuitem_fixscalerange) 

982 

983 self.menuitem_allowdownsampling = qw.QAction( 

984 'Allow Downsampling', self.menu) 

985 self.menuitem_allowdownsampling.setCheckable(True) 

986 self.menuitem_allowdownsampling.setChecked(True) 

987 self.menu.addAction(self.menuitem_allowdownsampling) 

988 

989 self.menuitem_degap = qw.QAction('Allow Degapping', self.menu) 

990 self.menuitem_degap.setCheckable(True) 

991 self.menuitem_degap.setChecked(True) 

992 self.menu.addAction(self.menuitem_degap) 

993 

994 self.menuitem_demean = qw.QAction('Demean', self.menu) 

995 self.menuitem_demean.setCheckable(True) 

996 self.menuitem_demean.setChecked(self.config.demean) 

997 self.menu.addAction(self.menuitem_demean) 

998 

999 self.menuitem_fft_filtering = qw.QAction( 

1000 'FFT Filtering', self.menu) 

1001 self.menuitem_fft_filtering.setCheckable(True) 

1002 self.menuitem_fft_filtering.setChecked(False) 

1003 self.menu.addAction(self.menuitem_fft_filtering) 

1004 

1005 self.menuitem_lphp = qw.QAction( 

1006 'Bandpass is Lowpass + Highpass', self.menu) 

1007 self.menuitem_lphp.setCheckable(True) 

1008 self.menuitem_lphp.setChecked(True) 

1009 self.menu.addAction(self.menuitem_lphp) 

1010 

1011 self.menuitem_watch = qw.QAction('Watch Files', self.menu) 

1012 self.menuitem_watch.setCheckable(True) 

1013 self.menuitem_watch.setChecked(False) 

1014 self.menu.addAction(self.menuitem_watch) 

1015 

1016 self.visible_length_menu = qw.QMenu('Visible Length', self.menu) 

1017 

1018 menudef = [(x.key, x.value) for x in 

1019 self.config.visible_length_setting] 

1020 

1021 self.menuitems_visible_length = add_radiobuttongroup( 

1022 self.visible_length_menu, menudef, self, 

1023 self.visible_length_change) 

1024 

1025 self.visible_length = menudef[0][1] 

1026 self.menu.addMenu(self.visible_length_menu) 

1027 self.menu.addSeparator() 

1028 

1029 self.snufflings_menu = qw.QMenu('Run Snuffling', self.menu) 

1030 self.menu.addMenu(self.snufflings_menu) 

1031 

1032 self.toggle_panel_menu = qw.QMenu('Panels', self.menu) 

1033 self.menu.addMenu(self.toggle_panel_menu) 

1034 

1035 self.menuitem_reload = qw.QAction('Reload Snufflings', self.menu) 

1036 self.menu.addAction(self.menuitem_reload) 

1037 self.menuitem_reload.triggered.connect( 

1038 self.setup_snufflings) 

1039 

1040 self.menu.addSeparator() 

1041 

1042 # Disable ShadowPileTest 

1043 if False: 

1044 self.menuitem_test = qw.QAction('Test', self.menu) 

1045 self.menuitem_test.setCheckable(True) 

1046 self.menuitem_test.setChecked(False) 

1047 self.menu.addAction(self.menuitem_test) 

1048 self.menuitem_test.triggered.connect( 

1049 self.toggletest) 

1050 

1051 self.menuitem_print = qw.QAction('Print', self.menu) 

1052 self.menu.addAction(self.menuitem_print) 

1053 self.menuitem_print.triggered.connect( 

1054 self.printit) 

1055 

1056 self.menuitem_svg = qw.QAction('Save as SVG|PNG', self.menu) 

1057 self.menu.addAction(self.menuitem_svg) 

1058 self.menuitem_svg.triggered.connect( 

1059 self.savesvg) 

1060 

1061 self.snuffling_help_menu = qw.QMenu('Help', self.menu) 

1062 self.menu.addMenu(self.snuffling_help_menu) 

1063 self.menuitem_help = qw.QAction( 

1064 'Snuffler Controls', self.snuffling_help_menu) 

1065 self.snuffling_help_menu.addAction(self.menuitem_help) 

1066 self.menuitem_help.triggered.connect(self.help) 

1067 

1068 self.snuffling_help_menu.addSeparator() 

1069 

1070 self.menuitem_about = qw.QAction('About', self.menu) 

1071 self.menu.addAction(self.menuitem_about) 

1072 self.menuitem_about.triggered.connect(self.about) 

1073 

1074 self.menuitem_close = qw.QAction('Close', self.menu) 

1075 self.menu.addAction(self.menuitem_close) 

1076 self.menuitem_close.triggered.connect(self.myclose) 

1077 

1078 self.menu.addSeparator() 

1079 

1080 self.menu.triggered.connect(self.update) 

1081 

1082 self.time_projection = Projection() 

1083 self.set_time_range(self.pile.get_tmin(), self.pile.get_tmax()) 

1084 self.time_projection.set_out_range(0., self.width()) 

1085 

1086 self.gather = None 

1087 

1088 self.trace_filter = None 

1089 self.quick_filter = None 

1090 self.quick_filter_patterns = None, None 

1091 self.blacklist = [] 

1092 

1093 self.track_to_screen = Projection() 

1094 self.track_to_nslc_ids = {} 

1095 

1096 self.old_vec = None 

1097 self.old_processed_traces = None 

1098 

1099 self.timer = qc.QTimer(self) 

1100 self.timer.timeout.connect(self.periodical) 

1101 self.timer.setInterval(1000) 

1102 self.timer.start() 

1103 self.pile.add_listener(self) 

1104 self.trace_styles = {} 

1105 self.determine_box_styles() 

1106 self.setMouseTracking(True) 

1107 

1108 user_home_dir = os.path.expanduser('~') 

1109 self.snuffling_modules = {} 

1110 self.snuffling_paths = [os.path.join(user_home_dir, '.snufflings')] 

1111 self.default_snufflings = None 

1112 self.snufflings = [] 

1113 

1114 self.stations = {} 

1115 

1116 self.timer_draw = Timer() 

1117 self.timer_cutout = Timer() 

1118 self.time_spent_painting = 0.0 

1119 self.time_last_painted = time.time() 

1120 

1121 self.interactive_range_change_time = 0.0 

1122 self.interactive_range_change_delay_time = 10.0 

1123 self.follow_timer = None 

1124 

1125 self.sortingmode_change_time = 0.0 

1126 self.sortingmode_change_delay_time = None 

1127 

1128 self.old_data_ranges = {} 

1129 

1130 self.error_messages = {} 

1131 self.return_tag = None 

1132 self.wheel_pos = 60 

1133 

1134 self.setAcceptDrops(True) 

1135 self._paths_to_load = [] 

1136 

1137 self.tf_cache = {} 

1138 

1139 self.automatic_updates = True 

1140 

1141 self.closing = False 

1142 self.paint_timer = qc.QTimer(self) 

1143 self.paint_timer.timeout.connect(self.reset_updates) 

1144 self.paint_timer.setInterval(20) 

1145 self.paint_timer.start() 

1146 

1147 @qc.pyqtSlot() 

1148 def reset_updates(self): 

1149 if not self.updatesEnabled(): 

1150 self.setUpdatesEnabled(True) 

1151 

1152 def fail(self, reason): 

1153 box = qw.QMessageBox(self) 

1154 box.setText(reason) 

1155 box.exec_() 

1156 

1157 def set_trace_filter(self, filter_func): 

1158 self.trace_filter = filter_func 

1159 self.sortingmode_change() 

1160 

1161 def update_trace_filter(self): 

1162 if self.blacklist: 

1163 

1164 def blacklist_func(tr): 

1165 return not pyrocko.util.match_nslc( 

1166 self.blacklist, tr.nslc_id) 

1167 

1168 else: 

1169 blacklist_func = None 

1170 

1171 if self.quick_filter is None and blacklist_func is None: 

1172 self.set_trace_filter(None) 

1173 elif self.quick_filter is None: 

1174 self.set_trace_filter(blacklist_func) 

1175 elif blacklist_func is None: 

1176 self.set_trace_filter(self.quick_filter) 

1177 else: 

1178 self.set_trace_filter( 

1179 lambda tr: blacklist_func(tr) and self.quick_filter(tr)) 

1180 

1181 def set_quick_filter(self, filter_func): 

1182 self.quick_filter = filter_func 

1183 self.update_trace_filter() 

1184 

1185 def set_quick_filter_patterns(self, patterns, inputline=None): 

1186 if patterns is not None: 

1187 self.set_quick_filter( 

1188 lambda tr: pyrocko.util.match_nslc(patterns, tr.nslc_id)) 

1189 else: 

1190 self.set_quick_filter(None) 

1191 

1192 self.quick_filter_patterns = patterns, inputline 

1193 

1194 def get_quick_filter_patterns(self): 

1195 return self.quick_filter_patterns 

1196 

1197 def add_blacklist_pattern(self, pattern): 

1198 if pattern == 'empty': 

1199 keys = set(self.pile.nslc_ids) 

1200 trs = self.pile.all( 

1201 tmin=self.tmin, 

1202 tmax=self.tmax, 

1203 load_data=False, 

1204 degap=False) 

1205 

1206 for tr in trs: 

1207 if tr.nslc_id in keys: 

1208 keys.remove(tr.nslc_id) 

1209 

1210 for key in keys: 

1211 xpattern = '.'.join(key) 

1212 if xpattern not in self.blacklist: 

1213 self.blacklist.append(xpattern) 

1214 

1215 else: 

1216 if pattern in self.blacklist: 

1217 self.blacklist.remove(pattern) 

1218 

1219 self.blacklist.append(pattern) 

1220 

1221 logger.info('Blacklist is [ %s ]' % ', '.join(self.blacklist)) 

1222 self.update_trace_filter() 

1223 

1224 def remove_blacklist_pattern(self, pattern): 

1225 if pattern in self.blacklist: 

1226 self.blacklist.remove(pattern) 

1227 else: 

1228 raise PileViewerMainException( 

1229 'Pattern not found in blacklist.') 

1230 

1231 logger.info('Blacklist is [ %s ]' % ', '.join(self.blacklist)) 

1232 self.update_trace_filter() 

1233 

1234 def clear_blacklist(self): 

1235 self.blacklist = [] 

1236 self.update_trace_filter() 

1237 

1238 def ssort(self, tr): 

1239 return self._ssort(tr) 

1240 

1241 def station_key(self, x): 

1242 return x.network, x.station 

1243 

1244 def station_keys(self, x): 

1245 return [ 

1246 (x.network, x.station, x.location), 

1247 (x.network, x.station)] 

1248 

1249 def station_attrib(self, tr, getter, default_getter): 

1250 for sk in self.station_keys(tr): 

1251 if sk in self.stations: 

1252 station = self.stations[sk] 

1253 return getter(station) 

1254 

1255 return default_getter(tr) 

1256 

1257 def get_station(self, sk): 

1258 return self.stations[sk] 

1259 

1260 def has_station(self, station): 

1261 for sk in self.station_keys(station): 

1262 if sk in self.stations: 

1263 return True 

1264 

1265 return False 

1266 

1267 def station_latlon(self, tr, default_getter=lambda tr: (0., 0.)): 

1268 return self.station_attrib( 

1269 tr, lambda sta: (sta.lat, sta.lon), default_getter) 

1270 

1271 def set_stations(self, stations): 

1272 self.stations = {} 

1273 self.add_stations(stations) 

1274 

1275 def add_stations(self, stations): 

1276 for station in stations: 

1277 for sk in self.station_keys(station): 

1278 self.stations[sk] = station 

1279 

1280 ev = self.get_active_event() 

1281 if ev: 

1282 self.set_origin(ev) 

1283 

1284 def add_event(self, event): 

1285 marker = EventMarker(event) 

1286 self.add_marker(marker) 

1287 

1288 def add_events(self, events): 

1289 markers = [EventMarker(e) for e in events] 

1290 self.add_markers(markers) 

1291 

1292 def set_event_marker_as_origin(self, ignore=None): 

1293 selected = self.selected_markers() 

1294 if not selected: 

1295 self.fail('An event marker must be selected.') 

1296 return 

1297 

1298 m = selected[0] 

1299 if not isinstance(m, EventMarker): 

1300 self.fail('Selected marker is not an event.') 

1301 return 

1302 

1303 self.set_active_event_marker(m) 

1304 

1305 def deactivate_event_marker(self): 

1306 if self.active_event_marker: 

1307 self.active_event_marker.active = False 

1308 

1309 self.active_event_marker_changed.emit() 

1310 self.active_event_marker = None 

1311 

1312 def set_active_event_marker(self, event_marker): 

1313 if self.active_event_marker: 

1314 self.active_event_marker.active = False 

1315 

1316 self.active_event_marker = event_marker 

1317 event_marker.active = True 

1318 event = event_marker.get_event() 

1319 self.set_origin(event) 

1320 self.active_event_marker_changed.emit() 

1321 

1322 def set_active_event(self, event): 

1323 for marker in self.markers: 

1324 if isinstance(marker, EventMarker): 

1325 if marker.get_event() is event: 

1326 self.set_active_event_marker(marker) 

1327 

1328 def get_active_event_marker(self): 

1329 return self.active_event_marker 

1330 

1331 def get_active_event(self): 

1332 m = self.get_active_event_marker() 

1333 if m is not None: 

1334 return m.get_event() 

1335 else: 

1336 return None 

1337 

1338 def get_active_markers(self): 

1339 emarker = self.get_active_event_marker() 

1340 if emarker is None: 

1341 return None, [] 

1342 

1343 else: 

1344 ev = emarker.get_event() 

1345 pmarkers = [ 

1346 m for m in self.markers 

1347 if isinstance(m, PhaseMarker) and m.get_event() is ev] 

1348 

1349 return emarker, pmarkers 

1350 

1351 def set_origin(self, location): 

1352 for station in self.stations.values(): 

1353 station.set_event_relative_data( 

1354 location, 

1355 distance_3d=self.menuitem_distances_3d.isChecked()) 

1356 

1357 self.sortingmode_change() 

1358 

1359 def distances_3d_changed(self, ignore): 

1360 self.set_event_marker_as_origin(ignore) 

1361 

1362 def toggletest(self, checked): 

1363 if checked: 

1364 sp = Integrator() 

1365 

1366 self.add_shadow_pile(sp) 

1367 else: 

1368 self.remove_shadow_piles() 

1369 

1370 def add_shadow_pile(self, shadow_pile): 

1371 shadow_pile.set_basepile(self.pile) 

1372 shadow_pile.add_listener(self) 

1373 self.pile = shadow_pile 

1374 

1375 def remove_shadow_piles(self): 

1376 self.pile = self.pile.get_basepile() 

1377 

1378 def iter_snuffling_modules(self): 

1379 pjoin = os.path.join 

1380 for path in self.snuffling_paths: 

1381 

1382 if not os.path.isdir(path): 

1383 os.mkdir(path) 

1384 

1385 for entry in os.listdir(path): 

1386 directory = path 

1387 fn = entry 

1388 d = pjoin(path, entry) 

1389 if os.path.isdir(d): 

1390 directory = d 

1391 if os.path.isfile( 

1392 os.path.join(directory, 'snuffling.py')): 

1393 fn = 'snuffling.py' 

1394 

1395 if not fn.endswith('.py'): 

1396 continue 

1397 

1398 name = fn[:-3] 

1399 

1400 if (directory, name) not in self.snuffling_modules: 

1401 self.snuffling_modules[directory, name] = \ 

1402 pyrocko.gui.snuffling.SnufflingModule( 

1403 directory, name, self) 

1404 

1405 yield self.snuffling_modules[directory, name] 

1406 

1407 def setup_snufflings(self): 

1408 # user snufflings 

1409 for mod in self.iter_snuffling_modules(): 

1410 try: 

1411 mod.load_if_needed() 

1412 except pyrocko.gui.snuffling.BrokenSnufflingModule as e: 

1413 logger.warning('Snuffling module "%s" is broken' % e) 

1414 

1415 # load the default snufflings on first run 

1416 if self.default_snufflings is None: 

1417 self.default_snufflings = pyrocko.gui\ 

1418 .snufflings.__snufflings__() 

1419 for snuffling in self.default_snufflings: 

1420 self.add_snuffling(snuffling) 

1421 

1422 def set_panel_parent(self, panel_parent): 

1423 self.panel_parent = panel_parent 

1424 

1425 def get_panel_parent(self): 

1426 return self.panel_parent 

1427 

1428 def add_snuffling(self, snuffling, reloaded=False): 

1429 logger.debug('Adding snuffling %s' % snuffling.get_name()) 

1430 snuffling.init_gui( 

1431 self, self.get_panel_parent(), self, reloaded=reloaded) 

1432 self.snufflings.append(snuffling) 

1433 self.update() 

1434 

1435 def remove_snuffling(self, snuffling): 

1436 snuffling.delete_gui() 

1437 self.update() 

1438 self.snufflings.remove(snuffling) 

1439 snuffling.pre_destroy() 

1440 

1441 def add_snuffling_menuitem(self, item): 

1442 self.snufflings_menu.addAction(item) 

1443 item.setParent(self.snufflings_menu) 

1444 sort_actions(self.snufflings_menu) 

1445 

1446 def remove_snuffling_menuitem(self, item): 

1447 self.snufflings_menu.removeAction(item) 

1448 

1449 def add_snuffling_help_menuitem(self, item): 

1450 self.snuffling_help_menu.addAction(item) 

1451 item.setParent(self.snuffling_help_menu) 

1452 sort_actions(self.snuffling_help_menu) 

1453 

1454 def remove_snuffling_help_menuitem(self, item): 

1455 self.snuffling_help_menu.removeAction(item) 

1456 

1457 def add_panel_toggler(self, item): 

1458 self.toggle_panel_menu.addAction(item) 

1459 item.setParent(self.toggle_panel_menu) 

1460 sort_actions(self.toggle_panel_menu) 

1461 

1462 def remove_panel_toggler(self, item): 

1463 self.toggle_panel_menu.removeAction(item) 

1464 

1465 def load(self, paths, regex=None, format='from_extension', 

1466 cache_dir=None, force_cache=False): 

1467 

1468 if cache_dir is None: 

1469 cache_dir = pyrocko.config.config().cache_dir 

1470 if isinstance(paths, str): 

1471 paths = [paths] 

1472 

1473 fns = pyrocko.util.select_files( 

1474 paths, selector=None, regex=regex, show_progress=False) 

1475 

1476 if not fns: 

1477 return 

1478 

1479 cache = pyrocko.pile.get_cache(cache_dir) 

1480 

1481 t = [time.time()] 

1482 

1483 def update_bar(label, value): 

1484 pbs = self.parent().get_progressbars() 

1485 if label.lower() == 'looking at files': 

1486 label = 'Looking at %i files' % len(fns) 

1487 else: 

1488 label = 'Scanning %i files' % len(fns) 

1489 

1490 return pbs.set_status(label, value) 

1491 

1492 def update_progress(label, i, n): 

1493 abort = False 

1494 

1495 qw.qApp.processEvents() 

1496 if n != 0: 

1497 perc = i*100/n 

1498 else: 

1499 perc = 100 

1500 abort |= update_bar(label, perc) 

1501 abort |= self.window().is_closing() 

1502 

1503 tnow = time.time() 

1504 if t[0] + 1. + self.time_spent_painting * 10. < tnow: 

1505 self.update() 

1506 t[0] = tnow 

1507 

1508 return abort 

1509 

1510 self.automatic_updates = False 

1511 

1512 self.pile.load_files( 

1513 sorted(fns), 

1514 filename_attributes=regex, 

1515 cache=cache, 

1516 fileformat=format, 

1517 show_progress=False, 

1518 update_progress=update_progress) 

1519 

1520 self.automatic_updates = True 

1521 self.update() 

1522 

1523 def load_queued(self): 

1524 if not self._paths_to_load: 

1525 return 

1526 paths = self._paths_to_load 

1527 self._paths_to_load = [] 

1528 self.load(paths) 

1529 

1530 def load_soon(self, paths): 

1531 self._paths_to_load.extend(paths) 

1532 qc.QTimer.singleShot(200, self.load_queued) 

1533 

1534 def open_waveforms(self): 

1535 caption = 'Select one or more files to open' 

1536 

1537 fns, _ = fnpatch(qw.QFileDialog.getOpenFileNames( 

1538 self, caption, options=qfiledialog_options)) 

1539 

1540 if fns: 

1541 self.load(list(str(fn) for fn in fns)) 

1542 

1543 def open_waveform_directory(self): 

1544 caption = 'Select directory to scan for waveform files' 

1545 

1546 dn = qw.QFileDialog.getExistingDirectory( 

1547 self, caption, options=qfiledialog_options) 

1548 

1549 if dn: 

1550 self.load([str(dn)]) 

1551 

1552 def open_stations(self, fns=None): 

1553 caption = 'Select one or more files to open' 

1554 

1555 if not fns: 

1556 fns, _ = fnpatch(qw.QFileDialog.getOpenFileNames( 

1557 self, caption, options=qfiledialog_options)) 

1558 

1559 try: 

1560 stations = [pyrocko.model.load_stations(str(x)) for x in fns] 

1561 for stat in stations: 

1562 self.add_stations(stat) 

1563 

1564 except Exception as e: 

1565 self.fail('Failed to read station file: %s' % str(e)) 

1566 

1567 def open_stations_xml(self, fns=None): 

1568 from pyrocko.io import stationxml 

1569 

1570 caption = 'Select one or more StationXML files to open' 

1571 

1572 if not fns: 

1573 fns, _ = fnpatch(qw.QFileDialog.getOpenFileNames( 

1574 self, caption, options=qfiledialog_options, 

1575 filter='StationXML *.xml (*.xml *.XML);;All files (*)')) 

1576 

1577 try: 

1578 stations = [ 

1579 stationxml.load_xml(filename=str(x)).get_pyrocko_stations() 

1580 for x in fns] 

1581 

1582 for stat in stations: 

1583 self.add_stations(stat) 

1584 

1585 except Exception as e: 

1586 self.fail('Failed to read StationXML file: %s' % str(e)) 

1587 

1588 def add_traces(self, traces): 

1589 if traces: 

1590 mtf = pyrocko.pile.MemTracesFile(None, traces) 

1591 self.pile.add_file(mtf) 

1592 ticket = (self.pile, mtf) 

1593 return ticket 

1594 else: 

1595 return (None, None) 

1596 

1597 def release_data(self, tickets): 

1598 for ticket in tickets: 

1599 pile, mtf = ticket 

1600 if pile is not None: 

1601 pile.remove_file(mtf) 

1602 

1603 def periodical(self): 

1604 if self.menuitem_watch.isChecked(): 

1605 if self.pile.reload_modified(): 

1606 self.update() 

1607 

1608 def get_pile(self): 

1609 return self.pile 

1610 

1611 def pile_changed(self, what): 

1612 self.pile_has_changed = True 

1613 self.pile_has_changed_signal.emit() 

1614 if self.automatic_updates: 

1615 self.update() 

1616 

1617 def set_gathering(self, gather=None, order=None, color=None): 

1618 

1619 if gather is None: 

1620 def gather(tr): 

1621 return tr.nslc_id 

1622 

1623 if order is None: 

1624 def order(a): 

1625 return a 

1626 

1627 if color is None: 

1628 def color(tr): 

1629 return tr.location 

1630 

1631 self.gather = gather 

1632 keys = self.pile.gather_keys(gather, self.trace_filter) 

1633 self.color_gather = color 

1634 self.color_keys = self.pile.gather_keys(color) 

1635 previous_ntracks = self.ntracks 

1636 self.set_ntracks(len(keys)) 

1637 

1638 if self.shown_tracks_range is None or \ 

1639 previous_ntracks == 0 or \ 

1640 self.show_all: 

1641 

1642 low, high = 0, min(self.ntracks_shown_max, self.ntracks) 

1643 key_at_top = None 

1644 n = high-low 

1645 

1646 else: 

1647 low, high = self.shown_tracks_range 

1648 key_at_top = self.track_keys[low] 

1649 n = high-low 

1650 

1651 self.track_keys = sorted(keys, key=order) 

1652 

1653 if key_at_top is not None: 

1654 try: 

1655 ind = self.track_keys.index(key_at_top) 

1656 low = ind 

1657 high = low+n 

1658 except Exception: 

1659 pass 

1660 

1661 self.set_tracks_range((low, high)) 

1662 

1663 self.key_to_row = dict( 

1664 [(key, i) for (i, key) in enumerate(self.track_keys)]) 

1665 

1666 def inrange(x, r): 

1667 return r[0] <= x and x < r[1] 

1668 

1669 def trace_selector(trace): 

1670 gt = self.gather(trace) 

1671 return ( 

1672 gt in self.key_to_row and 

1673 inrange(self.key_to_row[gt], self.shown_tracks_range)) 

1674 

1675 if self.trace_filter is not None: 

1676 self.trace_selector = lambda x: \ 

1677 self.trace_filter(x) and trace_selector(x) 

1678 else: 

1679 self.trace_selector = trace_selector 

1680 

1681 if self.tmin == working_system_time_range[0] and \ 

1682 self.tmax == working_system_time_range[1] or \ 

1683 self.show_all: 

1684 

1685 tmin, tmax = self.pile.get_tmin(), self.pile.get_tmax() 

1686 if tmin is not None and tmax is not None: 

1687 tlen = (tmax - tmin) 

1688 tpad = tlen * 5./self.width() 

1689 self.set_time_range(tmin-tpad, tmax+tpad) 

1690 

1691 def set_time_range(self, tmin, tmax): 

1692 if tmin is None: 

1693 tmin = initial_time_range[0] 

1694 

1695 if tmax is None: 

1696 tmax = initial_time_range[1] 

1697 

1698 if tmin > tmax: 

1699 tmin, tmax = tmax, tmin 

1700 

1701 if tmin == tmax: 

1702 tmin -= 1. 

1703 tmax += 1. 

1704 

1705 tmin = max(working_system_time_range[0], tmin) 

1706 tmax = min(working_system_time_range[1], tmax) 

1707 

1708 min_deltat = self.content_deltat_range()[0] 

1709 if (tmax - tmin < min_deltat): 

1710 m = (tmin + tmax) / 2. 

1711 tmin = m - min_deltat/2. 

1712 tmax = m + min_deltat/2. 

1713 

1714 self.time_projection.set_in_range(tmin, tmax) 

1715 self.tmin, self.tmax = tmin, tmax 

1716 

1717 def get_time_range(self): 

1718 return self.tmin, self.tmax 

1719 

1720 def ypart(self, y): 

1721 if y < self.ax_height: 

1722 return -1 

1723 elif y > self.height()-self.ax_height: 

1724 return 1 

1725 else: 

1726 return 0 

1727 

1728 def time_fractional_digits(self): 

1729 min_deltat = self.content_deltat_range()[0] 

1730 return min(9, max(1, int(-math.floor(math.log10(min_deltat)))+2)) 

1731 

1732 def write_markers(self, fn=None): 

1733 caption = "Choose a file name to write markers" 

1734 if not fn: 

1735 fn, _ = fnpatch(qw.QFileDialog.getSaveFileName( 

1736 self, caption, options=qfiledialog_options)) 

1737 if fn: 

1738 try: 

1739 Marker.save_markers( 

1740 self.markers, fn, 

1741 fdigits=self.time_fractional_digits()) 

1742 

1743 except Exception as e: 

1744 self.fail('Failed to write marker file: %s' % str(e)) 

1745 

1746 def write_selected_markers(self, fn=None): 

1747 caption = "Choose a file name to write selected markers" 

1748 if not fn: 

1749 fn, _ = fnpatch(qw.QFileDialog.getSaveFileName( 

1750 self, caption, options=qfiledialog_options)) 

1751 if fn: 

1752 try: 

1753 Marker.save_markers( 

1754 self.iter_selected_markers(), 

1755 fn, 

1756 fdigits=self.time_fractional_digits()) 

1757 

1758 except Exception as e: 

1759 self.fail('Failed to write marker file: %s' % str(e)) 

1760 

1761 def read_events(self, fn=None): 

1762 ''' 

1763 Open QFileDialog to open, read and add 

1764 :py:class:`pyrocko.model.Event` instances and their marker 

1765 representation to the pile viewer. 

1766 ''' 

1767 caption = "Selet one or more files to open" 

1768 if not fn: 

1769 fn, _ = fnpatch(qw.QFileDialog.getOpenFileName( 

1770 self, caption, options=qfiledialog_options)) 

1771 if fn: 

1772 try: 

1773 self.add_events(pyrocko.model.load_events(fn)) 

1774 self.associate_phases_to_events() 

1775 

1776 except Exception as e: 

1777 self.fail('Failed to read event file: %s' % str(e)) 

1778 

1779 def read_markers(self, fn=None): 

1780 ''' 

1781 Open QFileDialog to open, read and add markers to the pile viewer. 

1782 ''' 

1783 caption = "Selet one or more files to open" 

1784 if not fn: 

1785 fn, _ = fnpatch(qw.QFileDialog.getOpenFileName( 

1786 self, caption, options=qfiledialog_options)) 

1787 if fn: 

1788 try: 

1789 self.add_markers(Marker.load_markers(fn)) 

1790 self.associate_phases_to_events() 

1791 

1792 except Exception as e: 

1793 self.fail('Failed to read marker file: %s' % str(e)) 

1794 

1795 def associate_phases_to_events(self): 

1796 associate_phases_to_events(self.markers) 

1797 

1798 def add_marker(self, marker): 

1799 # need index to inform QAbstactTableModel about upcoming change, 

1800 # but have to restore current state in order to not cause problems 

1801 self.markers.insert(marker) 

1802 i = self.markers.remove(marker) 

1803 

1804 self.begin_markers_add.emit(i, i) 

1805 self.markers.insert(marker) 

1806 self.end_markers_add.emit() 

1807 self.markers_deltat_max = max( 

1808 self.markers_deltat_max, marker.tmax - marker.tmin) 

1809 

1810 def add_markers(self, markers): 

1811 if not self.markers: 

1812 self.begin_markers_add.emit(0, len(markers) - 1) 

1813 self.markers.insert_many(markers) 

1814 self.end_markers_add.emit() 

1815 self.update_markers_deltat_max() 

1816 else: 

1817 for marker in markers: 

1818 self.add_marker(marker) 

1819 

1820 def update_markers_deltat_max(self): 

1821 if self.markers: 

1822 self.markers_deltat_max = max( 

1823 marker.tmax - marker.tmin for marker in self.markers) 

1824 

1825 def remove_marker(self, marker): 

1826 ''' 

1827 Remove a ``marker`` from the :py:class:`PileViewer`. 

1828 

1829 :param marker: :py:class:`Marker` (or subclass) instance 

1830 ''' 

1831 

1832 if marker is self.active_event_marker: 

1833 self.deactivate_event_marker() 

1834 

1835 try: 

1836 i = self.markers.index(marker) 

1837 self.begin_markers_remove.emit(i, i) 

1838 self.markers.remove_at(i) 

1839 self.end_markers_remove.emit() 

1840 except ValueError: 

1841 pass 

1842 

1843 def remove_markers(self, markers): 

1844 ''' 

1845 Remove a list of ``markers`` from the :py:class:`PileViewer`. 

1846 

1847 :param markers: list of :py:class:`Marker` (or subclass) 

1848 instances 

1849 ''' 

1850 

1851 if markers is self.markers: 

1852 markers = list(markers) 

1853 

1854 for marker in markers: 

1855 self.remove_marker(marker) 

1856 

1857 self.update_markers_deltat_max() 

1858 

1859 def remove_selected_markers(self): 

1860 def delete_segment(istart, iend): 

1861 self.begin_markers_remove.emit(istart, iend-1) 

1862 for _ in range(iend - istart): 

1863 self.markers.remove_at(istart) 

1864 

1865 self.end_markers_remove.emit() 

1866 

1867 istart = None 

1868 ipos = 0 

1869 markers = self.markers 

1870 nmarkers = len(self.markers) 

1871 while ipos < nmarkers: 

1872 marker = markers[ipos] 

1873 if marker.is_selected(): 

1874 if marker is self.active_event_marker: 

1875 self.deactivate_event_marker() 

1876 

1877 if istart is None: 

1878 istart = ipos 

1879 else: 

1880 if istart is not None: 

1881 delete_segment(istart, ipos) 

1882 nmarkers -= ipos - istart 

1883 ipos = istart - 1 

1884 istart = None 

1885 

1886 ipos += 1 

1887 

1888 if istart is not None: 

1889 delete_segment(istart, ipos) 

1890 

1891 self.update_markers_deltat_max() 

1892 

1893 def selected_markers(self): 

1894 return [marker for marker in self.markers if marker.is_selected()] 

1895 

1896 def iter_selected_markers(self): 

1897 for marker in self.markers: 

1898 if marker.is_selected(): 

1899 yield marker 

1900 

1901 def get_markers(self): 

1902 return self.markers 

1903 

1904 def mousePressEvent(self, mouse_ev): 

1905 self.show_all = False 

1906 point = self.mapFromGlobal(mouse_ev.globalPos()) 

1907 

1908 if mouse_ev.button() == qc.Qt.LeftButton: 

1909 marker = self.marker_under_cursor(point.x(), point.y()) 

1910 if self.picking: 

1911 if self.picking_down is None: 

1912 self.picking_down = ( 

1913 self.time_projection.rev(mouse_ev.x()), 

1914 mouse_ev.y()) 

1915 

1916 elif marker is not None: 

1917 if not (mouse_ev.modifiers() & qc.Qt.ShiftModifier): 

1918 self.deselect_all() 

1919 marker.selected = True 

1920 self.emit_selected_markers() 

1921 self.update() 

1922 else: 

1923 self.track_start = mouse_ev.x(), mouse_ev.y() 

1924 self.track_trange = self.tmin, self.tmax 

1925 

1926 if mouse_ev.button() == qc.Qt.RightButton: 

1927 self.menu.exec_(qg.QCursor.pos()) 

1928 self.update_status() 

1929 

1930 def mouseReleaseEvent(self, mouse_ev): 

1931 if self.ignore_releases: 

1932 self.ignore_releases -= 1 

1933 return 

1934 

1935 if self.picking: 

1936 self.stop_picking(mouse_ev.x(), mouse_ev.y()) 

1937 self.emit_selected_markers() 

1938 

1939 if self.track_start: 

1940 self.update() 

1941 

1942 self.track_start = None 

1943 self.track_trange = None 

1944 self.update_status() 

1945 

1946 def mouseDoubleClickEvent(self, mouse_ev): 

1947 self.show_all = False 

1948 self.start_picking(None) 

1949 self.ignore_releases = 1 

1950 

1951 def mouseMoveEvent(self, mouse_ev): 

1952 self.setUpdatesEnabled(False) 

1953 point = self.mapFromGlobal(mouse_ev.globalPos()) 

1954 

1955 if self.picking: 

1956 self.update_picking(point.x(), point.y()) 

1957 

1958 elif self.track_start is not None: 

1959 x0, y0 = self.track_start 

1960 dx = (point.x() - x0)/float(self.width()) 

1961 dy = (point.y() - y0)/float(self.height()) 

1962 if self.ypart(y0) == 1: 

1963 dy = 0 

1964 

1965 tmin0, tmax0 = self.track_trange 

1966 

1967 scale = math.exp(-dy*5.) 

1968 dtr = scale*(tmax0-tmin0) - (tmax0-tmin0) 

1969 frac = x0/float(self.width()) 

1970 dt = dx*(tmax0-tmin0)*scale 

1971 

1972 self.interrupt_following() 

1973 self.set_time_range( 

1974 tmin0 - dt - dtr*frac, 

1975 tmax0 - dt + dtr*(1.-frac)) 

1976 

1977 self.update() 

1978 else: 

1979 self.hoovering(point.x(), point.y()) 

1980 

1981 self.update_status() 

1982 

1983 def nslc_ids_under_cursor(self, x, y): 

1984 ftrack = self.track_to_screen.rev(y) 

1985 nslc_ids = self.get_nslc_ids_for_track(ftrack) 

1986 return nslc_ids 

1987 

1988 def marker_under_cursor(self, x, y): 

1989 mouset = self.time_projection.rev(x) 

1990 deltat = (self.tmax-self.tmin)*self.click_tolerance/self.width() 

1991 relevant_nslc_ids = None 

1992 for marker in self.markers: 

1993 if marker.kind not in self.visible_marker_kinds: 

1994 continue 

1995 

1996 if (abs(mouset-marker.tmin) < deltat or 

1997 abs(mouset-marker.tmax) < deltat): 

1998 

1999 if relevant_nslc_ids is None: 

2000 relevant_nslc_ids = self.nslc_ids_under_cursor(x, y) 

2001 

2002 marker_nslc_ids = marker.get_nslc_ids() 

2003 if not marker_nslc_ids: 

2004 return marker 

2005 

2006 for nslc_id in marker_nslc_ids: 

2007 if nslc_id in relevant_nslc_ids: 

2008 return marker 

2009 

2010 def hoovering(self, x, y): 

2011 mouset = self.time_projection.rev(x) 

2012 deltat = (self.tmax-self.tmin)*self.click_tolerance/self.width() 

2013 needupdate = False 

2014 haveone = False 

2015 relevant_nslc_ids = self.nslc_ids_under_cursor(x, y) 

2016 for marker in self.markers: 

2017 if marker.kind not in self.visible_marker_kinds: 

2018 continue 

2019 

2020 state = abs(mouset-marker.tmin) < deltat or \ 

2021 abs(mouset-marker.tmax) < deltat and not haveone 

2022 

2023 if state: 

2024 xstate = False 

2025 

2026 marker_nslc_ids = marker.get_nslc_ids() 

2027 if not marker_nslc_ids: 

2028 xstate = True 

2029 

2030 for nslc in relevant_nslc_ids: 

2031 if marker.match_nslc(nslc): 

2032 xstate = True 

2033 

2034 state = xstate 

2035 

2036 if state: 

2037 haveone = True 

2038 oldstate = marker.is_alerted() 

2039 if oldstate != state: 

2040 needupdate = True 

2041 marker.set_alerted(state) 

2042 if state: 

2043 self.message = marker.hoover_message() 

2044 

2045 if not haveone: 

2046 self.message = None 

2047 

2048 if needupdate: 

2049 self.update() 

2050 

2051 def event(self, event): 

2052 if event.type() == qc.QEvent.KeyPress: 

2053 self.keyPressEvent(event) 

2054 return True 

2055 else: 

2056 return base.event(self, event) 

2057 

2058 def keyPressEvent(self, key_event): 

2059 self.show_all = False 

2060 dt = self.tmax - self.tmin 

2061 tmid = (self.tmin + self.tmax) / 2. 

2062 

2063 try: 

2064 keytext = str(key_event.text()) 

2065 except UnicodeEncodeError: 

2066 return 

2067 

2068 if keytext == '?': 

2069 self.help() 

2070 

2071 elif keytext == ' ': 

2072 self.interrupt_following() 

2073 self.set_time_range(self.tmin+dt, self.tmax+dt) 

2074 

2075 elif key_event.key() == qc.Qt.Key_Up: 

2076 for m in self.selected_markers(): 

2077 if isinstance(m, PhaseMarker): 

2078 if key_event.modifiers() & qc.Qt.ShiftModifier: 

2079 p = 0 

2080 else: 

2081 p = 1 if m.get_polarity() != 1 else None 

2082 m.set_polarity(p) 

2083 

2084 elif key_event.key() == qc.Qt.Key_Down: 

2085 for m in self.selected_markers(): 

2086 if isinstance(m, PhaseMarker): 

2087 if key_event.modifiers() & qc.Qt.ShiftModifier: 

2088 p = 0 

2089 else: 

2090 p = -1 if m.get_polarity() != -1 else None 

2091 m.set_polarity(p) 

2092 

2093 elif keytext == 'b': 

2094 dt = self.tmax - self.tmin 

2095 self.interrupt_following() 

2096 self.set_time_range(self.tmin-dt, self.tmax-dt) 

2097 

2098 elif key_event.key() in (qc.Qt.Key_Tab, qc.Qt.Key_Backtab): 

2099 self.interrupt_following() 

2100 

2101 tgo = None 

2102 

2103 class TraceDummy(object): 

2104 def __init__(self, marker): 

2105 self._marker = marker 

2106 

2107 @property 

2108 def nslc_id(self): 

2109 return self._marker.one_nslc() 

2110 

2111 def marker_to_itrack(marker): 

2112 try: 

2113 return self.key_to_row.get( 

2114 self.gather(TraceDummy(marker)), -1) 

2115 

2116 except MarkerOneNSLCRequired: 

2117 return -1 

2118 

2119 emarker, pmarkers = self.get_active_markers() 

2120 pmarkers = [ 

2121 m for m in pmarkers if m.kind in self.visible_marker_kinds] 

2122 pmarkers.sort(key=lambda m: ( 

2123 marker_to_itrack(m), (m.tmin + m.tmax) / 2.0)) 

2124 

2125 if key_event.key() == qc.Qt.Key_Backtab: 

2126 pmarkers.reverse() 

2127 

2128 smarkers = self.selected_markers() 

2129 iselected = [] 

2130 for sm in smarkers: 

2131 try: 

2132 iselected.append(pmarkers.index(sm)) 

2133 except ValueError: 

2134 pass 

2135 

2136 if iselected: 

2137 icurrent = max(iselected) + 1 

2138 else: 

2139 icurrent = 0 

2140 

2141 if icurrent < len(pmarkers): 

2142 self.deselect_all() 

2143 cmarker = pmarkers[icurrent] 

2144 cmarker.selected = True 

2145 tgo = cmarker.tmin 

2146 if not self.tmin < tgo < self.tmax: 

2147 self.set_time_range(tgo-dt/2., tgo+dt/2.) 

2148 

2149 itrack = marker_to_itrack(cmarker) 

2150 if itrack != -1: 

2151 if itrack < self.shown_tracks_range[0]: 

2152 self.scroll_tracks( 

2153 - (self.shown_tracks_range[0] - itrack)) 

2154 elif self.shown_tracks_range[1] <= itrack: 

2155 self.scroll_tracks( 

2156 itrack - self.shown_tracks_range[1]+1) 

2157 

2158 if itrack not in self.track_to_nslc_ids: 

2159 self.go_to_selection() 

2160 

2161 elif keytext in ('p', 'n', 'P', 'N'): 

2162 smarkers = self.selected_markers() 

2163 tgo = None 

2164 dir = str(keytext) 

2165 if smarkers: 

2166 tmid = smarkers[0].tmin 

2167 for smarker in smarkers: 

2168 if dir == 'n': 

2169 tmid = max(smarker.tmin, tmid) 

2170 else: 

2171 tmid = min(smarker.tmin, tmid) 

2172 

2173 tgo = tmid 

2174 

2175 if dir.lower() == 'n': 

2176 for marker in sorted( 

2177 self.markers, 

2178 key=operator.attrgetter('tmin')): 

2179 

2180 t = marker.tmin 

2181 if t > tmid and \ 

2182 marker.kind in self.visible_marker_kinds and \ 

2183 (dir == 'n' or 

2184 isinstance(marker, EventMarker)): 

2185 

2186 self.deselect_all() 

2187 marker.selected = True 

2188 tgo = t 

2189 break 

2190 else: 

2191 for marker in sorted( 

2192 self.markers, 

2193 key=operator.attrgetter('tmin'), 

2194 reverse=True): 

2195 

2196 t = marker.tmin 

2197 if t < tmid and \ 

2198 marker.kind in self.visible_marker_kinds and \ 

2199 (dir == 'p' or 

2200 isinstance(marker, EventMarker)): 

2201 self.deselect_all() 

2202 marker.selected = True 

2203 tgo = t 

2204 break 

2205 

2206 if tgo is not None: 

2207 self.interrupt_following() 

2208 self.set_time_range(tgo-dt/2., tgo+dt/2.) 

2209 

2210 elif keytext == 'q' or keytext == 'x': 

2211 self.myclose(keytext) 

2212 

2213 elif keytext == 'r': 

2214 if self.pile.reload_modified(): 

2215 self.reloaded = True 

2216 

2217 elif keytext == 'R': 

2218 self.setup_snufflings() 

2219 

2220 elif key_event.key() == qc.Qt.Key_Backspace: 

2221 self.remove_selected_markers() 

2222 

2223 elif keytext == 'a': 

2224 for marker in self.markers: 

2225 if ((self.tmin <= marker.tmin <= self.tmax or 

2226 self.tmin <= marker.tmax <= self.tmax) and 

2227 marker.kind in self.visible_marker_kinds): 

2228 marker.selected = True 

2229 else: 

2230 marker.selected = False 

2231 

2232 elif keytext == 'A': 

2233 for marker in self.markers: 

2234 if marker.kind in self.visible_marker_kinds: 

2235 marker.selected = True 

2236 

2237 elif keytext == 'd': 

2238 self.deselect_all() 

2239 

2240 elif keytext == 'E': 

2241 self.deactivate_event_marker() 

2242 

2243 elif keytext == 'e': 

2244 markers = self.selected_markers() 

2245 event_markers_in_spe = [ 

2246 marker for marker in markers 

2247 if not isinstance(marker, PhaseMarker)] 

2248 

2249 phase_markers = [ 

2250 marker for marker in markers 

2251 if isinstance(marker, PhaseMarker)] 

2252 

2253 if len(event_markers_in_spe) == 1: 

2254 event_marker = event_markers_in_spe[0] 

2255 if not isinstance(event_marker, EventMarker): 

2256 nslcs = list(event_marker.nslc_ids) 

2257 lat, lon = 0.0, 0.0 

2258 old = self.get_active_event() 

2259 if len(nslcs) == 1: 

2260 lat, lon = self.station_latlon(NSLC(*nslcs[0])) 

2261 elif old is not None: 

2262 lat, lon = old.lat, old.lon 

2263 

2264 event_marker.convert_to_event_marker(lat, lon) 

2265 

2266 self.set_active_event_marker(event_marker) 

2267 event = event_marker.get_event() 

2268 for marker in phase_markers: 

2269 marker.set_event(event) 

2270 

2271 else: 

2272 for marker in event_markers_in_spe: 

2273 marker.convert_to_event_marker() 

2274 

2275 elif keytext in ('0', '1', '2', '3', '4', '5'): 

2276 for marker in self.selected_markers(): 

2277 marker.set_kind(int(keytext)) 

2278 self.emit_selected_markers() 

2279 

2280 elif key_event.key() in fkey_map: 

2281 self.handle_fkeys(key_event.key()) 

2282 

2283 elif key_event.key() == qc.Qt.Key_Escape: 

2284 if self.picking: 

2285 self.stop_picking(0, 0, abort=True) 

2286 

2287 elif key_event.key() == qc.Qt.Key_PageDown: 

2288 self.scroll_tracks( 

2289 self.shown_tracks_range[1]-self.shown_tracks_range[0]) 

2290 

2291 elif key_event.key() == qc.Qt.Key_PageUp: 

2292 self.scroll_tracks( 

2293 self.shown_tracks_range[0]-self.shown_tracks_range[1]) 

2294 

2295 elif keytext == '+': 

2296 self.zoom_tracks(0., 1.) 

2297 

2298 elif keytext == '-': 

2299 self.zoom_tracks(0., -1.) 

2300 

2301 elif keytext == '=': 

2302 ntracks_shown = self.shown_tracks_range[1] - \ 

2303 self.shown_tracks_range[0] 

2304 dtracks = self.initial_ntracks_shown_max - ntracks_shown 

2305 self.zoom_tracks(0., dtracks) 

2306 

2307 elif keytext == ':': 

2308 self.want_input.emit() 

2309 

2310 elif keytext == 'f': 

2311 if self.window().windowState() & qc.Qt.WindowFullScreen or \ 

2312 self.window().windowState() & qc.Qt.WindowMaximized: 

2313 

2314 self.window().showNormal() 

2315 else: 

2316 if macosx: 

2317 self.window().showMaximized() 

2318 else: 

2319 self.window().showFullScreen() 

2320 

2321 elif keytext == 'g': 

2322 self.go_to_selection() 

2323 

2324 elif keytext == 'G': 

2325 self.go_to_selection(tight=True) 

2326 

2327 elif keytext == 'm': 

2328 self.toggle_marker_editor() 

2329 

2330 elif keytext == 'c': 

2331 self.toggle_main_controls() 

2332 

2333 elif key_event.key() in (qc.Qt.Key_Left, qc.Qt.Key_Right): 

2334 dir = 1 

2335 amount = 1 

2336 if key_event.key() == qc.Qt.Key_Left: 

2337 dir = -1 

2338 if key_event.modifiers() & qc.Qt.ShiftModifier: 

2339 amount = 10 

2340 self.nudge_selected_markers(dir*amount) 

2341 

2342 if keytext != '' and keytext in 'degaApPnN': 

2343 self.emit_selected_markers() 

2344 

2345 self.update() 

2346 self.update_status() 

2347 

2348 def handle_fkeys(self, key): 

2349 self.set_phase_kind( 

2350 self.selected_markers(), 

2351 fkey_map[key] + 1) 

2352 self.emit_selected_markers() 

2353 

2354 def emit_selected_markers(self): 

2355 ibounds = [] 

2356 last_selected = False 

2357 for imarker, marker in enumerate(self.markers): 

2358 this_selected = marker.is_selected() 

2359 if this_selected != last_selected: 

2360 ibounds.append(imarker) 

2361 

2362 last_selected = this_selected 

2363 

2364 if last_selected: 

2365 ibounds.append(len(self.markers)) 

2366 

2367 chunks = list(zip(ibounds[::2], ibounds[1::2])) 

2368 self.n_selected_markers = sum( 

2369 chunk[1] - chunk[0] for chunk in chunks) 

2370 self.marker_selection_changed.emit(chunks) 

2371 

2372 def toggle_marker_editor(self): 

2373 self.panel_parent.toggle_marker_editor() 

2374 

2375 def toggle_main_controls(self): 

2376 self.panel_parent.toggle_main_controls() 

2377 

2378 def nudge_selected_markers(self, npixels): 

2379 a, b = self.time_projection.ur 

2380 c, d = self.time_projection.xr 

2381 for marker in self.selected_markers(): 

2382 if not isinstance(marker, EventMarker): 

2383 marker.tmin += npixels * (d-c)/b 

2384 marker.tmax += npixels * (d-c)/b 

2385 

2386 def about(self): 

2387 fn = pyrocko.util.data_file('snuffler.png') 

2388 with open(pyrocko.util.data_file('snuffler_about.html')) as f: 

2389 txt = f.read() 

2390 label = qw.QLabel(txt % {'logo': fn}) 

2391 label.setAlignment(qc.Qt.AlignVCenter | qc.Qt.AlignHCenter) 

2392 self.show_doc('About', [label], target='tab') 

2393 

2394 def help(self): 

2395 class MyScrollArea(qw.QScrollArea): 

2396 

2397 def sizeHint(self): 

2398 s = qc.QSize() 

2399 s.setWidth(self.widget().sizeHint().width()) 

2400 s.setHeight(self.widget().sizeHint().height()) 

2401 return s 

2402 

2403 with open(pyrocko.util.data_file( 

2404 'snuffler_help.html')) as f: 

2405 hcheat = qw.QLabel(f.read()) 

2406 

2407 with open(pyrocko.util.data_file( 

2408 'snuffler_help_epilog.html')) as f: 

2409 hepilog = qw.QLabel(f.read()) 

2410 

2411 for h in [hcheat, hepilog]: 

2412 h.setAlignment(qc.Qt.AlignTop | qc.Qt.AlignHCenter) 

2413 h.setWordWrap(True) 

2414 

2415 self.show_doc('Help', [hcheat, hepilog], target='panel') 

2416 

2417 def show_doc(self, name, labels, target='panel'): 

2418 scroller = qw.QScrollArea() 

2419 frame = qw.QFrame(scroller) 

2420 frame.setLineWidth(0) 

2421 layout = qw.QVBoxLayout() 

2422 layout.setContentsMargins(0, 0, 0, 0) 

2423 layout.setSpacing(0) 

2424 frame.setLayout(layout) 

2425 scroller.setWidget(frame) 

2426 scroller.setWidgetResizable(True) 

2427 frame.setBackgroundRole(qg.QPalette.Base) 

2428 for h in labels: 

2429 h.setParent(frame) 

2430 h.setMargin(3) 

2431 h.setTextInteractionFlags( 

2432 qc.Qt.LinksAccessibleByMouse | qc.Qt.TextSelectableByMouse) 

2433 h.setBackgroundRole(qg.QPalette.Base) 

2434 layout.addWidget(h) 

2435 h.linkActivated.connect( 

2436 self.open_link) 

2437 

2438 if self.panel_parent is not None: 

2439 if target == 'panel': 

2440 self.panel_parent.add_panel( 

2441 name, scroller, True, volatile=False) 

2442 else: 

2443 self.panel_parent.add_tab(name, scroller) 

2444 

2445 def open_link(self, link): 

2446 qg.QDesktopServices.openUrl(qc.QUrl(link)) 

2447 

2448 def wheelEvent(self, wheel_event): 

2449 if use_pyqt5: 

2450 self.wheel_pos += wheel_event.angleDelta().y() 

2451 else: 

2452 self.wheel_pos += wheel_event.delta() 

2453 

2454 n = self.wheel_pos // 120 

2455 self.wheel_pos = self.wheel_pos % 120 

2456 if n == 0: 

2457 return 

2458 

2459 amount = max( 

2460 1., 

2461 abs(self.shown_tracks_range[0]-self.shown_tracks_range[1])/5.) 

2462 wdelta = amount * n 

2463 

2464 trmin, trmax = self.track_to_screen.get_in_range() 

2465 anchor = (self.track_to_screen.rev(wheel_event.y())-trmin) \ 

2466 / (trmax-trmin) 

2467 

2468 if wheel_event.modifiers() & qc.Qt.ControlModifier: 

2469 self.zoom_tracks(anchor, wdelta) 

2470 else: 

2471 self.scroll_tracks(-wdelta) 

2472 

2473 def dragEnterEvent(self, event): 

2474 if event.mimeData().hasUrls(): 

2475 if any(url.toLocalFile() for url in event.mimeData().urls()): 

2476 event.setDropAction(qc.Qt.LinkAction) 

2477 event.accept() 

2478 

2479 def dropEvent(self, event): 

2480 if event.mimeData().hasUrls(): 

2481 paths = list( 

2482 str(url.toLocalFile()) for url in event.mimeData().urls()) 

2483 event.acceptProposedAction() 

2484 self.load(paths) 

2485 

2486 def get_phase_name(self, kind): 

2487 return self.config.get_phase_name(kind) 

2488 

2489 def set_phase_kind(self, markers, kind): 

2490 phasename = self.get_phase_name(kind) 

2491 

2492 for marker in markers: 

2493 if isinstance(marker, PhaseMarker): 

2494 if kind == 10: 

2495 marker.convert_to_marker() 

2496 else: 

2497 marker.set_phasename(phasename) 

2498 marker.set_event(self.get_active_event()) 

2499 

2500 elif isinstance(marker, EventMarker): 

2501 pass 

2502 

2503 else: 

2504 if kind != 10: 

2505 event = self.get_active_event() 

2506 marker.convert_to_phase_marker( 

2507 event, phasename, None, False) 

2508 

2509 def set_ntracks(self, ntracks): 

2510 if self.ntracks != ntracks: 

2511 self.ntracks = ntracks 

2512 if self.shown_tracks_range is not None: 

2513 l, h = self.shown_tracks_range 

2514 else: 

2515 l, h = 0, self.ntracks 

2516 

2517 self.tracks_range_changed.emit(self.ntracks, l, h) 

2518 

2519 def set_tracks_range(self, range, start=None): 

2520 

2521 low, high = range 

2522 low = min(self.ntracks-1, low) 

2523 high = min(self.ntracks, high) 

2524 low = max(0, low) 

2525 high = max(1, high) 

2526 

2527 if start is None: 

2528 start = float(low) 

2529 

2530 if self.shown_tracks_range != (low, high): 

2531 self.shown_tracks_range = low, high 

2532 self.shown_tracks_start = start 

2533 

2534 self.tracks_range_changed.emit(self.ntracks, low, high) 

2535 

2536 def scroll_tracks(self, shift): 

2537 shown = self.shown_tracks_range 

2538 shiftmin = -shown[0] 

2539 shiftmax = self.ntracks-shown[1] 

2540 shift = max(shiftmin, shift) 

2541 shift = min(shiftmax, shift) 

2542 shown = shown[0] + shift, shown[1] + shift 

2543 

2544 self.set_tracks_range((int(shown[0]), int(shown[1]))) 

2545 

2546 self.update() 

2547 

2548 def zoom_tracks(self, anchor, delta): 

2549 ntracks_shown = self.shown_tracks_range[1] \ 

2550 - self.shown_tracks_range[0] 

2551 

2552 if (ntracks_shown == 1 and delta <= 0) or \ 

2553 (ntracks_shown == self.ntracks and delta >= 0): 

2554 return 

2555 

2556 ntracks_shown += int(round(delta)) 

2557 ntracks_shown = min(max(1, ntracks_shown), self.ntracks) 

2558 

2559 u = self.shown_tracks_start 

2560 nu = max(0., u-anchor*delta) 

2561 nv = nu + ntracks_shown 

2562 if nv > self.ntracks: 

2563 nu -= nv - self.ntracks 

2564 nv -= nv - self.ntracks 

2565 

2566 self.set_tracks_range((int(round(nu)), int(round(nv))), nu) 

2567 

2568 self.ntracks_shown_max = self.shown_tracks_range[1] \ 

2569 - self.shown_tracks_range[0] 

2570 

2571 self.update() 

2572 

2573 def content_time_range(self): 

2574 pile = self.get_pile() 

2575 tmin, tmax = pile.get_tmin(), pile.get_tmax() 

2576 if tmin is None: 

2577 tmin = initial_time_range[0] 

2578 if tmax is None: 

2579 tmax = initial_time_range[1] 

2580 

2581 return tmin, tmax 

2582 

2583 def content_deltat_range(self): 

2584 pile = self.get_pile() 

2585 

2586 deltatmin, deltatmax = pile.get_deltatmin(), pile.get_deltatmax() 

2587 

2588 if deltatmin is None: 

2589 deltatmin = 0.001 

2590 

2591 if deltatmax is None: 

2592 deltatmax = 1000.0 

2593 

2594 return deltatmin, deltatmax 

2595 

2596 def make_good_looking_time_range(self, tmin, tmax, tight=False): 

2597 if tmax < tmin: 

2598 tmin, tmax = tmax, tmin 

2599 

2600 deltatmin = self.content_deltat_range()[0] 

2601 dt = deltatmin * self.visible_length * 0.95 

2602 

2603 if dt == 0.0: 

2604 dt = 1.0 

2605 

2606 if tight: 

2607 if tmax != tmin: 

2608 dtm = tmax - tmin 

2609 tmin -= dtm*0.1 

2610 tmax += dtm*0.1 

2611 return tmin, tmax 

2612 else: 

2613 tcenter = (tmin + tmax) / 2. 

2614 tmin = tcenter - 0.5*dt 

2615 tmax = tcenter + 0.5*dt 

2616 return tmin, tmax 

2617 

2618 if tmax-tmin < dt: 

2619 vmin, vmax = self.get_time_range() 

2620 dt = min(vmax - vmin, dt) 

2621 

2622 tcenter = (tmin+tmax)/2. 

2623 etmin, etmax = tmin, tmax 

2624 tmin = min(etmin, tcenter - 0.5*dt) 

2625 tmax = max(etmax, tcenter + 0.5*dt) 

2626 dtm = tmax-tmin 

2627 if etmin == tmin: 

2628 tmin -= dtm*0.1 

2629 if etmax == tmax: 

2630 tmax += dtm*0.1 

2631 

2632 else: 

2633 dtm = tmax-tmin 

2634 tmin -= dtm*0.1 

2635 tmax += dtm*0.1 

2636 

2637 return tmin, tmax 

2638 

2639 def go_to_selection(self, tight=False): 

2640 markers = self.selected_markers() 

2641 if markers: 

2642 tmax, tmin = self.content_time_range() 

2643 for marker in markers: 

2644 tmin = min(tmin, marker.tmin) 

2645 tmax = max(tmax, marker.tmax) 

2646 

2647 else: 

2648 if tight: 

2649 vmin, vmax = self.get_time_range() 

2650 tmin = tmax = (vmin + vmax) / 2. 

2651 else: 

2652 tmin, tmax = self.content_time_range() 

2653 

2654 tmin, tmax = self.make_good_looking_time_range( 

2655 tmin, tmax, tight=tight) 

2656 

2657 self.interrupt_following() 

2658 self.set_time_range(tmin, tmax) 

2659 self.update() 

2660 

2661 def go_to_time(self, t, tlen=None): 

2662 tmax = t 

2663 if tlen is not None: 

2664 tmax = t+tlen 

2665 tmin, tmax = self.make_good_looking_time_range(t, tmax) 

2666 self.interrupt_following() 

2667 self.set_time_range(tmin, tmax) 

2668 self.update() 

2669 

2670 def go_to_event_by_name(self, name): 

2671 for marker in self.markers: 

2672 if isinstance(marker, EventMarker): 

2673 event = marker.get_event() 

2674 if event.name and event.name.lower() == name.lower(): 

2675 tmin, tmax = self.make_good_looking_time_range( 

2676 event.time, event.time) 

2677 

2678 self.interrupt_following() 

2679 self.set_time_range(tmin, tmax) 

2680 

2681 def printit(self): 

2682 from .qt_compat import qprint 

2683 printer = qprint.QPrinter() 

2684 printer.setOrientation(qprint.QPrinter.Landscape) 

2685 

2686 dialog = qprint.QPrintDialog(printer, self) 

2687 dialog.setWindowTitle('Print') 

2688 

2689 if dialog.exec_() != qw.QDialog.Accepted: 

2690 return 

2691 

2692 painter = qg.QPainter() 

2693 painter.begin(printer) 

2694 page = printer.pageRect() 

2695 self.drawit( 

2696 painter, printmode=False, w=page.width(), h=page.height()) 

2697 

2698 painter.end() 

2699 

2700 def savesvg(self, fn=None): 

2701 

2702 if not fn: 

2703 fn, _ = fnpatch(qw.QFileDialog.getSaveFileName( 

2704 self, 

2705 'Save as SVG|PNG', 

2706 os.path.expanduser(os.path.join('~', 'untitled.svg')), 

2707 'SVG|PNG (*.svg *.png)', 

2708 options=qfiledialog_options)) 

2709 

2710 if fn == '': 

2711 return 

2712 

2713 fn = str(fn) 

2714 

2715 if fn.lower().endswith('.svg'): 

2716 try: 

2717 w, h = 842, 595 

2718 margin = 0.025 

2719 m = max(w, h)*margin 

2720 

2721 generator = qsvg.QSvgGenerator() 

2722 generator.setFileName(fn) 

2723 generator.setSize(qc.QSize(w, h)) 

2724 generator.setViewBox(qc.QRectF(-m, -m, w+2*m, h+2*m)) 

2725 

2726 painter = qg.QPainter() 

2727 painter.begin(generator) 

2728 self.drawit(painter, printmode=False, w=w, h=h) 

2729 painter.end() 

2730 

2731 except Exception as e: 

2732 self.fail('Failed to write SVG file: %s' % str(e)) 

2733 

2734 elif fn.lower().endswith('.png'): 

2735 if use_pyqt5: 

2736 pixmap = self.grab() 

2737 else: 

2738 pixmap = qg.QPixmap().grabWidget(self) 

2739 

2740 try: 

2741 pixmap.save(fn) 

2742 

2743 except Exception as e: 

2744 self.fail('Failed to write PNG file: %s' % str(e)) 

2745 

2746 else: 

2747 self.fail( 

2748 'Unsupported file type: filename must end with ".svg" or ' 

2749 '".png".') 

2750 

2751 def paintEvent(self, paint_ev): 

2752 ''' 

2753 Called by QT whenever widget needs to be painted. 

2754 ''' 

2755 painter = qg.QPainter(self) 

2756 

2757 if self.menuitem_antialias.isChecked(): 

2758 painter.setRenderHint(qg.QPainter.Antialiasing) 

2759 

2760 self.drawit(painter) 

2761 

2762 logger.debug( 

2763 'Time spent drawing: %.3f %.3f %.3f %.3f %.3f' % 

2764 (self.timer_draw - self.timer_cutout)) 

2765 

2766 logger.debug( 

2767 'Time spent processing: %.3f %.3f %.3f %.3f %.3f' % 

2768 self.timer_cutout.get()) 

2769 

2770 self.time_spent_painting = self.timer_draw.get()[-1] 

2771 self.time_last_painted = time.time() 

2772 

2773 def determine_box_styles(self): 

2774 

2775 traces = list(self.pile.iter_traces()) 

2776 traces.sort(key=operator.attrgetter('full_id')) 

2777 istyle = 0 

2778 trace_styles = {} 

2779 for itr, tr in enumerate(traces): 

2780 if itr > 0: 

2781 other = traces[itr-1] 

2782 if not ( 

2783 other.nslc_id == tr.nslc_id 

2784 and other.deltat == tr.deltat 

2785 and abs(other.tmax - tr.tmin) 

2786 < gap_lap_tolerance*tr.deltat): 

2787 

2788 istyle += 1 

2789 

2790 trace_styles[tr.full_id, tr.deltat] = istyle 

2791 

2792 self.trace_styles = trace_styles 

2793 

2794 def draw_trace_boxes(self, p, time_projection, track_projections): 

2795 

2796 for v_projection in track_projections.values(): 

2797 v_projection.set_in_range(0., 1.) 

2798 

2799 def selector(x): 

2800 return x.overlaps(*time_projection.get_in_range()) 

2801 

2802 if self.trace_filter is not None: 

2803 def tselector(x): 

2804 return selector(x) and self.trace_filter(x) 

2805 

2806 else: 

2807 tselector = selector 

2808 

2809 traces = list(self.pile.iter_traces( 

2810 group_selector=selector, trace_selector=tselector)) 

2811 

2812 traces.sort(key=operator.attrgetter('full_id')) 

2813 

2814 def drawbox(itrack, istyle, traces): 

2815 v_projection = track_projections[itrack] 

2816 dvmin = v_projection(0.) 

2817 dvmax = v_projection(1.) 

2818 dtmin = time_projection.clipped(traces[0].tmin) 

2819 dtmax = time_projection.clipped(traces[-1].tmax) 

2820 

2821 style = box_styles[istyle % len(box_styles)] 

2822 rect = qc.QRectF(dtmin, dvmin, float(dtmax-dtmin), dvmax-dvmin) 

2823 p.fillRect(rect, style.fill_brush) 

2824 p.setPen(style.frame_pen) 

2825 p.drawRect(rect) 

2826 

2827 traces_by_style = {} 

2828 for itr, tr in enumerate(traces): 

2829 gt = self.gather(tr) 

2830 if gt not in self.key_to_row: 

2831 continue 

2832 

2833 itrack = self.key_to_row[gt] 

2834 if itrack not in track_projections: 

2835 continue 

2836 

2837 istyle = self.trace_styles.get((tr.full_id, tr.deltat), 0) 

2838 

2839 if len(traces) < 500: 

2840 drawbox(itrack, istyle, [tr]) 

2841 else: 

2842 if (itrack, istyle) not in traces_by_style: 

2843 traces_by_style[itrack, istyle] = [] 

2844 traces_by_style[itrack, istyle].append(tr) 

2845 

2846 for (itrack, istyle), traces in traces_by_style.items(): 

2847 drawbox(itrack, istyle, traces) 

2848 

2849 def draw_visible_markers( 

2850 self, p, vcenter_projection, primary_pen): 

2851 

2852 try: 

2853 markers = self.markers.with_key_in_limited( 

2854 self.tmin - self.markers_deltat_max, self.tmax, 2000) 

2855 

2856 except pyrocko.pile.TooMany: 

2857 tmin = self.markers[0].tmin 

2858 tmax = self.markers[-1].tmax 

2859 umin_view, umax_view = self.time_projection.get_out_range() 

2860 umin = max(umin_view, self.time_projection(tmin)) 

2861 umax = min(umax_view, self.time_projection(tmax)) 

2862 v0, _ = vcenter_projection.get_out_range() 

2863 label_bg = qg.QBrush(qg.QColor(255, 255, 255)) 

2864 

2865 p.save() 

2866 

2867 pen = qg.QPen(primary_pen) 

2868 pen.setWidth(2) 

2869 pen.setStyle(qc.Qt.DotLine) 

2870 # pat = [5., 3.] 

2871 # pen.setDashPattern(pat) 

2872 p.setPen(pen) 

2873 

2874 if self.n_selected_markers == len(self.markers): 

2875 s_selected = ' (all selected)' 

2876 elif self.n_selected_markers > 0: 

2877 s_selected = ' (%i selected)' % self.n_selected_markers 

2878 else: 

2879 s_selected = '' 

2880 

2881 draw_label( 

2882 p, umin+10., v0-10., 

2883 '%i Markers' % len(self.markers) + s_selected, 

2884 label_bg, 'LB') 

2885 

2886 line = qc.QLineF(umin, v0, umax, v0) 

2887 p.drawLine(line) 

2888 p.restore() 

2889 

2890 return 

2891 

2892 for marker in markers: 

2893 if marker.tmin < self.tmax and self.tmin < marker.tmax \ 

2894 and marker.kind in self.visible_marker_kinds: 

2895 

2896 marker.draw( 

2897 p, self.time_projection, vcenter_projection, 

2898 with_label=True) 

2899 

2900 def drawit(self, p, printmode=False, w=None, h=None): 

2901 ''' 

2902 This performs the actual drawing. 

2903 ''' 

2904 

2905 self.timer_draw.start() 

2906 

2907 if self.gather is None: 

2908 self.set_gathering() 

2909 

2910 if self.pile_has_changed: 

2911 

2912 if not self.sortingmode_change_delayed(): 

2913 self.sortingmode_change() 

2914 

2915 if self.menuitem_showboxes.isChecked(): 

2916 self.determine_box_styles() 

2917 

2918 self.pile_has_changed = False 

2919 

2920 if h is None: 

2921 h = float(self.height()) 

2922 if w is None: 

2923 w = float(self.width()) 

2924 

2925 if printmode: 

2926 primary_color = (0, 0, 0) 

2927 else: 

2928 primary_color = pyrocko.plot.tango_colors['aluminium5'] 

2929 

2930 primary_pen = qg.QPen(qg.QColor(*primary_color)) 

2931 

2932 ax_h = float(self.ax_height) 

2933 

2934 vbottom_ax_projection = Projection() 

2935 vtop_ax_projection = Projection() 

2936 vcenter_projection = Projection() 

2937 

2938 self.time_projection.set_out_range(0., w) 

2939 vbottom_ax_projection.set_out_range(h-ax_h, h) 

2940 vtop_ax_projection.set_out_range(0., ax_h) 

2941 vcenter_projection.set_out_range(ax_h, h-ax_h) 

2942 vcenter_projection.set_in_range(0., 1.) 

2943 self.track_to_screen.set_out_range(ax_h, h-ax_h) 

2944 

2945 self.track_to_screen.set_in_range(*self.shown_tracks_range) 

2946 track_projections = {} 

2947 for i in range(*self.shown_tracks_range): 

2948 proj = Projection() 

2949 proj.set_out_range( 

2950 self.track_to_screen(i+0.05), 

2951 self.track_to_screen(i+1.-0.05)) 

2952 

2953 track_projections[i] = proj 

2954 

2955 if self.tmin < self.tmax: 

2956 self.time_projection.set_in_range(self.tmin, self.tmax) 

2957 vbottom_ax_projection.set_in_range(0, ax_h) 

2958 

2959 self.tax.drawit(p, self.time_projection, vbottom_ax_projection) 

2960 

2961 yscaler = pyrocko.plot.AutoScaler() 

2962 if not printmode and self.menuitem_showboxes.isChecked(): 

2963 self.draw_trace_boxes( 

2964 p, self.time_projection, track_projections) 

2965 

2966 if self.floating_marker: 

2967 self.floating_marker.draw( 

2968 p, self.time_projection, vcenter_projection) 

2969 

2970 self.draw_visible_markers( 

2971 p, vcenter_projection, primary_pen) 

2972 

2973 p.setPen(primary_pen) 

2974 

2975 font = qg.QFont() 

2976 font.setBold(True) 

2977 

2978 axannotfont = qg.QFont() 

2979 axannotfont.setBold(True) 

2980 axannotfont.setPointSize(8) 

2981 

2982 p.setFont(font) 

2983 label_bg = qg.QBrush(qg.QColor(255, 255, 255, 100)) 

2984 

2985 processed_traces = self.prepare_cutout2( 

2986 self.tmin, self.tmax, 

2987 trace_selector=self.trace_selector, 

2988 degap=self.menuitem_degap.isChecked(), 

2989 demean=self.menuitem_demean.isChecked()) 

2990 

2991 color_lookup = dict( 

2992 [(k, i) for (i, k) in enumerate(self.color_keys)]) 

2993 

2994 self.track_to_nslc_ids = {} 

2995 nticks = 0 

2996 annot_labels = [] 

2997 if processed_traces: 

2998 show_scales = self.menuitem_showscalerange.isChecked() \ 

2999 or self.menuitem_showscaleaxis.isChecked() 

3000 

3001 fm = qg.QFontMetrics(axannotfont, p.device()) 

3002 trackheight = self.track_to_screen(1.-0.05) \ 

3003 - self.track_to_screen(0.05) 

3004 

3005 nlinesavail = trackheight/float(fm.lineSpacing()) 

3006 if self.menuitem_showscaleaxis.isChecked(): 

3007 nticks = max(3, min(nlinesavail * 0.5, 15)) 

3008 else: 

3009 nticks = 15 

3010 

3011 yscaler = pyrocko.plot.AutoScaler( 

3012 no_exp_interval=(-3, 2), approx_ticks=nticks, 

3013 snap=show_scales 

3014 and not self.menuitem_showscaleaxis.isChecked()) 

3015 

3016 data_ranges = pyrocko.trace.minmax( 

3017 processed_traces, 

3018 key=self.scaling_key, 

3019 mode=self.scaling_base) 

3020 

3021 if not self.menuitem_fixscalerange.isChecked(): 

3022 self.old_data_ranges = data_ranges 

3023 else: 

3024 data_ranges.update(self.old_data_ranges) 

3025 

3026 self.apply_scaling_hooks(data_ranges) 

3027 

3028 trace_to_itrack = {} 

3029 track_scaling_keys = {} 

3030 track_scaling_colors = {} 

3031 for trace in processed_traces: 

3032 gt = self.gather(trace) 

3033 if gt not in self.key_to_row: 

3034 continue 

3035 

3036 itrack = self.key_to_row[gt] 

3037 if itrack not in track_projections: 

3038 continue 

3039 

3040 trace_to_itrack[trace] = itrack 

3041 

3042 if itrack not in self.track_to_nslc_ids: 

3043 self.track_to_nslc_ids[itrack] = set() 

3044 

3045 self.track_to_nslc_ids[itrack].add(trace.nslc_id) 

3046 

3047 if itrack not in track_scaling_keys: 

3048 track_scaling_keys[itrack] = set() 

3049 

3050 scaling_key = self.scaling_key(trace) 

3051 track_scaling_keys[itrack].add(scaling_key) 

3052 

3053 color = pyrocko.plot.color( 

3054 color_lookup[self.color_gather(trace)]) 

3055 

3056 k = itrack, scaling_key 

3057 if k not in track_scaling_colors \ 

3058 and self.menuitem_colortraces.isChecked(): 

3059 track_scaling_colors[k] = color 

3060 else: 

3061 track_scaling_colors[k] = primary_color 

3062 

3063 # y axes, zero lines 

3064 

3065 trace_projections = {} 

3066 for itrack in list(track_projections.keys()): 

3067 if itrack not in track_scaling_keys: 

3068 continue 

3069 uoff = 0 

3070 for scaling_key in track_scaling_keys[itrack]: 

3071 data_range = data_ranges[scaling_key] 

3072 dymin, dymax = data_range 

3073 ymin, ymax, yinc = yscaler.make_scale( 

3074 (dymin/self.gain, dymax/self.gain)) 

3075 iexp = yscaler.make_exp(yinc) 

3076 factor = 10**iexp 

3077 trace_projection = track_projections[itrack].copy() 

3078 trace_projection.set_in_range(ymax, ymin) 

3079 trace_projections[itrack, scaling_key] = \ 

3080 trace_projection 

3081 umin, umax = self.time_projection.get_out_range() 

3082 vmin, vmax = trace_projection.get_out_range() 

3083 umax_zeroline = umax 

3084 uoffnext = uoff 

3085 

3086 if show_scales: 

3087 pen = qg.QPen(primary_pen) 

3088 k = itrack, scaling_key 

3089 if k in track_scaling_colors: 

3090 c = qg.QColor(*track_scaling_colors[ 

3091 itrack, scaling_key]) 

3092 

3093 pen.setColor(c) 

3094 

3095 p.setPen(pen) 

3096 if nlinesavail > 3: 

3097 if self.menuitem_showscaleaxis.isChecked(): 

3098 ymin_annot = math.ceil(ymin/yinc)*yinc 

3099 ny_annot = int( 

3100 math.floor(ymax/yinc) 

3101 - math.ceil(ymin/yinc)) + 1 

3102 

3103 for iy_annot in range(ny_annot): 

3104 y = ymin_annot + iy_annot*yinc 

3105 v = trace_projection(y) 

3106 line = qc.QLineF( 

3107 umax-10-uoff, v, umax-uoff, v) 

3108 

3109 p.drawLine(line) 

3110 if iy_annot == ny_annot - 1 \ 

3111 and iexp != 0: 

3112 

3113 sexp = ' &times; ' \ 

3114 '10<sup>%i</sup>' % iexp 

3115 else: 

3116 sexp = '' 

3117 

3118 snum = num_to_html(y/factor) 

3119 lab = Label( 

3120 p, 

3121 umax-20-uoff, 

3122 v, '%s%s' % (snum, sexp), 

3123 label_bg=None, 

3124 anchor='MR', 

3125 font=axannotfont, 

3126 color=c) 

3127 

3128 uoffnext = max( 

3129 lab.rect.width()+30., 

3130 uoffnext) 

3131 

3132 annot_labels.append(lab) 

3133 if y == 0.: 

3134 umax_zeroline = \ 

3135 umax - 20 \ 

3136 - lab.rect.width() - 10 \ 

3137 - uoff 

3138 else: 

3139 if not self.menuitem_showboxes\ 

3140 .isChecked(): 

3141 qpoints = make_QPolygonF( 

3142 [umax-20-uoff, 

3143 umax-10-uoff, 

3144 umax-10-uoff, 

3145 umax-20-uoff], 

3146 [vmax, vmax, vmin, vmin]) 

3147 p.drawPolyline(qpoints) 

3148 

3149 snum = num_to_html(ymin) 

3150 labmin = Label( 

3151 p, umax-15-uoff, vmax, snum, 

3152 label_bg=None, 

3153 anchor='BR', 

3154 font=axannotfont, 

3155 color=c) 

3156 

3157 annot_labels.append(labmin) 

3158 snum = num_to_html(ymax) 

3159 labmax = Label( 

3160 p, umax-15-uoff, vmin, snum, 

3161 label_bg=None, 

3162 anchor='TR', 

3163 font=axannotfont, 

3164 color=c) 

3165 

3166 annot_labels.append(labmax) 

3167 

3168 for lab in (labmin, labmax): 

3169 uoffnext = max( 

3170 lab.rect.width()+10., uoffnext) 

3171 

3172 if self.menuitem_showzeroline.isChecked(): 

3173 v = trace_projection(0.) 

3174 if vmin <= v <= vmax: 

3175 line = qc.QLineF(umin, v, umax_zeroline, v) 

3176 p.drawLine(line) 

3177 

3178 uoff = uoffnext 

3179 

3180 p.setFont(font) 

3181 p.setPen(primary_pen) 

3182 for trace in processed_traces: 

3183 if trace not in trace_to_itrack: 

3184 continue 

3185 

3186 itrack = trace_to_itrack[trace] 

3187 scaling_key = self.scaling_key(trace) 

3188 trace_projection = trace_projections[ 

3189 itrack, scaling_key] 

3190 

3191 vdata = trace_projection(trace.get_ydata()) 

3192 

3193 udata_min = float(self.time_projection(trace.tmin)) 

3194 udata_max = float(self.time_projection( 

3195 trace.tmin+trace.deltat*(vdata.size-1))) 

3196 udata = num.linspace(udata_min, udata_max, vdata.size) 

3197 

3198 qpoints = make_QPolygonF(udata, vdata) 

3199 

3200 umin, umax = self.time_projection.get_out_range() 

3201 vmin, vmax = trace_projection.get_out_range() 

3202 

3203 trackrect = qc.QRectF(umin, vmin, umax-umin, vmax-vmin) 

3204 

3205 if self.menuitem_cliptraces.isChecked(): 

3206 p.setClipRect(trackrect) 

3207 

3208 if self.menuitem_colortraces.isChecked(): 

3209 color = pyrocko.plot.color( 

3210 color_lookup[self.color_gather(trace)]) 

3211 pen = qg.QPen(qg.QColor(*color), 1) 

3212 p.setPen(pen) 

3213 

3214 p.drawPolyline(qpoints) 

3215 

3216 if self.floating_marker: 

3217 self.floating_marker.draw_trace( 

3218 self, p, trace, 

3219 self.time_projection, trace_projection, 1.0) 

3220 

3221 for marker in self.markers.with_key_in( 

3222 self.tmin - self.markers_deltat_max, 

3223 self.tmax): 

3224 

3225 if marker.tmin < self.tmax \ 

3226 and self.tmin < marker.tmax \ 

3227 and marker.kind \ 

3228 in self.visible_marker_kinds: 

3229 marker.draw_trace( 

3230 self, p, trace, self.time_projection, 

3231 trace_projection, 1.0) 

3232 

3233 p.setPen(primary_pen) 

3234 

3235 if self.menuitem_cliptraces.isChecked(): 

3236 p.setClipRect(0, 0, w, h) 

3237 

3238 p.setPen(primary_pen) 

3239 

3240 while font.pointSize() > 2: 

3241 fm = qg.QFontMetrics(font, p.device()) 

3242 trackheight = self.track_to_screen(1.-0.05) \ 

3243 - self.track_to_screen(0.05) 

3244 nlinesavail = trackheight/float(fm.lineSpacing()) 

3245 if nlinesavail > 1: 

3246 break 

3247 

3248 font.setPointSize(font.pointSize()-1) 

3249 

3250 p.setFont(font) 

3251 for key in self.track_keys: 

3252 itrack = self.key_to_row[key] 

3253 if itrack in track_projections: 

3254 plabel = ' '.join( 

3255 [str(x) for x in key if x is not None]) 

3256 lx = 10 

3257 ly = self.track_to_screen(itrack+0.5) 

3258 draw_label(p, lx, ly, plabel, label_bg, 'ML') 

3259 

3260 for lab in annot_labels: 

3261 lab.draw() 

3262 

3263 self.timer_draw.stop() 

3264 

3265 def see_data_params(self): 

3266 

3267 min_deltat = self.content_deltat_range()[0] 

3268 

3269 # determine padding and downampling requirements 

3270 if self.lowpass is not None: 

3271 deltat_target = 1./self.lowpass * 0.25 

3272 ndecimate = min( 

3273 50, 

3274 max(1, int(round(deltat_target / min_deltat)))) 

3275 tpad = 1./self.lowpass * 2. 

3276 else: 

3277 ndecimate = 1 

3278 tpad = min_deltat*5. 

3279 

3280 if self.highpass is not None: 

3281 tpad = max(1./self.highpass * 2., tpad) 

3282 

3283 nsee_points_per_trace = 5000*10 

3284 tsee = ndecimate*nsee_points_per_trace*min_deltat 

3285 

3286 return ndecimate, tpad, tsee 

3287 

3288 def clean_update(self): 

3289 self.old_processed_traces = None 

3290 self.update() 

3291 

3292 def get_adequate_tpad(self): 

3293 tpad = 0. 

3294 for f in [self.highpass, self.lowpass]: 

3295 if f is not None: 

3296 tpad = max(tpad, 1.0/f) 

3297 

3298 for snuffling in self.snufflings: 

3299 if snuffling._post_process_hook_enabled \ 

3300 or snuffling._pre_process_hook_enabled: 

3301 

3302 tpad = max(tpad, snuffling.get_tpad()) 

3303 

3304 return tpad 

3305 

3306 def prepare_cutout2( 

3307 self, tmin, tmax, trace_selector=None, degap=True, 

3308 demean=True, nmax=6000): 

3309 

3310 if self.pile.is_empty(): 

3311 return [] 

3312 

3313 nmax = self.visible_length 

3314 

3315 self.timer_cutout.start() 

3316 

3317 tsee = tmax-tmin 

3318 min_deltat_wo_decimate = tsee/nmax 

3319 min_deltat_w_decimate = min_deltat_wo_decimate / 32. 

3320 

3321 min_deltat_allow = min_deltat_wo_decimate 

3322 if self.lowpass is not None: 

3323 target_deltat_lp = 0.25/self.lowpass 

3324 if target_deltat_lp > min_deltat_wo_decimate: 

3325 min_deltat_allow = min_deltat_w_decimate 

3326 

3327 min_deltat_allow = math.exp( 

3328 int(math.floor(math.log(min_deltat_allow)))) 

3329 

3330 tmin_ = tmin 

3331 tmax_ = tmax 

3332 

3333 # fetch more than needed? 

3334 if self.menuitem_liberal_fetch.isChecked(): 

3335 tlen = pyrocko.trace.nextpow2((tmax-tmin)*1.5) 

3336 tmin = math.floor(tmin/tlen) * tlen 

3337 tmax = math.ceil(tmax/tlen) * tlen 

3338 

3339 fft_filtering = self.menuitem_fft_filtering.isChecked() 

3340 lphp = self.menuitem_lphp.isChecked() 

3341 ads = self.menuitem_allowdownsampling.isChecked() 

3342 

3343 tpad = self.get_adequate_tpad() 

3344 tpad = max(tpad, tsee) 

3345 

3346 # state vector to decide if cached traces can be used 

3347 vec = ( 

3348 tmin, tmax, tpad, trace_selector, degap, demean, self.lowpass, 

3349 self.highpass, fft_filtering, lphp, 

3350 min_deltat_allow, self.rotate, self.shown_tracks_range, 

3351 ads, self.pile.get_update_count()) 

3352 

3353 if (self.old_vec 

3354 and self.old_vec[0] <= vec[0] 

3355 and vec[1] <= self.old_vec[1] 

3356 and vec[2:] == self.old_vec[2:] 

3357 and not (self.reloaded or self.menuitem_watch.isChecked()) 

3358 and self.old_processed_traces is not None): 

3359 

3360 logger.debug('Using cached traces') 

3361 processed_traces = self.old_processed_traces 

3362 

3363 else: 

3364 self.old_vec = vec 

3365 

3366 processed_traces = [] 

3367 

3368 if self.pile.deltatmax >= min_deltat_allow: 

3369 

3370 def group_selector(gr): 

3371 return gr.deltatmax >= min_deltat_allow 

3372 

3373 if trace_selector is not None: 

3374 def trace_selectorx(tr): 

3375 return tr.deltat >= min_deltat_allow \ 

3376 and trace_selector(tr) 

3377 else: 

3378 def trace_selectorx(tr): 

3379 return tr.deltat >= min_deltat_allow 

3380 

3381 for traces in self.pile.chopper( 

3382 tmin=tmin, tmax=tmax, tpad=tpad, 

3383 want_incomplete=True, 

3384 degap=degap, 

3385 maxgap=gap_lap_tolerance, 

3386 maxlap=gap_lap_tolerance, 

3387 keep_current_files_open=True, 

3388 group_selector=group_selector, 

3389 trace_selector=trace_selectorx, 

3390 accessor_id=id(self), 

3391 snap=(math.floor, math.ceil), 

3392 include_last=True): 

3393 

3394 if demean: 

3395 for tr in traces: 

3396 if (tr.meta and tr.meta.get('tabu', False)): 

3397 continue 

3398 y = tr.get_ydata() 

3399 tr.set_ydata(y - num.mean(y)) 

3400 

3401 traces = self.pre_process_hooks(traces) 

3402 

3403 for trace in traces: 

3404 

3405 if not (trace.meta 

3406 and trace.meta.get('tabu', False)): 

3407 

3408 if fft_filtering: 

3409 but = pyrocko.trace.ButterworthResponse 

3410 multres = pyrocko.trace.MultiplyResponse 

3411 if self.lowpass is not None \ 

3412 or self.highpass is not None: 

3413 

3414 it = num.arange( 

3415 trace.data_len(), dtype=float) 

3416 detr_data, m, b = detrend( 

3417 it, trace.get_ydata()) 

3418 

3419 trace.set_ydata(detr_data) 

3420 

3421 freqs, fdata = trace.spectrum( 

3422 pad_to_pow2=True, tfade=None) 

3423 

3424 nfreqs = fdata.size 

3425 

3426 key = (trace.deltat, nfreqs) 

3427 

3428 if key not in self.tf_cache: 

3429 resps = [] 

3430 if self.lowpass is not None: 

3431 resps.append(but( 

3432 order=4, 

3433 corner=self.lowpass, 

3434 type='low')) 

3435 

3436 if self.highpass is not None: 

3437 resps.append(but( 

3438 order=4, 

3439 corner=self.highpass, 

3440 type='high')) 

3441 

3442 resp = multres(resps) 

3443 self.tf_cache[key] = \ 

3444 resp.evaluate(freqs) 

3445 

3446 filtered_data = num.fft.irfft( 

3447 fdata*self.tf_cache[key] 

3448 )[:trace.data_len()] 

3449 

3450 retrended_data = retrend( 

3451 it, filtered_data, m, b) 

3452 

3453 trace.set_ydata(retrended_data) 

3454 

3455 else: 

3456 

3457 if ads and self.lowpass is not None: 

3458 while trace.deltat \ 

3459 < min_deltat_wo_decimate: 

3460 

3461 trace.downsample(2, demean=False) 

3462 

3463 fmax = 0.5/trace.deltat 

3464 if not lphp and ( 

3465 self.lowpass is not None 

3466 and self.highpass is not None 

3467 and self.lowpass < fmax 

3468 and self.highpass < fmax 

3469 and self.highpass < self.lowpass): 

3470 

3471 trace.bandpass( 

3472 2, self.highpass, self.lowpass) 

3473 else: 

3474 if self.lowpass is not None: 

3475 if self.lowpass < 0.5/trace.deltat: 

3476 trace.lowpass( 

3477 4, self.lowpass, 

3478 demean=False) 

3479 

3480 if self.highpass is not None: 

3481 if self.lowpass is None \ 

3482 or self.highpass \ 

3483 < self.lowpass: 

3484 

3485 if self.highpass < \ 

3486 0.5/trace.deltat: 

3487 trace.highpass( 

3488 4, self.highpass, 

3489 demean=False) 

3490 

3491 processed_traces.append(trace) 

3492 

3493 if self.rotate != 0.0: 

3494 phi = self.rotate/180.*math.pi 

3495 cphi = math.cos(phi) 

3496 sphi = math.sin(phi) 

3497 for a in processed_traces: 

3498 for b in processed_traces: 

3499 if (a.network == b.network 

3500 and a.station == b.station 

3501 and a.location == b.location 

3502 and ((a.channel.lower().endswith('n') 

3503 and b.channel.lower().endswith('e')) 

3504 or (a.channel.endswith('1') 

3505 and b.channel.endswith('2'))) 

3506 and abs(a.deltat-b.deltat) < a.deltat*0.001 

3507 and abs(a.tmin-b.tmin) < a.deltat*0.01 and 

3508 len(a.get_ydata()) == len(b.get_ydata())): 

3509 

3510 aydata = a.get_ydata()*cphi+b.get_ydata()*sphi 

3511 bydata = -a.get_ydata()*sphi+b.get_ydata()*cphi 

3512 a.set_ydata(aydata) 

3513 b.set_ydata(bydata) 

3514 

3515 processed_traces = self.post_process_hooks(processed_traces) 

3516 

3517 self.old_processed_traces = processed_traces 

3518 

3519 chopped_traces = [] 

3520 for trace in processed_traces: 

3521 try: 

3522 ctrace = trace.chop( 

3523 tmin_-trace.deltat*4., tmax_+trace.deltat*4., 

3524 inplace=False) 

3525 

3526 except pyrocko.trace.NoData: 

3527 continue 

3528 

3529 if ctrace.data_len() < 2: 

3530 continue 

3531 

3532 chopped_traces.append(ctrace) 

3533 

3534 self.timer_cutout.stop() 

3535 return chopped_traces 

3536 

3537 def pre_process_hooks(self, traces): 

3538 for snuffling in self.snufflings: 

3539 if snuffling._pre_process_hook_enabled: 

3540 traces = snuffling.pre_process_hook(traces) 

3541 

3542 return traces 

3543 

3544 def post_process_hooks(self, traces): 

3545 for snuffling in self.snufflings: 

3546 if snuffling._post_process_hook_enabled: 

3547 traces = snuffling.post_process_hook(traces) 

3548 

3549 return traces 

3550 

3551 def visible_length_change(self, ignore=None): 

3552 for menuitem, vlen in self.menuitems_visible_length: 

3553 if menuitem.isChecked(): 

3554 self.visible_length = vlen 

3555 

3556 def scaling_base_change(self, ignore=None): 

3557 for menuitem, scaling_base in self.menuitems_scaling_base: 

3558 if menuitem.isChecked(): 

3559 self.scaling_base = scaling_base 

3560 

3561 def scalingmode_change(self, ignore=None): 

3562 for menuitem, scaling_key in self.menuitems_scaling: 

3563 if menuitem.isChecked(): 

3564 self.scaling_key = scaling_key 

3565 

3566 def apply_scaling_hooks(self, data_ranges): 

3567 for k in sorted(self.scaling_hooks.keys()): 

3568 hook = self.scaling_hooks[k] 

3569 hook(data_ranges) 

3570 

3571 def set_scaling_hook(self, k, hook): 

3572 self.scaling_hooks[k] = hook 

3573 

3574 def remove_scaling_hook(self, k): 

3575 del self.scaling_hooks[k] 

3576 

3577 def remove_scaling_hooks(self): 

3578 self.scaling_hooks = {} 

3579 

3580 def s_sortingmode_change(self, ignore=None): 

3581 for menuitem, valfunc in self.menuitems_ssorting: 

3582 if menuitem.isChecked(): 

3583 self._ssort = valfunc 

3584 

3585 self.sortingmode_change() 

3586 

3587 def sortingmode_change(self, ignore=None): 

3588 for menuitem, (gather, order, color) in self.menuitems_sorting: 

3589 if menuitem.isChecked(): 

3590 self.set_gathering(gather, order, color) 

3591 

3592 self.sortingmode_change_time = time.time() 

3593 

3594 def lowpass_change(self, value, ignore=None): 

3595 self.lowpass = value 

3596 self.passband_check() 

3597 self.tf_cache = {} 

3598 self.update() 

3599 

3600 def highpass_change(self, value, ignore=None): 

3601 self.highpass = value 

3602 self.passband_check() 

3603 self.tf_cache = {} 

3604 self.update() 

3605 

3606 def passband_check(self): 

3607 if self.highpass and self.lowpass \ 

3608 and self.highpass >= self.lowpass: 

3609 

3610 self.message = 'Corner frequency of highpass larger than ' \ 

3611 'corner frequency of lowpass! I will now ' \ 

3612 'deactivate the highpass.' 

3613 

3614 self.update_status() 

3615 else: 

3616 oldmess = self.message 

3617 self.message = None 

3618 if oldmess is not None: 

3619 self.update_status() 

3620 

3621 def gain_change(self, value, ignore): 

3622 self.gain = value 

3623 self.update() 

3624 

3625 def rot_change(self, value, ignore): 

3626 self.rotate = value 

3627 self.update() 

3628 

3629 def set_selected_markers(self, markers): 

3630 ''' 

3631 Set a list of markers selected 

3632 

3633 :param markers: list of markers 

3634 ''' 

3635 self.deselect_all() 

3636 for m in markers: 

3637 m.selected = True 

3638 

3639 self.update() 

3640 

3641 def deselect_all(self): 

3642 for marker in self.markers: 

3643 marker.selected = False 

3644 

3645 def animate_picking(self): 

3646 point = self.mapFromGlobal(qg.QCursor.pos()) 

3647 self.update_picking(point.x(), point.y(), doshift=True) 

3648 

3649 def get_nslc_ids_for_track(self, ftrack): 

3650 itrack = int(ftrack) 

3651 return self.track_to_nslc_ids.get(itrack, []) 

3652 

3653 def stop_picking(self, x, y, abort=False): 

3654 if self.picking: 

3655 self.update_picking(x, y, doshift=False) 

3656 self.picking = None 

3657 self.picking_down = None 

3658 self.picking_timer.stop() 

3659 self.picking_timer = None 

3660 if not abort: 

3661 self.add_marker(self.floating_marker) 

3662 self.floating_marker.selected = True 

3663 self.emit_selected_markers() 

3664 

3665 self.floating_marker = None 

3666 

3667 def start_picking(self, ignore): 

3668 

3669 if not self.picking: 

3670 self.deselect_all() 

3671 self.picking = qw.QRubberBand(qw.QRubberBand.Rectangle) 

3672 point = self.mapFromGlobal(qg.QCursor.pos()) 

3673 

3674 gpoint = self.mapToGlobal(qc.QPoint(point.x(), 0)) 

3675 self.picking.setGeometry( 

3676 gpoint.x(), gpoint.y(), 1, self.height()) 

3677 t = self.time_projection.rev(point.x()) 

3678 

3679 ftrack = self.track_to_screen.rev(point.y()) 

3680 nslc_ids = self.get_nslc_ids_for_track(ftrack) 

3681 self.floating_marker = Marker(nslc_ids, t, t) 

3682 self.floating_marker.selected = True 

3683 

3684 self.picking_timer = qc.QTimer() 

3685 self.picking_timer.timeout.connect( 

3686 self.animate_picking) 

3687 

3688 self.picking_timer.setInterval(50) 

3689 self.picking_timer.start() 

3690 

3691 def update_picking(self, x, y, doshift=False): 

3692 if self.picking: 

3693 mouset = self.time_projection.rev(x) 

3694 dt = 0.0 

3695 if mouset < self.tmin or mouset > self.tmax: 

3696 if mouset < self.tmin: 

3697 dt = -(self.tmin - mouset) 

3698 else: 

3699 dt = mouset - self.tmax 

3700 ddt = self.tmax-self.tmin 

3701 dt = max(dt, -ddt/10.) 

3702 dt = min(dt, ddt/10.) 

3703 

3704 x0 = x 

3705 if self.picking_down is not None: 

3706 x0 = self.time_projection(self.picking_down[0]) 

3707 

3708 w = abs(x-x0) 

3709 x0 = min(x0, x) 

3710 

3711 tmin, tmax = ( 

3712 self.time_projection.rev(x0), 

3713 self.time_projection.rev(x0+w)) 

3714 

3715 tmin, tmax = ( 

3716 max(working_system_time_range[0], tmin), 

3717 min(working_system_time_range[1], tmax)) 

3718 

3719 p1 = self.mapToGlobal(qc.QPoint(x0, 0)) 

3720 

3721 self.picking.setGeometry( 

3722 p1.x(), p1.y(), max(w, 1), self.height()) 

3723 

3724 ftrack = self.track_to_screen.rev(y) 

3725 nslc_ids = self.get_nslc_ids_for_track(ftrack) 

3726 self.floating_marker.set(nslc_ids, tmin, tmax) 

3727 

3728 if dt != 0.0 and doshift: 

3729 self.interrupt_following() 

3730 self.set_time_range(self.tmin+dt, self.tmax+dt) 

3731 

3732 self.update() 

3733 

3734 def update_status(self): 

3735 

3736 if self.message is None: 

3737 point = self.mapFromGlobal(qg.QCursor.pos()) 

3738 

3739 mouse_t = self.time_projection.rev(point.x()) 

3740 if not is_working_time(mouse_t): 

3741 return 

3742 

3743 if self.floating_marker: 

3744 tmi, tma = ( 

3745 self.floating_marker.tmin, 

3746 self.floating_marker.tmax) 

3747 

3748 tt, ms = gmtime_x(tmi) 

3749 

3750 if tmi == tma: 

3751 message = mystrftime( 

3752 fmt='Pick: %Y-%m-%d %H:%M:%S .%r', 

3753 tt=tt, milliseconds=ms) 

3754 else: 

3755 srange = '%g s' % (tma-tmi) 

3756 message = mystrftime( 

3757 fmt='Start: %Y-%m-%d %H:%M:%S .%r Length: '+srange, 

3758 tt=tt, milliseconds=ms) 

3759 else: 

3760 tt, ms = gmtime_x(mouse_t) 

3761 

3762 message = mystrftime(fmt=None, tt=tt, milliseconds=ms) 

3763 else: 

3764 message = self.message 

3765 

3766 sb = self.window().statusBar() 

3767 sb.clearMessage() 

3768 sb.showMessage(message) 

3769 

3770 def set_sortingmode_change_delay_time(self, dt): 

3771 self.sortingmode_change_delay_time = dt 

3772 

3773 def sortingmode_change_delayed(self): 

3774 now = time.time() 

3775 return ( 

3776 self.sortingmode_change_delay_time is not None 

3777 and now - self.sortingmode_change_time 

3778 < self.sortingmode_change_delay_time) 

3779 

3780 def set_visible_marker_kinds(self, kinds): 

3781 self.deselect_all() 

3782 self.visible_marker_kinds = tuple(kinds) 

3783 self.emit_selected_markers() 

3784 

3785 def following(self): 

3786 return self.follow_timer is not None \ 

3787 and not self.following_interrupted() 

3788 

3789 def interrupt_following(self): 

3790 self.interactive_range_change_time = time.time() 

3791 

3792 def following_interrupted(self, now=None): 

3793 if now is None: 

3794 now = time.time() 

3795 return now - self.interactive_range_change_time \ 

3796 < self.interactive_range_change_delay_time 

3797 

3798 def follow(self, tlen, interval=50, lapse=None, tmax_start=None): 

3799 if tmax_start is None: 

3800 tmax_start = time.time() 

3801 self.show_all = False 

3802 self.follow_time = tlen 

3803 self.follow_timer = qc.QTimer(self) 

3804 self.follow_timer.timeout.connect( 

3805 self.follow_update) 

3806 self.follow_timer.setInterval(interval) 

3807 self.follow_timer.start() 

3808 self.follow_started = time.time() 

3809 self.follow_lapse = lapse 

3810 self.follow_tshift = self.follow_started - tmax_start 

3811 self.interactive_range_change_time = 0.0 

3812 

3813 def unfollow(self): 

3814 if self.follow_timer is not None: 

3815 self.follow_timer.stop() 

3816 self.follow_timer = None 

3817 self.interactive_range_change_time = 0.0 

3818 

3819 def follow_update(self): 

3820 rnow = time.time() 

3821 if self.follow_lapse is None: 

3822 now = rnow 

3823 else: 

3824 now = self.follow_started + (rnow - self.follow_started) \ 

3825 * self.follow_lapse 

3826 

3827 if self.following_interrupted(rnow): 

3828 return 

3829 self.set_time_range( 

3830 now-self.follow_time-self.follow_tshift, 

3831 now-self.follow_tshift) 

3832 

3833 self.update() 

3834 

3835 def myclose(self, return_tag=''): 

3836 self.return_tag = return_tag 

3837 self.window().close() 

3838 

3839 def cleanup(self): 

3840 self.about_to_close.emit() 

3841 self.timer.stop() 

3842 if self.follow_timer is not None: 

3843 self.follow_timer.stop() 

3844 

3845 for snuffling in list(self.snufflings): 

3846 self.remove_snuffling(snuffling) 

3847 

3848 def set_error_message(self, key, value): 

3849 if value is None: 

3850 if key in self.error_messages: 

3851 del self.error_messages[key] 

3852 else: 

3853 self.error_messages[key] = value 

3854 

3855 def inputline_changed(self, text): 

3856 pass 

3857 

3858 def inputline_finished(self, text): 

3859 line = str(text) 

3860 

3861 toks = line.split() 

3862 clearit, hideit, error = False, True, None 

3863 if len(toks) >= 1: 

3864 command = toks[0].lower() 

3865 

3866 try: 

3867 quick_filter_commands = { 

3868 'n': '%s.*.*.*', 

3869 's': '*.%s.*.*', 

3870 'l': '*.*.%s.*', 

3871 'c': '*.*.*.%s'} 

3872 

3873 if command in quick_filter_commands: 

3874 if len(toks) >= 2: 

3875 patterns = [ 

3876 quick_filter_commands[toks[0]] % pat 

3877 for pat in toks[1:]] 

3878 self.set_quick_filter_patterns(patterns, line) 

3879 else: 

3880 self.set_quick_filter_patterns(None) 

3881 

3882 self.update() 

3883 

3884 elif command in ('hide', 'unhide'): 

3885 if len(toks) >= 2: 

3886 patterns = [] 

3887 if len(toks) == 2: 

3888 patterns = [toks[1]] 

3889 elif len(toks) >= 3: 

3890 x = { 

3891 'n': '%s.*.*.*', 

3892 's': '*.%s.*.*', 

3893 'l': '*.*.%s.*', 

3894 'c': '*.*.*.%s'} 

3895 

3896 if toks[1] in x: 

3897 patterns.extend( 

3898 x[toks[1]] % tok for tok in toks[2:]) 

3899 

3900 for pattern in patterns: 

3901 if command == 'hide': 

3902 self.add_blacklist_pattern(pattern) 

3903 else: 

3904 self.remove_blacklist_pattern(pattern) 

3905 

3906 elif command == 'unhide' and len(toks) == 1: 

3907 self.clear_blacklist() 

3908 

3909 clearit = True 

3910 

3911 self.update() 

3912 

3913 elif command == 'markers': 

3914 if len(toks) == 2: 

3915 if toks[1] == 'all': 

3916 kinds = self.all_marker_kinds 

3917 else: 

3918 kinds = [] 

3919 for x in toks[1]: 

3920 try: 

3921 kinds.append(int(x)) 

3922 except Exception: 

3923 pass 

3924 

3925 self.set_visible_marker_kinds(kinds) 

3926 

3927 elif len(toks) == 1: 

3928 self.set_visible_marker_kinds(()) 

3929 

3930 self.update() 

3931 

3932 elif command == 'scaling': 

3933 if len(toks) == 2: 

3934 hideit = False 

3935 error = 'wrong number of arguments' 

3936 

3937 if len(toks) >= 3: 

3938 vmin, vmax = [ 

3939 pyrocko.model.float_or_none(x) 

3940 for x in toks[-2:]] 

3941 

3942 def upd(d, k, vmin, vmax): 

3943 if k in d: 

3944 if vmin is not None: 

3945 d[k] = vmin, d[k][1] 

3946 if vmax is not None: 

3947 d[k] = d[k][0], vmax 

3948 

3949 if len(toks) == 1: 

3950 self.remove_scaling_hooks() 

3951 

3952 elif len(toks) == 3: 

3953 def hook(data_ranges): 

3954 for k in data_ranges: 

3955 upd(data_ranges, k, vmin, vmax) 

3956 

3957 self.set_scaling_hook('_', hook) 

3958 

3959 elif len(toks) == 4: 

3960 pattern = toks[1] 

3961 

3962 def hook(data_ranges): 

3963 for k in pyrocko.util.match_nslcs( 

3964 pattern, list(data_ranges.keys())): 

3965 

3966 upd(data_ranges, k, vmin, vmax) 

3967 

3968 self.set_scaling_hook(pattern, hook) 

3969 

3970 elif command == 'goto': 

3971 toks2 = line.split(None, 1) 

3972 if len(toks2) == 2: 

3973 arg = toks2[1] 

3974 m = re.match( 

3975 r'^\d\d\d\d(-\d\d(-\d\d( \d\d(:\d\d' 

3976 r'(:\d\d(\.\d+)?)?)?)?)?)?$', arg) 

3977 if m: 

3978 tlen = None 

3979 if not m.group(1): 

3980 tlen = 12*32*24*60*60 

3981 elif not m.group(2): 

3982 tlen = 32*24*60*60 

3983 elif not m.group(3): 

3984 tlen = 24*60*60 

3985 elif not m.group(4): 

3986 tlen = 60*60 

3987 elif not m.group(5): 

3988 tlen = 60 

3989 

3990 supl = '1970-01-01 00:00:00' 

3991 if len(supl) > len(arg): 

3992 arg = arg + supl[-(len(supl)-len(arg)):] 

3993 t = pyrocko.util.str_to_time(arg) 

3994 self.go_to_time(t, tlen=tlen) 

3995 

3996 elif re.match(r'^\d\d:\d\d(:\d\d(\.\d+)?)?$', arg): 

3997 supl = '00:00:00' 

3998 if len(supl) > len(arg): 

3999 arg = arg + supl[-(len(supl)-len(arg)):] 

4000 tmin, tmax = self.get_time_range() 

4001 sdate = pyrocko.util.time_to_str( 

4002 tmin/2.+tmax/2., format='%Y-%m-%d') 

4003 t = pyrocko.util.str_to_time(sdate + ' ' + arg) 

4004 self.go_to_time(t) 

4005 

4006 else: 

4007 self.go_to_event_by_name(arg) 

4008 

4009 else: 

4010 raise PileViewerMainException( 

4011 'No such command: %s' % command) 

4012 

4013 except PileViewerMainException as e: 

4014 error = str(e) 

4015 hideit = False 

4016 

4017 return clearit, hideit, error 

4018 

4019 return PileViewerMain 

4020 

4021 

4022PileViewerMain = MakePileViewerMainClass(qw.QWidget) 

4023GLPileViewerMain = MakePileViewerMainClass(qgl.QGLWidget) 

4024 

4025 

4026class LineEditWithAbort(qw.QLineEdit): 

4027 

4028 aborted = qc.pyqtSignal() 

4029 history_down = qc.pyqtSignal() 

4030 history_up = qc.pyqtSignal() 

4031 

4032 def keyPressEvent(self, key_event): 

4033 if key_event.key() == qc.Qt.Key_Escape: 

4034 self.aborted.emit() 

4035 elif key_event.key() == qc.Qt.Key_Down: 

4036 self.history_down.emit() 

4037 elif key_event.key() == qc.Qt.Key_Up: 

4038 self.history_up.emit() 

4039 else: 

4040 return qw.QLineEdit.keyPressEvent(self, key_event) 

4041 

4042 

4043class PileViewer(qw.QFrame): 

4044 ''' 

4045 PileViewerMain + Controls + Inputline 

4046 ''' 

4047 

4048 def __init__( 

4049 self, pile, 

4050 ntracks_shown_max=20, 

4051 marker_editor_sortable=True, 

4052 use_opengl=False, 

4053 panel_parent=None, 

4054 *args): 

4055 

4056 qw.QFrame.__init__(self, *args) 

4057 

4058 if use_opengl: 

4059 self.viewer = GLPileViewerMain( 

4060 pile, 

4061 ntracks_shown_max=ntracks_shown_max, 

4062 panel_parent=panel_parent) 

4063 else: 

4064 self.viewer = PileViewerMain( 

4065 pile, 

4066 ntracks_shown_max=ntracks_shown_max, 

4067 panel_parent=panel_parent) 

4068 

4069 self.marker_editor_sortable = marker_editor_sortable 

4070 

4071 layout = qw.QGridLayout() 

4072 self.setLayout(layout) 

4073 layout.setContentsMargins(0, 0, 0, 0) 

4074 layout.setSpacing(0) 

4075 

4076 self.setFrameShape(qw.QFrame.StyledPanel) 

4077 self.setFrameShadow(qw.QFrame.Sunken) 

4078 

4079 self.input_area = qw.QFrame(self) 

4080 ia_layout = qw.QGridLayout() 

4081 ia_layout.setContentsMargins(11, 11, 11, 11) 

4082 self.input_area.setLayout(ia_layout) 

4083 

4084 self.inputline = LineEditWithAbort(self.input_area) 

4085 self.inputline.returnPressed.connect( 

4086 self.inputline_returnpressed) 

4087 self.inputline.editingFinished.connect( 

4088 self.inputline_finished) 

4089 self.inputline.aborted.connect( 

4090 self.inputline_aborted) 

4091 

4092 self.inputline.history_down.connect( 

4093 lambda: self.step_through_history(1)) 

4094 self.inputline.history_up.connect( 

4095 lambda: self.step_through_history(-1)) 

4096 

4097 self.inputline.textEdited.connect( 

4098 self.inputline_changed) 

4099 

4100 self.inputline.setFocusPolicy(qc.Qt.ClickFocus) 

4101 self.input_area.hide() 

4102 self.history = None 

4103 

4104 self.inputline_error_str = None 

4105 

4106 self.inputline_error = qw.QLabel() 

4107 self.inputline_error.hide() 

4108 

4109 ia_layout.addWidget(self.inputline, 0, 0) 

4110 ia_layout.addWidget(self.inputline_error, 1, 0) 

4111 layout.addWidget(self.input_area, 0, 0, 1, 2) 

4112 layout.addWidget(self.viewer, 1, 0) 

4113 

4114 pb = Progressbars(self) 

4115 layout.addWidget(pb, 2, 0, 1, 2) 

4116 self.progressbars = pb 

4117 

4118 scrollbar = qw.QScrollBar(qc.Qt.Vertical) 

4119 self.scrollbar = scrollbar 

4120 layout.addWidget(scrollbar, 1, 1) 

4121 self.scrollbar.valueChanged.connect( 

4122 self.scrollbar_changed) 

4123 

4124 self.block_scrollbar_changes = False 

4125 

4126 self.viewer.want_input.connect( 

4127 self.inputline_show) 

4128 self.viewer.tracks_range_changed.connect( 

4129 self.tracks_range_changed) 

4130 self.viewer.pile_has_changed_signal.connect( 

4131 self.adjust_controls) 

4132 self.viewer.about_to_close.connect( 

4133 self.save_inputline_history) 

4134 

4135 def cleanup(self): 

4136 self.viewer.cleanup() 

4137 

4138 def get_progressbars(self): 

4139 return self.progressbars 

4140 

4141 def inputline_show(self): 

4142 if not self.history: 

4143 self.load_inputline_history() 

4144 

4145 self.input_area.show() 

4146 self.inputline.setFocus(qc.Qt.OtherFocusReason) 

4147 self.inputline.selectAll() 

4148 

4149 def inputline_set_error(self, string): 

4150 self.inputline_error_str = string 

4151 self.inputline.setPalette(pyrocko.gui.util.get_err_palette()) 

4152 self.inputline.selectAll() 

4153 self.inputline_error.setText(string) 

4154 self.input_area.show() 

4155 self.inputline_error.show() 

4156 

4157 def inputline_clear_error(self): 

4158 if self.inputline_error_str: 

4159 self.inputline.setPalette(qw.QApplication.palette()) 

4160 self.inputline_error_str = None 

4161 self.inputline_error.clear() 

4162 self.inputline_error.hide() 

4163 

4164 def inputline_changed(self, line): 

4165 self.viewer.inputline_changed(str(line)) 

4166 self.inputline_clear_error() 

4167 

4168 def inputline_returnpressed(self): 

4169 line = str(self.inputline.text()) 

4170 clearit, hideit, error = self.viewer.inputline_finished(line) 

4171 

4172 if error: 

4173 self.inputline_set_error(error) 

4174 

4175 line = line.strip() 

4176 

4177 if line != '' and not error: 

4178 if not (len(self.history) >= 1 and line == self.history[-1]): 

4179 self.history.append(line) 

4180 

4181 if clearit: 

4182 

4183 self.inputline.blockSignals(True) 

4184 qpat, qinp = self.viewer.get_quick_filter_patterns() 

4185 if qpat is None: 

4186 self.inputline.clear() 

4187 else: 

4188 self.inputline.setText(qinp) 

4189 self.inputline.blockSignals(False) 

4190 

4191 if hideit and not error: 

4192 self.viewer.setFocus(qc.Qt.OtherFocusReason) 

4193 self.input_area.hide() 

4194 

4195 self.hist_ind = len(self.history) 

4196 

4197 def inputline_aborted(self): 

4198 ''' 

4199 Hide the input line. 

4200 ''' 

4201 self.viewer.setFocus(qc.Qt.OtherFocusReason) 

4202 self.hist_ind = len(self.history) 

4203 self.input_area.hide() 

4204 

4205 def save_inputline_history(self): 

4206 ''' 

4207 Save input line history to "$HOME/.pyrocko/.snuffler_history.pf" 

4208 ''' 

4209 if not self.history: 

4210 return 

4211 

4212 conf = pyrocko.config 

4213 fn_hist = conf.expand(conf.make_conf_path_tmpl('.snuffler_history')) 

4214 with open(fn_hist, 'w') as f: 

4215 i = min(100, len(self.history)) 

4216 for c in self.history[-i:]: 

4217 f.write('%s\n' % c) 

4218 

4219 def load_inputline_history(self): 

4220 ''' 

4221 Load input line history from "$HOME/.pyrocko/.snuffler_history.pf" 

4222 ''' 

4223 conf = pyrocko.config 

4224 fn_hist = conf.expand(conf.make_conf_path_tmpl('.snuffler_history')) 

4225 if not os.path.exists(fn_hist): 

4226 with open(fn_hist, 'w+') as f: 

4227 f.write('\n') 

4228 

4229 with open(fn_hist, 'r') as f: 

4230 self.history = [line.strip() for line in f.readlines()] 

4231 

4232 self.hist_ind = len(self.history) 

4233 

4234 def step_through_history(self, ud=1): 

4235 ''' 

4236 Step through input line history and set the input line text. 

4237 ''' 

4238 n = len(self.history) 

4239 self.hist_ind += ud 

4240 self.hist_ind %= (n + 1) 

4241 if len(self.history) != 0 and self.hist_ind != n: 

4242 self.inputline.setText(self.history[self.hist_ind]) 

4243 else: 

4244 self.inputline.setText('') 

4245 

4246 def inputline_finished(self): 

4247 pass 

4248 

4249 def tracks_range_changed(self, ntracks, ilo, ihi): 

4250 if self.block_scrollbar_changes: 

4251 return 

4252 

4253 self.scrollbar.blockSignals(True) 

4254 self.scrollbar.setPageStep(ihi-ilo) 

4255 vmax = max(0, ntracks-(ihi-ilo)) 

4256 self.scrollbar.setRange(0, vmax) 

4257 self.scrollbar.setValue(ilo) 

4258 self.scrollbar.setHidden(vmax == 0) 

4259 self.scrollbar.blockSignals(False) 

4260 

4261 def scrollbar_changed(self, value): 

4262 self.block_scrollbar_changes = True 

4263 ilo = value 

4264 ihi = ilo + self.scrollbar.pageStep() 

4265 self.viewer.set_tracks_range((ilo, ihi)) 

4266 self.block_scrollbar_changes = False 

4267 self.update_contents() 

4268 

4269 def controls(self): 

4270 frame = qw.QFrame(self) 

4271 layout = qw.QGridLayout() 

4272 frame.setLayout(layout) 

4273 

4274 minfreq = 0.001 

4275 maxfreq = 1000.0 

4276 self.lowpass_control = ValControl(high_is_none=True) 

4277 self.lowpass_control.setup( 

4278 'Lowpass [Hz]:', minfreq, maxfreq, maxfreq, 0) 

4279 self.highpass_control = ValControl(low_is_none=True) 

4280 self.highpass_control.setup( 

4281 'Highpass [Hz]:', minfreq, maxfreq, minfreq, 1) 

4282 self.gain_control = ValControl() 

4283 self.gain_control.setup('Gain:', 0.001, 1000., 1., 2) 

4284 self.rot_control = LinValControl() 

4285 self.rot_control.setup('Rotate [deg]:', -180., 180., 0., 3) 

4286 

4287 self.lowpass_control.valchange.connect( 

4288 self.viewer.lowpass_change) 

4289 self.highpass_control.valchange.connect( 

4290 self.viewer.highpass_change) 

4291 self.gain_control.valchange.connect( 

4292 self.viewer.gain_change) 

4293 self.rot_control.valchange.connect( 

4294 self.viewer.rot_change) 

4295 

4296 for icontrol, control in enumerate(( 

4297 self.highpass_control, 

4298 self.lowpass_control, 

4299 self.gain_control, 

4300 self.rot_control)): 

4301 

4302 for iwidget, widget in enumerate(control.widgets()): 

4303 layout.addWidget(widget, icontrol, iwidget) 

4304 

4305 spacer = qw.QSpacerItem( 

4306 0, 0, qw.QSizePolicy.Expanding, qw.QSizePolicy.Expanding) 

4307 layout.addItem(spacer, 4, 0, 1, 3) 

4308 

4309 self.adjust_controls() 

4310 return frame 

4311 

4312 def marker_editor(self): 

4313 editor = pyrocko.gui.marker_editor.MarkerEditor( 

4314 self, sortable=self.marker_editor_sortable) 

4315 

4316 editor.set_viewer(self.get_view()) 

4317 editor.get_marker_model().dataChanged.connect( 

4318 self.update_contents) 

4319 return editor 

4320 

4321 def adjust_controls(self): 

4322 dtmin, dtmax = self.viewer.content_deltat_range() 

4323 maxfreq = 0.5/dtmin 

4324 minfreq = (0.5/dtmax)*0.001 

4325 self.lowpass_control.set_range(minfreq, maxfreq) 

4326 self.highpass_control.set_range(minfreq, maxfreq) 

4327 

4328 def setup_snufflings(self): 

4329 self.viewer.setup_snufflings() 

4330 

4331 def get_view(self): 

4332 return self.viewer 

4333 

4334 def update_contents(self): 

4335 self.viewer.update() 

4336 

4337 def get_pile(self): 

4338 return self.viewer.get_pile()