Mercurial > repos > rv43 > tomo
comparison general.py @ 63:98a83f03d91b draft
"planemo upload for repository https://github.com/rolfverberg/galaxytools commit a9ebbfca439737ae17ddcb0df84408155f964877"
author | rv43 |
---|---|
date | Thu, 18 Aug 2022 14:36:46 +0000 |
parents | ca61007a60fa |
children | f31ef7bfb430 |
comparison
equal
deleted
inserted
replaced
62:e544e5d110eb | 63:98a83f03d91b |
---|---|
19 pass | 19 pass |
20 import numpy as np | 20 import numpy as np |
21 try: | 21 try: |
22 import matplotlib.pyplot as plt | 22 import matplotlib.pyplot as plt |
23 from matplotlib.widgets import Button | 23 from matplotlib.widgets import Button |
24 except: | |
25 pass | |
26 try: | |
27 import pyinputplus as pyip | |
24 except: | 28 except: |
25 pass | 29 pass |
26 | 30 |
27 from ast import literal_eval | 31 from ast import literal_eval |
28 from copy import deepcopy | 32 from copy import deepcopy |
286 if mo is None: | 290 if mo is None: |
287 return None | 291 return None |
288 else: | 292 else: |
289 return int(mo.group()) | 293 return int(mo.group()) |
290 | 294 |
291 def input_int(s=None, v_min=None, v_max=None, default=None, inset=None): | 295 def input_int(s=None, v_min=None, v_max=None, default=None): |
292 if default is not None: | 296 if default is not None: |
293 if not isinstance(default, int): | 297 if not isinstance(default, int): |
294 illegal_value(default, 'default', 'input_int') | 298 illegal_value(default, 'default', 'input_int') |
295 return None | 299 return None |
296 default_string = f' [{default}]' | 300 default_string = f' [{default}]' |
297 else: | 301 else: |
298 default_string = '' | 302 default_string = '' |
299 if v_min is not None: | 303 if v_min is not None: |
300 if not isinstance(v_min, int): | 304 if not isinstance(v_min, int): |
301 illegal_value(v_min, 'v_min', 'input_int') | 305 illegal_value(vmin, 'vmin', 'input_int') |
302 return None | 306 return None |
303 if default is not None and default < v_min: | 307 if default is not None and default < v_min: |
304 logging.error('Illegal v_min, default combination ({v_min}, {default})') | 308 logging.error('Illegal v_min, default combination ({v_min}, {default})') |
305 return None | 309 return None |
306 if v_max is not None: | 310 if v_max is not None: |
307 if not isinstance(v_max, int): | 311 if not isinstance(v_max, int): |
308 illegal_value(v_max, 'v_max', 'input_int') | 312 illegal_value(vmax, 'vmax', 'input_int') |
309 return None | 313 return None |
310 if v_min is not None and v_min > v_max: | 314 if v_min is not None and v_min > v_max: |
311 logging.error(f'Illegal v_min, v_max combination ({v_min}, {v_max})') | 315 logging.error(f'Illegal v_min, v_max combination ({v_min}, {v_max})') |
312 return None | 316 return None |
313 if default is not None and default > v_max: | 317 if default is not None and default > v_max: |
314 logging.error('Illegal default, v_max combination ({default}, {v_max})') | 318 logging.error('Illegal default, v_max combination ({default}, {v_max})') |
315 return None | 319 return None |
316 if inset is not None: | |
317 if (not isinstance(inset, (tuple, list)) or False in [True if isinstance(i, int) else | |
318 False for i in inset]): | |
319 illegal_value(inset, 'inset', 'input_int') | |
320 return None | |
321 if v_min is not None and v_max is not None: | 320 if v_min is not None and v_max is not None: |
322 v_range = f' ({v_min}, {v_max})' | 321 v_range = f' (in range [{v_min}, {v_max}])' |
323 elif v_min is not None: | 322 elif v_min is not None: |
324 v_range = f' (>= {v_min})' | 323 v_range = f' (>= {v_min})' |
325 elif v_max is not None: | 324 elif v_max is not None: |
326 v_range = f' (<= {v_max})' | 325 v_range = f' (<= {v_max})' |
327 else: | 326 else: |
332 print(f'{s}{v_range}{default_string}: ') | 331 print(f'{s}{v_range}{default_string}: ') |
333 try: | 332 try: |
334 i = input() | 333 i = input() |
335 if isinstance(i, str) and not len(i): | 334 if isinstance(i, str) and not len(i): |
336 v = default | 335 v = default |
337 print(f'{v}') | |
338 else: | 336 else: |
339 v = literal_eval(i) | 337 v = literal_eval(i) |
340 if inset and v not in inset: | |
341 raise ValueError(f'{v} not part of the set {inset}') | |
342 except (ValueError, TypeError, SyntaxError, MemoryError, RecursionError): | 338 except (ValueError, TypeError, SyntaxError, MemoryError, RecursionError): |
343 v = None | 339 v = None |
344 except: | 340 except: |
345 print('Unexpected error') | 341 print('Unexpected error') |
346 raise | 342 raise |
373 return None | 369 return None |
374 if default is not None and default > v_max: | 370 if default is not None and default > v_max: |
375 logging.error('Illegal default, v_max combination ({default}, {v_max})') | 371 logging.error('Illegal default, v_max combination ({default}, {v_max})') |
376 return None | 372 return None |
377 if v_min is not None and v_max is not None: | 373 if v_min is not None and v_max is not None: |
378 v_range = f' ({v_min}, {v_max})' | 374 v_range = f' (in range [{v_min}, {v_max}])' |
379 elif v_min is not None: | 375 elif v_min is not None: |
380 v_range = f' (>= {v_min})' | 376 v_range = f' (>= {v_min})' |
381 elif v_max is not None: | 377 elif v_max is not None: |
382 v_range = f' (<= {v_max})' | 378 v_range = f' (<= {v_max})' |
383 else: | 379 else: |
388 print(f'{s}{v_range}{default_string}: ') | 384 print(f'{s}{v_range}{default_string}: ') |
389 try: | 385 try: |
390 i = input() | 386 i = input() |
391 if isinstance(i, str) and not len(i): | 387 if isinstance(i, str) and not len(i): |
392 v = default | 388 v = default |
393 print(f'{v}') | |
394 else: | 389 else: |
395 v = literal_eval(i) | 390 v = literal_eval(i) |
396 except (ValueError, TypeError, SyntaxError, MemoryError, RecursionError): | 391 except (ValueError, TypeError, SyntaxError, MemoryError, RecursionError): |
397 v = None | 392 v = None |
398 except: | 393 except: |
413 return None | 408 return None |
414 if v_max < v_min: | 409 if v_max < v_min: |
415 logging.error(f'Illegal v_min, v_max combination ({v_min}, {v_max})') | 410 logging.error(f'Illegal v_min, v_max combination ({v_min}, {v_max})') |
416 return None | 411 return None |
417 if v_min is not None and v_max is not None: | 412 if v_min is not None and v_max is not None: |
418 v_range = f' (each value in ({v_min}, {v_max}))' | 413 v_range = f' (each value in range [{v_min}, {v_max}])' |
419 elif v_min is not None: | 414 elif v_min is not None: |
420 v_range = f' (each value >= {v_min})' | 415 v_range = f' (each value >= {v_min})' |
421 elif v_max is not None: | 416 elif v_max is not None: |
422 v_range = f' (each value <= {v_max})' | 417 v_range = f' (each value <= {v_max})' |
423 else: | 418 else: |
460 else: | 455 else: |
461 print(f'{s}{default_string}: ') | 456 print(f'{s}{default_string}: ') |
462 i = input() | 457 i = input() |
463 if isinstance(i, str) and not len(i): | 458 if isinstance(i, str) and not len(i): |
464 i = default | 459 i = default |
465 print(f'{i}') | 460 if i.lower() in 'yes': |
466 if i is not None and i.lower() in 'yes': | |
467 v = True | 461 v = True |
468 elif i is not None and i.lower() in 'no': | 462 elif i.lower() in 'no': |
469 v = False | 463 v = False |
470 else: | 464 else: |
471 print('Illegal input, enter yes or no') | 465 print('Illegal input, enter yes or no') |
472 v = input_yesno(s, default) | 466 v = input_yesno(s, default) |
473 return v | 467 return v |
474 | |
475 def input_menu(items, default=None, header=None): | |
476 if not isinstance(items, (tuple, list)) or False in [True if isinstance(i, str) else False | |
477 for i in items]: | |
478 illegal_value(items, 'items', 'input_menu') | |
479 return None | |
480 if default is not None: | |
481 if not (isinstance(default, str) and default in items): | |
482 logging.error(f'Illegal value for default ({default}), must be in {items}') | |
483 return None | |
484 default_string = f' [{items.index(default)+1}]' | |
485 else: | |
486 default_string = '' | |
487 if header is None: | |
488 print(f'Choose one of the following items (1, {len(items)}){default_string}:') | |
489 else: | |
490 print(f'{header} (1, {len(items)}){default_string}:') | |
491 for i, choice in enumerate(items): | |
492 print(f' {i+1}: {choice}') | |
493 try: | |
494 choice = input() | |
495 if isinstance(choice, str) and not len(choice): | |
496 choice = items.index(default) | |
497 print(f'{choice+1}') | |
498 else: | |
499 choice = literal_eval(choice) | |
500 if isinstance(choice, int) and 1 <= choice <= len(items): | |
501 choice -= 1 | |
502 else: | |
503 raise ValueError | |
504 except (ValueError, TypeError, SyntaxError, MemoryError, RecursionError): | |
505 choice = None | |
506 except: | |
507 print('Unexpected error') | |
508 raise | |
509 if choice is None: | |
510 print(f'Illegal choice, enter a number between 1 and {len(items)}') | |
511 choice = input_menu(items, default) | |
512 return choice | |
513 | 468 |
514 def create_mask(x, bounds=None, reverse_mask=False, current_mask=None): | 469 def create_mask(x, bounds=None, reverse_mask=False, current_mask=None): |
515 # bounds is a pair of number in the same units a x | 470 # bounds is a pair of number in the same units a x |
516 if not isinstance(x, (tuple, list, np.ndarray)) or not len(x): | 471 if not isinstance(x, (tuple, list, np.ndarray)) or not len(x): |
517 logging.warning(f'Illegal input array ({x}, {type(x)})') | 472 logging.warning(f'Illegal input array ({x}, {type(x)})') |
535 if not True in mask: | 490 if not True in mask: |
536 logging.warning('Entire data array is masked') | 491 logging.warning('Entire data array is masked') |
537 return mask | 492 return mask |
538 | 493 |
539 def draw_mask_1d(ydata, xdata=None, current_index_ranges=None, current_mask=None, | 494 def draw_mask_1d(ydata, xdata=None, current_index_ranges=None, current_mask=None, |
540 select_mask=True, num_index_ranges_max=None, title=None, legend=None, test_mode=False): | 495 select_mask=True): |
541 def draw_selections(ax): | 496 def draw_selections(ax): |
542 ax.clear() | 497 ax.clear() |
543 ax.set_title(title) | 498 ax.set_title(axes_title) |
544 ax.legend([legend]) | |
545 ax.plot(xdata, ydata, 'k') | 499 ax.plot(xdata, ydata, 'k') |
546 for (low, upp) in current_include: | 500 for (low, upp) in current_include: |
547 xlow = 0.5*(xdata[max(0, low-1)]+xdata[low]) | 501 xlow = 0.5*(xdata[max(0, low-1)]+xdata[low]) |
548 xupp = 0.5*(xdata[upp]+xdata[min(num_data-1, upp+1)]) | 502 xupp = 0.5*(xdata[upp]+xdata[min(num_data-1, upp+1)]) |
549 ax.axvspan(xlow, xupp, facecolor='green', alpha=0.5) | 503 ax.axvspan(xlow, xupp, facecolor='green', alpha=0.5) |
607 | 561 |
608 # Check for valid inputs | 562 # Check for valid inputs |
609 ydata = np.asarray(ydata) | 563 ydata = np.asarray(ydata) |
610 if ydata.ndim > 1: | 564 if ydata.ndim > 1: |
611 logging.warning(f'Illegal ydata dimension ({ydata.ndim})') | 565 logging.warning(f'Illegal ydata dimension ({ydata.ndim})') |
612 return None, None | 566 return None |
613 num_data = ydata.size | 567 num_data = ydata.size |
614 if xdata is None: | 568 if xdata is None: |
615 xdata = np.arange(num_data) | 569 xdata = np.arange(num_data) |
616 else: | 570 else: |
617 xdata = np.asarray(xdata, dtype=np.float64) | 571 xdata = np.asarray(xdata, dtype=np.float64) |
618 if xdata.ndim > 1 or xdata.size != num_data: | 572 if xdata.ndim > 1 or xdata.size != num_data: |
619 logging.warning(f'Illegal xdata shape ({xdata.shape})') | 573 logging.warning(f'Illegal xdata shape ({xdata.shape})') |
620 return None, None | 574 return None |
621 if not np.all(xdata[:-1] < xdata[1:]): | 575 if not np.all(xdata[:-1] < xdata[1:]): |
622 logging.warning('Illegal xdata: must be monotonically increasing') | 576 logging.warning('Illegal xdata: must be monotonically increasing') |
623 return None, None | 577 return None |
624 if current_index_ranges is not None: | 578 if current_index_ranges is not None: |
625 if not isinstance(current_index_ranges, (tuple, list)): | 579 if not isinstance(current_index_ranges, (tuple, list)): |
626 logging.warning('Illegal current_index_ranges parameter ({current_index_ranges}, '+ | 580 logging.warning('Illegal current_index_ranges parameter ({current_index_ranges}, '+ |
627 f'{type(current_index_ranges)})') | 581 f'{type(current_index_ranges)})') |
628 return None, None | 582 return None |
629 if not isinstance(select_mask, bool): | 583 if not isinstance(select_mask, bool): |
630 logging.warning('Illegal select_mask parameter ({select_mask}, {type(select_mask)})') | 584 logging.warning('Illegal select_mask parameter ({select_mask}, {type(select_mask)})') |
631 return None, None | 585 return None |
632 if num_index_ranges_max is not None: | |
633 logging.warning('num_index_ranges_max input not yet implemented in draw_mask_1d') | |
634 if title is None: | |
635 title = 'select ranges of data' | |
636 elif not isinstance(title, str): | |
637 illegal(title, 'title') | |
638 title = '' | |
639 if legend is None and not isinstance(title, str): | |
640 illegal(legend, 'legend') | |
641 legend = None | |
642 | 586 |
643 if select_mask: | 587 if select_mask: |
644 title = f'Click and drag to {title} you wish to include' | 588 axes_title = 'Click and drag to select ranges of data you wish to include.' |
645 selection_color = 'green' | 589 selection_color = 'green' |
646 else: | 590 else: |
647 title = f'Click and drag to {title} you wish to exclude' | 591 axes_title = 'Click and drag to select ranges of data you wish to exclude.' |
648 selection_color = 'red' | 592 selection_color = 'red' |
649 | 593 |
650 # Set initial selected mask and the selected/unselected index ranges as needed | 594 # Set initial selected mask and the selected/unselected index ranges as needed |
651 selected_index_ranges = [] | 595 selected_index_ranges = [] |
652 unselected_index_ranges = [] | 596 unselected_index_ranges = [] |
689 for i in range(1, len(current_include)): | 633 for i in range(1, len(current_include)): |
690 current_exclude.append((current_include[i-1][1]+1, current_include[i][0]-1)) | 634 current_exclude.append((current_include[i-1][1]+1, current_include[i][0]-1)) |
691 if current_include[-1][1] < num_data-1: | 635 if current_include[-1][1] < num_data-1: |
692 current_exclude.append((current_include[-1][1]+1, num_data-1)) | 636 current_exclude.append((current_include[-1][1]+1, num_data-1)) |
693 | 637 |
694 if not test_mode: | 638 # Set up matplotlib figure |
695 | 639 fig, ax = plt.subplots() |
696 # Set up matplotlib figure | 640 plt.subplots_adjust(bottom=0.2) |
697 plt.close('all') | 641 draw_selections(ax) |
698 fig, ax = plt.subplots() | 642 |
699 plt.subplots_adjust(bottom=0.2) | 643 # Set up event handling for click-and-drag range selection |
700 draw_selections(ax) | 644 cid_click = fig.canvas.mpl_connect('button_press_event', onclick) |
701 | 645 cid_release = fig.canvas.mpl_connect('button_release_event', onrelease) |
702 # Set up event handling for click-and-drag range selection | 646 |
703 cid_click = fig.canvas.mpl_connect('button_press_event', onclick) | 647 # Set up confirm / clear range selection buttons |
704 cid_release = fig.canvas.mpl_connect('button_release_event', onrelease) | 648 confirm_b = Button(plt.axes([0.75, 0.05, 0.15, 0.075]), 'Confirm') |
705 | 649 clear_b = Button(plt.axes([0.59, 0.05, 0.15, 0.075]), 'Clear') |
706 # Set up confirm / clear range selection buttons | 650 cid_confirm = confirm_b.on_clicked(confirm_selection) |
707 confirm_b = Button(plt.axes([0.75, 0.05, 0.15, 0.075]), 'Confirm') | 651 cid_clear = clear_b.on_clicked(clear_last_selection) |
708 clear_b = Button(plt.axes([0.59, 0.05, 0.15, 0.075]), 'Clear') | 652 |
709 cid_confirm = confirm_b.on_clicked(confirm_selection) | 653 # Show figure |
710 cid_clear = clear_b.on_clicked(clear_last_selection) | 654 plt.show() |
711 | 655 |
712 # Show figure | 656 # Disconnect callbacks when figure is closed |
713 plt.show(block=True) | 657 fig.canvas.mpl_disconnect(cid_click) |
714 | 658 fig.canvas.mpl_disconnect(cid_release) |
715 # Disconnect callbacks when figure is closed | 659 confirm_b.disconnect(cid_confirm) |
716 fig.canvas.mpl_disconnect(cid_click) | 660 clear_b.disconnect(cid_clear) |
717 fig.canvas.mpl_disconnect(cid_release) | |
718 confirm_b.disconnect(cid_confirm) | |
719 clear_b.disconnect(cid_clear) | |
720 | 661 |
721 # Swap selection depending on select_mask | 662 # Swap selection depending on select_mask |
722 if not select_mask: | 663 if not select_mask: |
723 selected_index_ranges, unselected_index_ranges = unselected_index_ranges, \ | 664 selected_index_ranges, unselected_index_ranges = unselected_index_ranges, \ |
724 selected_index_ranges | 665 selected_index_ranges |
783 if isinstance(name, str): | 724 if isinstance(name, str): |
784 name = f' {name} ' | 725 name = f' {name} ' |
785 else: | 726 else: |
786 name = ' ' | 727 name = ' ' |
787 # Check existing values | 728 # Check existing values |
788 use_input = False | 729 use_input = 'no' |
789 if (is_int(first_index, 0) and is_int(offset, 0) and is_int(num_imgs, 1)): | 730 if (is_int(first_index, 0) and is_int(offset, 0) and is_int(num_imgs, 1)): |
790 if offset < 0: | 731 if offset < 0: |
791 use_input = input_yesno(f'\nCurrent{name}first index = {first_index}, '+ | 732 use_input = pyip.inputYesNo('\nCurrent'+name+f'first index = {first_index}, '+ |
792 'use this value (y/n)?', 'y') | 733 'use this value ([y]/n)? ', blank=True) |
793 else: | 734 else: |
794 use_input = input_yesno(f'\nCurrent{name}first index/offset = '+ | 735 use_input = pyip.inputYesNo('\nCurrent'+name+'first index/offset = '+ |
795 f'{first_index}/{offset}, use these values (y/n)?', 'y') | 736 f'{first_index}/{offset}, use these values ([y]/n)? ', |
737 blank=True) | |
796 if num_required is None: | 738 if num_required is None: |
797 if use_input: | 739 if use_input != 'no': |
798 use_input = input_yesno(f'Current number of{name}images = '+ | 740 use_input = pyip.inputYesNo('Current number of'+name+'images = '+ |
799 f'{num_imgs}, use this value (y/n)? ', 'y') | 741 f'{num_imgs}, use this value ([y]/n)? ', |
800 if use_input: | 742 blank=True) |
743 if use_input != 'no': | |
801 return first_index, offset, num_imgs | 744 return first_index, offset, num_imgs |
802 | 745 |
803 # Check range against requirements | 746 # Check range against requirements |
804 if num_imgs < 1: | 747 if num_imgs < 1: |
805 logging.warning('No available'+name+'images') | 748 logging.warning('No available'+name+'images') |
821 if num_required is None: | 764 if num_required is None: |
822 last_index = first_index+num_imgs | 765 last_index = first_index+num_imgs |
823 use_all = f'Use all ([{first_index}, {last_index}])' | 766 use_all = f'Use all ([{first_index}, {last_index}])' |
824 pick_offset = 'Pick a first index offset and a number of images' | 767 pick_offset = 'Pick a first index offset and a number of images' |
825 pick_bounds = 'Pick the first and last index' | 768 pick_bounds = 'Pick the first and last index' |
826 choice = input_menu([use_all, pick_offset, pick_bounds], default=pick_offset) | 769 menuchoice = pyip.inputMenu([use_all, pick_offset, pick_bounds], numbered=True) |
827 if not choice: | 770 if menuchoice == use_all: |
828 offset = 0 | 771 offset = 0 |
829 elif choice == 1: | 772 elif menuchoice == pick_offset: |
830 offset = input_int('Enter the first index offset', 0, last_index-first_index) | 773 offset = pyip.inputInt('Enter the first index offset'+ |
774 f' [0, {last_index-first_index}]: ', min=0, max=last_index-first_index) | |
831 first_index += offset | 775 first_index += offset |
832 if first_index == last_index: | 776 if first_index == last_index: |
833 num_imgs = 1 | 777 num_imgs = 1 |
834 else: | 778 else: |
835 num_imgs = input_int('Enter the number of images', 1, num_imgs-offset) | 779 num_imgs = pyip.inputInt(f'Enter the number of images [1, {num_imgs-offset}]: ', |
836 else: | 780 min=1, max=num_imgs-offset) |
837 offset = input_int('Enter the first index', first_index, last_index) | 781 else: |
782 offset = pyip.inputInt(f'Enter the first index [{first_index}, {last_index}]: ', | |
783 min=first_index, max=last_index)-first_index | |
838 first_index += offset | 784 first_index += offset |
839 num_imgs = input_int('Enter the last index', first_index, last_index)-first_index+1 | 785 num_imgs = pyip.inputInt(f'Enter the last index [{first_index}, {last_index}]: ', |
786 min=first_index, max=last_index)-first_index+1 | |
840 else: | 787 else: |
841 use_all = f'Use ([{first_index}, {first_index+num_required-1}])' | 788 use_all = f'Use ([{first_index}, {first_index+num_required-1}])' |
842 pick_offset = 'Pick the first index offset' | 789 pick_offset = 'Pick the first index offset' |
843 choice = input_menu([use_all, pick_offset], pick_offset) | 790 menuchoice = pyip.inputMenu([use_all, pick_offset], numbered=True) |
844 offset = 0 | 791 offset = 0 |
845 if choice == 1: | 792 if menuchoice == pick_offset: |
846 offset = input_int('Enter the first index offset', 0, num_imgs-num_required) | 793 offset = pyip.inputInt('Enter the first index offset'+ |
794 f'[0, {num_imgs-num_required}]: ', min=0, max=num_imgs-num_required) | |
847 first_index += offset | 795 first_index += offset |
848 num_imgs = num_required | 796 num_imgs = num_required |
849 | 797 |
850 return first_index, offset, num_imgs | 798 return first_index, offset, num_imgs |
851 | 799 |
923 img_stack = f.get('entry/instrument/detector/data')[ | 871 img_stack = f.get('entry/instrument/detector/data')[ |
924 img_offset:img_offset+num_imgs:num_img_skip+1, | 872 img_offset:img_offset+num_imgs:num_img_skip+1, |
925 img_x_bounds[0]:img_x_bounds[1],img_y_bounds[0]:img_y_bounds[1]] | 873 img_x_bounds[0]:img_x_bounds[1],img_y_bounds[0]:img_y_bounds[1]] |
926 logging.info(f'... done in {time()-t0:.2f} seconds!') | 874 logging.info(f'... done in {time()-t0:.2f} seconds!') |
927 else: | 875 else: |
928 illegal_value(filetype, 'filetype', 'loadImageStack') | 876 illegal_value(filetype, 'filetype', 'findImageRange') |
929 return img_stack | 877 return img_stack |
930 | 878 |
931 def combine_tiffs_in_h5(files, num_imgs, h5_filename): | 879 def combine_tiffs_in_h5(files, num_imgs, h5_filename): |
932 img_stack = loadImageStack(files, 'tif', 0, num_imgs) | 880 img_stack = loadImageStack(files, 'tif', 0, num_imgs) |
933 with h5py.File(h5_filename, 'w') as f: | 881 with h5py.File(h5_filename, 'w') as f: |
1134 x_upp = len_a | 1082 x_upp = len_a |
1135 if not is_int(x_upp, x_low+num_x_min, len_a): | 1083 if not is_int(x_upp, x_low+num_x_min, len_a): |
1136 illegal_value(x_upp, 'x_upp', 'selectArrayBounds') | 1084 illegal_value(x_upp, 'x_upp', 'selectArrayBounds') |
1137 return None | 1085 return None |
1138 quickPlot((range(len_a), a), vlines=(x_low,x_upp), title=title) | 1086 quickPlot((range(len_a), a), vlines=(x_low,x_upp), title=title) |
1139 if not input_yesno(f'\nCurrent array bounds: [{x_low}, {x_upp}] '+ | 1087 if pyip.inputYesNo(f'\nCurrent array bounds: [{x_low}, {x_upp}], '+ |
1140 'use these values (y/n)?', 'y'): | 1088 'use these values ([y]/n)? ', blank=True) == 'no': |
1141 x_low = None | 1089 x_low = None |
1142 x_upp = None | 1090 x_upp = None |
1143 else: | 1091 else: |
1144 clearPlot(title) | 1092 clearPlot(title) |
1145 return x_low, x_upp | 1093 return x_low, x_upp |
1148 x_min = 0 | 1096 x_min = 0 |
1149 x_max = len_a | 1097 x_max = len_a |
1150 x_low_max = len_a-num_x_min | 1098 x_low_max = len_a-num_x_min |
1151 while True: | 1099 while True: |
1152 quickPlot(range(x_min, x_max), a[x_min:x_max], title=title) | 1100 quickPlot(range(x_min, x_max), a[x_min:x_max], title=title) |
1153 zoom_flag = input_yesno('Set lower data bound (y) or zoom in (n)?', 'y') | 1101 zoom_flag = pyip.inputInt('Set lower data bound ([0]) or zoom in (1)?: ', |
1154 if zoom_flag: | 1102 min=0, max=1, blank=True) |
1155 x_low = input_int(' Set lower data bound', 0, x_low_max) | 1103 if zoom_flag == 1: |
1104 x_min = pyip.inputInt(f' Set lower zoom index [0, {x_low_max}]: ', | |
1105 min=0, max=x_low_max) | |
1106 x_max = pyip.inputInt(f' Set upper zoom index [{x_min+1}, {x_low_max+1}]: ', | |
1107 min=x_min+1, max=x_low_max+1) | |
1108 else: | |
1109 x_low = pyip.inputInt(f' Set lower data bound [0, {x_low_max}]: ', | |
1110 min=0, max=x_low_max) | |
1156 break | 1111 break |
1157 else: | |
1158 x_min = input_int(' Set lower zoom index', 0, x_low_max) | |
1159 x_max = input_int(' Set upper zoom index', x_min+1, x_low_max+1) | |
1160 else: | 1112 else: |
1161 if not is_int(x_low, 0, len_a-num_x_min): | 1113 if not is_int(x_low, 0, len_a-num_x_min): |
1162 illegal_value(x_low, 'x_low', 'selectArrayBounds') | 1114 illegal_value(x_low, 'x_low', 'selectArrayBounds') |
1163 return None | 1115 return None |
1164 if x_upp is None: | 1116 if x_upp is None: |
1165 x_min = x_low+num_x_min | 1117 x_min = x_low+num_x_min |
1166 x_max = len_a | 1118 x_max = len_a |
1167 x_upp_min = x_min | 1119 x_upp_min = x_min |
1168 while True: | 1120 while True: |
1169 quickPlot(range(x_min, x_max), a[x_min:x_max], title=title) | 1121 quickPlot(range(x_min, x_max), a[x_min:x_max], title=title) |
1170 zoom_flag = input_yesno('Set upper data bound (y) or zoom in (n)?', 'y') | 1122 zoom_flag = pyip.inputInt('Set upper data bound ([0]) or zoom in (1)?: ', |
1171 if zoom_flag: | 1123 min=0, max=1, blank=True) |
1172 x_upp = input_int(' Set upper data bound', x_upp_min, len_a) | 1124 if zoom_flag == 1: |
1125 x_min = pyip.inputInt(f' Set upper zoom index [{x_upp_min}, {len_a-1}]: ', | |
1126 min=x_upp_min, max=len_a-1) | |
1127 x_max = pyip.inputInt(f' Set upper zoom index [{x_min+1}, {len_a}]: ', | |
1128 min=x_min+1, max=len_a) | |
1129 else: | |
1130 x_upp = pyip.inputInt(f' Set upper data bound [{x_upp_min}, {len_a}]: ', | |
1131 min=x_upp_min, max=len_a) | |
1173 break | 1132 break |
1174 else: | |
1175 x_min = input_int(' Set upper zoom index', x_upp_min, len_a-1) | |
1176 x_max = input_int(' Set upper zoom index', x_min+1, len_a) | |
1177 else: | 1133 else: |
1178 if not is_int(x_upp, x_low+num_x_min, len_a): | 1134 if not is_int(x_upp, x_low+num_x_min, len_a): |
1179 illegal_value(x_upp, 'x_upp', 'selectArrayBounds') | 1135 illegal_value(x_upp, 'x_upp', 'selectArrayBounds') |
1180 return None | 1136 return None |
1181 print(f'lower bound = {x_low} (inclusive)\nupper bound = {x_upp} (exclusive)]') | 1137 print(f'lower bound = {x_low} (inclusive)\nupper bound = {x_upp} (exclusive)]') |
1182 quickPlot((range(len_a), a), vlines=(x_low,x_upp), title=title) | 1138 quickPlot((range(len_a), a), vlines=(x_low,x_upp), title=title) |
1183 if not input_yesno('Accept these bounds (y/n)?', 'y'): | 1139 if pyip.inputYesNo('Accept these bounds ([y]/n)?: ', blank=True) == 'no': |
1184 x_low, x_upp = selectArrayBounds(a, None, None, num_x_min, title=title) | 1140 x_low, x_upp = selectArrayBounds(a, None, None, num_x_min, title=title) |
1185 clearPlot(title) | 1141 clearPlot(title) |
1186 return x_low, x_upp | 1142 return x_low, x_upp |
1187 | 1143 |
1188 def selectImageBounds(a, axis, low=None, upp=None, num_min=None, | 1144 def selectImageBounds(a, axis, low=None, upp=None, num_min=None, |
1226 quickImshow(a[:,min_:max_], title=title, aspect='auto', | 1182 quickImshow(a[:,min_:max_], title=title, aspect='auto', |
1227 extent=[min_,max_,a.shape[0],0]) | 1183 extent=[min_,max_,a.shape[0],0]) |
1228 else: | 1184 else: |
1229 quickImshow(a[min_:max_,:], title=title, aspect='auto', | 1185 quickImshow(a[min_:max_,:], title=title, aspect='auto', |
1230 extent=[0,a.shape[1], max_,min_]) | 1186 extent=[0,a.shape[1], max_,min_]) |
1231 zoom_flag = input_yesno('Set lower data bound (y) or zoom in (n)?', 'y') | 1187 zoom_flag = pyip.inputInt('Set lower data bound (0) or zoom in (1)?: ', |
1188 min=0, max=1) | |
1232 if zoom_flag: | 1189 if zoom_flag: |
1233 low = input_int(' Set lower data bound', 0, low_max) | 1190 min_ = pyip.inputInt(f' Set lower zoom index [0, {low_max}]: ', |
1191 min=0, max=low_max) | |
1192 max_ = pyip.inputInt(f' Set upper zoom index [{min_+1}, {low_max+1}]: ', | |
1193 min=min_+1, max=low_max+1) | |
1194 else: | |
1195 low = pyip.inputInt(f' Set lower data bound [0, {low_max}]: ', | |
1196 min=0, max=low_max) | |
1234 break | 1197 break |
1235 else: | |
1236 min_ = input_int(' Set lower zoom index', 0, low_max) | |
1237 max_ = input_int(' Set upper zoom index', min_+1, low_max+1) | |
1238 else: | 1198 else: |
1239 if not is_int(low, 0, a.shape[axis]-num_min): | 1199 if not is_int(low, 0, a.shape[axis]-num_min): |
1240 illegal_value(low, 'low', 'selectImageBounds') | 1200 illegal_value(low, 'low', 'selectImageBounds') |
1241 return None | 1201 return None |
1242 if upp is None: | 1202 if upp is None: |
1248 quickImshow(a[:,min_:max_], title=title, aspect='auto', | 1208 quickImshow(a[:,min_:max_], title=title, aspect='auto', |
1249 extent=[min_,max_,a.shape[0],0]) | 1209 extent=[min_,max_,a.shape[0],0]) |
1250 else: | 1210 else: |
1251 quickImshow(a[min_:max_,:], title=title, aspect='auto', | 1211 quickImshow(a[min_:max_,:], title=title, aspect='auto', |
1252 extent=[0,a.shape[1], max_,min_]) | 1212 extent=[0,a.shape[1], max_,min_]) |
1253 zoom_flag = input_yesno('Set upper data bound (y) or zoom in (n)?', 'y') | 1213 zoom_flag = pyip.inputInt('Set upper data bound (0) or zoom in (1)?: ', |
1214 min=0, max=1) | |
1254 if zoom_flag: | 1215 if zoom_flag: |
1255 upp = input_int(' Set upper data bound', upp_min, a.shape[axis]) | 1216 min_ = pyip.inputInt(f' Set upper zoom index [{upp_min}, {a.shape[axis]-1}]: ', |
1217 min=upp_min, max=a.shape[axis]-1) | |
1218 max_ = pyip.inputInt(f' Set upper zoom index [{min_+1}, {a.shape[axis]}]: ', | |
1219 min=min_+1, max=a.shape[axis]) | |
1220 else: | |
1221 upp = pyip.inputInt(f' Set upper data bound [{upp_min}, {a.shape[axis]}]: ', | |
1222 min=upp_min, max=a.shape[axis]) | |
1256 break | 1223 break |
1257 else: | |
1258 min_ = input_int(' Set upper zoom index', upp_min, a.shape[axis]-1) | |
1259 max_ = input_int(' Set upper zoom index', min_+1, a.shape[axis]) | |
1260 else: | 1224 else: |
1261 if not is_int(upp, low+num_min, a.shape[axis]): | 1225 if not is_int(upp, low+num_min, a.shape[axis]): |
1262 illegal_value(upp, 'upp', 'selectImageBounds') | 1226 illegal_value(upp, 'upp', 'selectImageBounds') |
1263 return None | 1227 return None |
1264 bounds = (low, upp) | 1228 bounds = (low, upp) |
1271 a_tmp[bounds[0],:] = a_tmp_max | 1235 a_tmp[bounds[0],:] = a_tmp_max |
1272 a_tmp[bounds[1]-1,:] = a_tmp_max | 1236 a_tmp[bounds[1]-1,:] = a_tmp_max |
1273 print(f'lower bound = {low} (inclusive)\nupper bound = {upp} (exclusive)') | 1237 print(f'lower bound = {low} (inclusive)\nupper bound = {upp} (exclusive)') |
1274 quickImshow(a_tmp, title=title) | 1238 quickImshow(a_tmp, title=title) |
1275 del a_tmp | 1239 del a_tmp |
1276 if not input_yesno('Accept these bounds (y/n)?', 'y'): | 1240 if pyip.inputYesNo('Accept these bounds ([y]/n)?: ', blank=True) == 'no': |
1277 bounds = selectImageBounds(a, axis, low=low_save, upp=upp_save, num_min=num_min_save, | 1241 bounds = selectImageBounds(a, axis, low=low_save, upp=upp_save, num_min=num_min_save, |
1278 title=title) | 1242 title=title) |
1279 return bounds | 1243 return bounds |
1244 | |
1280 | 1245 |
1281 | 1246 |
1282 class Config: | 1247 class Config: |
1283 """Base class for processing a config file or dictionary. | 1248 """Base class for processing a config file or dictionary. |
1284 """ | 1249 """ |
1316 with open(config_file, 'r') as f: | 1281 with open(config_file, 'r') as f: |
1317 lines = f.read().splitlines() | 1282 lines = f.read().splitlines() |
1318 self.config = {item[0].strip():literal_eval(item[1].strip()) for item in | 1283 self.config = {item[0].strip():literal_eval(item[1].strip()) for item in |
1319 [line.split('#')[0].split('=') for line in lines if '=' in line.split('#')[0]]} | 1284 [line.split('#')[0].split('=') for line in lines if '=' in line.split('#')[0]]} |
1320 else: | 1285 else: |
1321 illegal_value(self.suffix, 'config file extension', 'Config.loadFile') | 1286 illegal_value(self.suffix, 'config file extension', 'loadFile') |
1322 | 1287 |
1323 # Make sure config file was correctly loaded | 1288 # Make sure config file was correctly loaded |
1324 if isinstance(self.config, dict): | 1289 if isinstance(self.config, dict): |
1325 self.load_flag = True | 1290 self.load_flag = True |
1326 else: | 1291 else: |
1328 self.config = {} | 1293 self.config = {} |
1329 | 1294 |
1330 def loadDict(self, config_dict): | 1295 def loadDict(self, config_dict): |
1331 """Takes a dictionary and places it into self.config. | 1296 """Takes a dictionary and places it into self.config. |
1332 """ | 1297 """ |
1298 exit('loadDict not tested yet, what format do we follow: txt or yaml?') | |
1333 if self.load_flag: | 1299 if self.load_flag: |
1334 logging.warning('Overwriting the previously loaded config file') | 1300 logging.warning('Overwriting the previously loaded config file') |
1335 | 1301 |
1336 if isinstance(config_dict, dict): | 1302 if isinstance(config_dict, dict): |
1337 self.config = config_dict | 1303 self.config = config_dict |
1338 self.load_flag = True | 1304 self.load_flag = True |
1339 else: | 1305 else: |
1340 illegal_value(config_dict, 'dictionary config object', 'Config.loadDict') | 1306 illegal_value(config_dict, 'dictionary config object', 'loadDict') |
1341 self.config = {} | 1307 self.config = {} |
1342 | 1308 |
1343 def saveFile(self, config_file): | 1309 def saveFile(self, config_file): |
1344 """Save the config file (as a yaml file only right now). | 1310 """Save the config file (as a yaml file only right now). |
1345 """ | 1311 """ |
1346 suffix = os.path.splitext(config_file)[1] | 1312 suffix = os.path.splitext(config_file)[1] |
1347 if suffix != '.yml' and suffix != '.yaml': | 1313 if suffix != '.yml' and suffix != '.yaml': |
1348 illegal_value(suffix, 'config file extension', 'Config.saveFile') | 1314 illegal_value(suffix, 'config file extension', 'saveFile') |
1349 | 1315 |
1350 # Check if config file exists | 1316 # Check if config file exists |
1351 if os.path.isfile(config_file): | 1317 if os.path.isfile(config_file): |
1352 logging.info(f'Updating {config_file}') | 1318 logging.info(f'Updating {config_file}') |
1353 else: | 1319 else: |
1356 # Save config file | 1322 # Save config file |
1357 with open(config_file, 'w') as f: | 1323 with open(config_file, 'w') as f: |
1358 yaml.safe_dump(self.config, f) | 1324 yaml.safe_dump(self.config, f) |
1359 | 1325 |
1360 def validate(self, pars_required, pars_missing=None): | 1326 def validate(self, pars_required, pars_missing=None): |
1361 """Returns False if any required keys are missing. | 1327 """Returns False if any required first level keys are missing. |
1362 """ | 1328 """ |
1363 if not self.load_flag: | 1329 if not self.load_flag: |
1364 logging.error('Load a config file prior to calling Config.validate') | 1330 logging.error('Load a config file prior to calling Config.validate') |
1365 | 1331 pars = [p for p in pars_required if p not in self.config] |
1366 def validate_nested_pars(config, par): | 1332 if isinstance(pars_missing, list): |
1367 par_levels = par.split(':') | 1333 pars_missing.extend(pars) |
1368 first_level_par = par_levels[0] | 1334 elif pars_missing is not None: |
1369 try: | 1335 illegal_value(pars_missing, 'pars_missing', 'Config.validate') |
1370 first_level_par = int(first_level_par) | 1336 if len(pars) > 0: |
1371 except: | |
1372 pass | |
1373 try: | |
1374 next_level_config = config[first_level_par] | |
1375 if len(par_levels) > 1: | |
1376 next_level_par = ':'.join(par_levels[1:]) | |
1377 return validate_nested_pars(next_level_config, next_level_par) | |
1378 else: | |
1379 return True | |
1380 except: | |
1381 return False | |
1382 | |
1383 pars_missing = [p for p in pars_required if not validate_nested_pars(self.config, p)] | |
1384 if len(pars_missing) > 0: | |
1385 logging.error(f'Missing item(s) in configuration: {", ".join(pars_missing)}') | |
1386 return False | 1337 return False |
1387 else: | 1338 return True |
1388 return True |