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