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 |
