Skip to content
Snippets Groups Projects
GUI_main.py 102 KiB
Newer Older
  • Learn to ignore specific revisions
  • lpbsscientist's avatar
    lpbsscientist committed
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    """
    This script is the main script used to produce a GUI which should help for
    cell segmentation. This script can only read .nd2 files containing the
    images of cells, especially it displays for each recorded positions (field
    of view) the pictures in the time axis.
    
    The script opens first a window which allows you to load an nd2 file and
    to load or create an hdf file. The hdf file contains all the masks, so if
    it is the first the user segments an nd2 file, a new one should be created.
    And it can be then loaded for later use.  Along with new hdf file, created
    by the name entered by the user (say filename), it creates three other hdf
    files (filename_predicted.h5, filename_thresholded.h5 and
    filename_segmented.h5) these contain all the steps of the NN to get to the
    segmented picture.
    
    After the first window is finished a second one opens, where at each time
    index, three pictures
    are displayed the t-1 picture, the t picture (current frame which can be
    edited) and the t+1 picture. Using the arrows one can navigate through time.
    On top of the picture, there is always a mask which is displayed, if no cells
    are present in the mask then the mask is blank and the user does not see it.
    If one wants to hand anmotate the pictures, one can just start to draw on the
    picture using the different functions (New Cell, Add Region, Brush, Eraser,
    Save Mask,...) and the informations will be saved in the mask overlayed on
    top of the pictures.
    
    If one wants to segment using a neural network, one can press the
    corresponding button (Launch CNN) and select the time range and
    the field of views on which the neural network is applied.
    
    Once the neural network has finished predicting, there are still no visible
    masks, but on the field of views and time indices where the NN has been
    applied, the threshold and segment buttons are enabled. By checking these
    two buttons one can either display the thresholded image of the prediction or
    display the segmentation of the thresholded prediction.
    
    At this stage, one can correct the segmentation of the prediction using
    the functions (New Cell, Add Region, etc..) by selecting the Segment
    checkbox and then save them using the Save Seg button.
    If the user is happy with the segmentation, the Cell Correspondance button
    can be clicked. Until then, the cells get random numbers attributed by
    the segmentation algorithm. In order to keep track of the cell through time,
    the same cells should have the same number between two different time pictures.
    This can be (with some errors) achieved by the Cell Correspondance button,
    which tries to attribute the same number to corresponding cells in time.
    After that, the final mask is saved and it is always visible when you go on
    the corresponding picture. This mask can also be corrected using the
    usual buttons (because the Cell Correspondance makes also mistakes).
    
    """
    
    import sys
    #append all the paths where the modules are stored. Such that this script
    #looks into all of these folders when importing modules.
    sys.path.append("./unet")
    sys.path.append("./disk")
    sys.path.append("./icons")
    sys.path.append("./init")
    sys.path.append("./misc")
    import time
    import os
    import numpy as np
    
    
    # Import everything for the Graphical User Interface from the PyQt5 library.
    from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu, QVBoxLayout, QSizePolicy, QMessageBox, QWidget, QPushButton, QShortcut, QComboBox, QCheckBox, QLineEdit, QMenu, QAction, QStatusBar
    from PyQt5 import QtGui
    from PyQt5.QtCore import pyqtSignal, QObject, Qt
    
    #Import from matplotlib to use it to display the pictures and masks.
    from matplotlib.backends.qt_compat import QtCore, QtWidgets, is_pyqt5
    from matplotlib.backends.backend_qt5agg import (FigureCanvasQTAgg as FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
    from matplotlib.figure import Figure
    import matplotlib.pyplot as plt
    #import the colormaps function to create a customed colormap scale with 10
    #colors only
    from matplotlib import cm
    from matplotlib.colors import ListedColormap, LinearSegmentedColormap
    #import Path functions to handle the regions drawn by the user. ("add region
    #and new cell")
    from matplotlib.path import Path
    
    
    #Import all the other python files
    #this file handles the interaction with the disk, so loading/saving images
    #and masks and it also runs the neural network.
    import InteractionDisk_temp as nd
    #this file contains a dialog window that takes two integers as entry to swap
    #two cell values
    import ExchangeCellValues as ecv
    #this file contains a dialog window which is opened before the main program
    #and allows to load the nd2 and hdf files by browsing through the computer.
    import DialogFileBrowser as dfb
    #this file contains a window that opens to change the value of one cell. It
    #is opened as soon as the user presses with the left click on a specific cell.
    import ChangeOneCellValue as cocv
    #this file contains a dialog window to browse for the excel file where
    #all the extracted information on the fluoerscence is written. Or to create a
    #new excel file by typing a name in the text box. It is thought to have one
    #excel file per field of view.
    import DialogDataBrowser as ddb
    #this file contains a dialog window where a time range and the field of views
    #can be selected to then launch a prediction of the neural network on
    #a specific range of pictures.
    import LaunchBatchPrediction as lbp
    
    #this file initializes all the buttons present in the gui, sets the shortcuts
    #to these buttons and also connect the buttons to the function that are
    #triggered when the buttons are pressed.
    import InitButtons
    
    #this file contains the layout of the main window so it justs puts the buttons
    #and the pictures at the desired position in the main window.
    import InitLayout
    
    
    import random
    
    #import everything needed to write and read excel files.
    from openpyxl import load_workbook
    from openpyxl import Workbook
    
    
    
    
    class NavigationToolbar(NavigationToolbar):
        """This is the standard matplotlib toolbar but only the buttons
        that are of interest for this gui are loaded. These buttons allow
        to zoom into the pictures/masks and to navigate in the zoomed picture.
        A Home button can be used to set the view back to the original view.
        """
        toolitems = [t for t in NavigationToolbar.toolitems if
                     t[0] in ('Home', 'Pan', 'Zoom','Back', 'Forward')]
    
    class App(QMainWindow):
        """This class creates the main window.
    
        """
    
        def __init__(self, nd2pathstr, hdfpathstr, newhdfstr):
            super().__init__()
    #        initializes the window
    
    
    #        id is an integer that gives the id of the connection between the mouseclick method
    #        and the activation of the button.
    
    #       all these ids are integers which are used to set a connection between
    #       the button and the function that this button calls.
    #       There are three of them because it happens that one can trigger three
    #       different functions with one button.
            self.id = 0
            self.id2 = 0
            self.id3 = 0
    
    
    
    #       it calls an object of the class Load Image from the InteractionDisk
    #       file which is used to load images and masks from the nd2 file. To
    #       initialize this object it needs the path of the nd2 file, of an
    #       existing hdf file and the name of a new hdf file. If the user has no
    #       hdf file yet the hdfpathstr will be empty and vice versa if the user
    #       selects an already existing hdf file.
    #       It takes all the strings given by the first window
    #       (called before the main window opens) from the DialogFileBrowser.py
    #       file. (see at the end of this code)
    
            self.reader = nd.Reader(hdfpathstr, newhdfstr, nd2pathstr)
    
    
    #       these variables are used to create/read/load the excel file used
    #       to write the fluorescence values extracted. For each field of view,
    #       the user will be asked each time to create a new xls file for the
    #       field of view or to load an existing field of view (this is the role
    #       of the boolean variable)
            self.xlsfilename = ''
            self.nd2path = nd2pathstr
            self.FlagFluoExtraction = False
    
    
    
    
    #        Set the indices for the time axis and the field of view index. These
    #        indices represent everywhere the current picture (the one that can be
    #        edited, i.e. the time t frame)
            self.Tindex = 0
            self.FOVindex = 0
    
    #        loading the first images of the cells from the nd2 file
            self.currentframe = self.reader.LoadOneImage(self.Tindex,self.FOVindex)
            self.nextframe = self.reader.LoadOneImage(self.Tindex+1, self.FOVindex)
            self.previousframe = np.zeros([self.reader.sizey, self.reader.sizex])
    
    
    
    #        loading the first masks from the hdf5 file
            self.mask_curr = self.reader.LoadMask(self.Tindex, self.FOVindex)
            self.mask_previous = np.zeros([self.reader.sizey, self.reader.sizex])
            self.mask_next = self.reader.LoadMask(self.Tindex+1, self.FOVindex)
    
    
    #        creates a list of all the buttons, which will then be used in order
    #        to disable all the other buttons at once when one button/function
    #        is pressed/used in the gui.
            self.buttonlist = []
    
    
    #        setting buttons as attributes
    #        the shortcuts for the buttons, the functions to which they are
    #        connected to,... are all set up in the ButtonInit file which is called
    #        in the self.initUI() method below.
    
            self.button_newcell = QPushButton("New cell")
            self.buttonlist.append(self.button_newcell)
    
            self.button_add_region = QPushButton("Add region")
            self.buttonlist.append(self.button_add_region)
    
            self.button_savemask = QPushButton("Save Mask")
            self.buttonlist.append(self.button_savemask)
    
            self.button_drawmouse = QPushButton('Brush')
            self.buttonlist.append(self.button_drawmouse)
    
            self.button_eraser = QPushButton('Eraser')
            self.buttonlist.append(self.button_eraser)
    
            self.button_exval = QPushButton('Exchange Cell Values')
            self.buttonlist.append(self.button_exval)
    
            self.button_showval = QCheckBox('Show Cell Values')
            self.buttonlist.append(self.button_showval)
    
            self.button_hidemask = QCheckBox('Hide Mask')
            self.buttonlist.append(self.button_hidemask)
    
            self.button_nextframe = QPushButton("Next Time Frame")
            self.buttonlist.append(self.button_nextframe)
    
            self.button_previousframe = QPushButton("Previous Time Frame")
            self.buttonlist.append(self.button_previousframe)
    
            self.button_cnn = QPushButton('Launch CNN')
            self.buttonlist.append(self.button_cnn)
    
            self.button_threshold = QCheckBox('Threshold prediction')
            self.buttonlist.append(self.button_threshold)
    
            self.button_segment = QCheckBox('Segment')
            self.buttonlist.append(self.button_segment)
    
            self.button_cellcorespondance = QPushButton('Cell Correspondance')
            self.buttonlist.append(self.button_cellcorespondance)
    
            self.button_changecellvalue = QPushButton('Change cell value')
            self.buttonlist.append(self.button_changecellvalue)
    
            self.button_extractfluorescence = QPushButton('Extract Fluorescence')
            self.buttonlist.append(self.button_extractfluorescence)
    
    
            self.initUI()
    
    
    
    
        def initUI(self):
            """Initializing the widgets contained in the window.
            Especially, it creates the widget to plot the
            pictures/masks by creating an object of the PlotCanvas class self.m.
            Every interaction with the masks or the pictures (loading new
            frames/editing the frames/masks) occurs through this class.
    
            This method initializes all the buttons with the InitButtons file.
            It connects the buttons to the functions that they should trigger,
            it sets the shortcuts to the buttons, a tool tip,
            eventually a message on the status bar when the user hovers
            over the button, etc..
    
            This function also sets all the layout in the InitLayout file. It
            takes and places the widgets (buttons, canvas, toolbar).
    
            The function initializes a Menu Bar to have a menu which can be
            improved later on.
            It sets a toolbar of the matplotlib library and hides it. But it allows
            to connect to the functions of this toolbar through "homemade"
            QPushButtons instead of the ones provided by matplotlib.
            Finally, it sets a StatusBar which displays some text to describe
            the use of some buttons, or to show that the program is working on
            something (running the neural network, loading frames, etc...)
    
            After all this has been initialized, the program is ready to be used.
    
            """
            self._main = QtWidgets.QWidget()
            self.setCentralWidget(self._main)
    
    #       Here our canvas is created where using matplotlib,
    #       one can plot data to display the pictures and masks.
            self.m = PlotCanvas(self)
    
    #       Initialize all the buttons that are needed and the functions that are
    #       connected when the buttons are triggered.
            InitButtons.Init(self)
            InitLayout.Init(self)
    
    #        MENU, TOOLBAR AND STATUS BAR
    #       creates a menu just in case, some other functions can be added later
    #        in this menu.
            menubar = self.menuBar()
            self.fileMenu = menubar.addMenu('File')
            self.saveactionmenu = QAction('Save')
            self.fileMenu.addAction(self.saveactionmenu)
            self.saveactionmenu.triggered.connect(self.ButtonSaveMask)
    
    
    #       hide the toolbar and instead of the original buttons of matplotlib,
    #       QPushbuttons are used and are connected to the functions of the toolbar
    #       it is than easier to interact with these buttons (for example to
    #       to disable them and so on..)
            self.Nvgtlbar = NavigationToolbar(self.m, self)
            self.addToolBar(self.Nvgtlbar)
            self.Nvgtlbar.hide()
    
    #        creates a status bar, which displays (or should display) some text
    #        whenever a function is used.
            self.statusBar = QStatusBar()
            self.setStatusBar(self.statusBar)
    
            self.show()
    
    
        def mousePressEvent(self, QMouseEvent):
            """this function is implemented just to have the QLineButtons of the
            change time index button, setthreshold button and the setsegmentation
            button out of focus when the user clicks somewhere
            on the gui. (to unfocus the buttons)
            """
            self.button_timeindex.clearFocus()
            if self.button_SetThreshold.isEnabled():
                self.button_SetThreshold.clearFocus()
    
            if self.button_SetSegmentation.isEnabled():
                self.button_SetSegmentation.clearFocus()
    
    
    
    #        CONNECT the functions of the toolbar to our custom QPushbuttons.
        def ZoomTlbar(self):
            """The button_zoom is connected to the zoom function of the toolbar
            already present in the matplotlib library.
    
            Depending on the buttons that are active or checked, when the zoom
            function is used, it does not disable all the buttons.
    
            If the segment and threshold button are not checked or used
            when the zoom button is clicked, it disables all the button
            using self.Disable which disables everything except the button passed
            in argument (in this case button_zoom).
    
    
            If the zoom button is used while the segment button is checked,
            it disables all the buttons (1st elif) except the segment button
            but once it is finished (so the zoom button becomes unchecked)
            then it enables only the editing buttons (as long as the segment
            button is still checked) such as New Cell, Add Region, Eraser,
            Brush,etc.. and the other toolbar buttons (3rd elif)
    
    
            If the zoom button is clicked while the threshold button is checked,
            it disables all the button except the threshold button (2nd elif).
            Once the zoom button is unchecked, it enables the toolbar buttons
            (4th elif)
            In any other case, it just enables all the buttons again.
    
    
            """
            self.Nvgtlbar.zoom()
    
            if self.button_zoom.isChecked() and not(self.button_segment.isChecked() or self.button_threshold.isChecked()):
                self.Disable(self.button_zoom)
    
            elif self.button_zoom.isChecked() and self.button_segment.isChecked():
                self.Disable(self.button_zoom)
                self.button_segment.setEnabled(True)
    
            elif self.button_zoom.isChecked() and self.button_threshold.isChecked():
                self.Disable(self.button_zoom)
                self.button_threshold.setEnabled(True)
    
            elif self.button_zoom.isChecked() == False and self.button_segment.isChecked():
    
                self.button_pan.setEnabled(True)
                self.button_home.setEnabled(True)
                self.button_back.setEnabled(True)
                self.button_forward.setEnabled(True)
    
                self.EnableCorrectionsButtons()
    
            elif self.button_zoom.isChecked() == False and self.button_threshold.isChecked():
    
                self.button_pan.setEnabled(True)
                self.button_home.setEnabled(True)
                self.button_back.setEnabled(True)
                self.button_forward.setEnabled(True)
    
            else:
                self.Enable(self.button_zoom)
    
        def HomeTlbar(self):
    #        connects the home button to the home function of the matplotlib
    #        toolbar. It sets the view to the original view (no zoom)
            self.Nvgtlbar.home()
    
    
        def BackTlbar(self):
    #        It calls the back function of the matplotlib toolbar which sets the
    #        view to the previous one (if the user does several zooms/pans,
    #        this button allows to go back in the "history of views")
            self.Nvgtlbar.back()
    
    
    
        def ForwardTlbar(self):
    #        It calls the forward function of the matplotlib toolbar which sets the
    #        view to the next one (if the user does several zooms/pans,
    #        this button allows to go forward in the "history of views"
    
            self.Nvgtlbar.forward()
    
    
    
        def PanTlbar(self):
            """The button_pan is connected to the pan function of the toolbar
            already present in the matplotlib library.
    
            Depending on the buttons that are active or checked, when the pan
            function is used, it does not disable all the buttons.
    
            If the segment and threshold button are not checked or used
            when the pan button is clicked, it disables all the button
            using self.Disable which disables everything except the button passed
            in argument (in this case button_pan).
    
    
            If the pan button is used while the segment button is checked,
            it disables all the buttons (1st elif) except the segment button
            but once it is finished (so the zoom button becomes unchecked)
            then it enables only the editing buttons (as long as the segment
            button is still checked) such as New Cell, Add Region, Eraser,
            Brush,etc.. and the other toolbar buttons (3rd elif)
    
    
            If the pan button is clicked while the threshold button is checked,
            it disables all the button except the threshold button (2nd elif).
            Once the pan button is unchecked, it enables the toolbar buttons
            (4th elif)
            In any other case, it just enables all the buttons again.
            """
    
            self.Nvgtlbar.pan()
    
            if self.button_pan.isChecked() and not(self.button_segment.isChecked() or self.button_threshold.isChecked()):
                self.Disable(self.button_pan)
    
            elif self.button_pan.isChecked() and self.button_segment.isChecked():
                self.Disable(self.button_pan)
                self.button_segment.setEnabled(True)
    
            elif self.button_pan.isChecked() and self.button_threshold.isChecked():
                self.Disable(self.button_pan)
                self.button_threshold.setEnabled(True)
    
            elif not(self.button_pan.isChecked()) and self.button_segment.isChecked():
    
                self.button_zoom.setEnabled(True)
                self.button_home.setEnabled(True)
                self.button_back.setEnabled(True)
                self.button_forward.setEnabled(True)
    
                self.EnableCorrectionsButtons()
    
            elif not(self.button_pan.isChecked()) and self.button_threshold.isChecked():
                self.button_zoom.setEnabled(True)
                self.button_home.setEnabled(True)
                self.button_back.setEnabled(True)
                self.button_forward.setEnabled(True)
    
    
            else:
                self.Enable(self.button_pan)
    
    
        def ButtonFluo(self):
            """This function is called everytime the Extract Fluorescence button is
            clicked (self.button_extractfluorescence).
    
            self.FlagFluoExtraction is boolean which is True when the path to the
            excel file has already been loaded into self.xlsfilename.
            This pathname changes for each field of view as it is thought to have
            one xls file per field of view.
            So at the beginning and each time the user changes field of view,
            self.FlagFluoExtraction is set to False.
    
            When it is set to False, this function calls a dialog window where
            the user is asked to load an already existing xls file for the current
            field of view or to give a name to create a new xls file for
            the current field of view. (self.Dialogxls)
    
            If it set to true, it means that self.xlsfilename contains the path
            to the xls file for the current field of view and it is directly given
            to the function that writes the fluorescence into the xls file.
            (self.ExtractFluo)
            """
    
            if self.FlagFluoExtraction:
                self.ExtractFluo(self.xlsfilename)
            else:
                self.DialogXls()
    
    
        def DialogXls(self):
            """This function creates a dialog window which gives two options to the
            user either to load an existing xls file or to give a new name in order
            to create a new xls file.
            """
    #        creates the window
            dwind = ddb.FileBrowser()
    
    #        this test is True if the user presses ok in the dialog window, if the
    #        user presses cancels it returns and nothing happens.
    
            if dwind.exec_():
    
    #            read the entry given by the file browser
                xlsname = dwind.xlsname
    
    #            reads the entry given by the new filename text field.
                newxlsname = dwind.newxlsentry.text()
    
    #            if the string containing the filepath to an existing xls file
    #            is not empty then it calls directly the function to write the
    #            data into this existing xls file and sets self.xlsfilename
                if xlsname:
    
                    self.xlsfilename = xlsname
                    self.ExtractFluo(xlsname)
    
    #           if xlsname is empty then it creates a new pathfilename and puts
    #           the new created xls file into the folder where nd2 is located.
    #           the string containing the nd2 namepath is split
                else:
                    xlsname = ''
                    templist = self.nd2path.split('/')
    
                    for k in range(0, len(templist)-1):
    
                        xlsname = xlsname + templist[k] + '/'
    #               this is the new path/filename
                    xlsname = xlsname + newxlsname + '.xlsx'
                    self.xlsfilename = xlsname
    
    #               here as a new name has been given, it means that a new xls file
    #                should be created, this is done with CreateXls
                    self.CreateXls(xlsname)
    #                once there is an existing xls file, it writes in this file
    #                using self.ExtractFluo.
                    self.ExtractFluo(xlsname)
    #           this flag is set to true, for the current field of view each
    #           time extract fluorescence is clicked it writes in the file located
    #           at self.xlsfilename.
                self.FlagFluoExtraction = True
            else:
    
                return
    
    
        def CreateXls(self, xlsfilename):
            """In case there is no xls file existing, here a new one is created
            and prepared. For each channel a new sheet is created.
            In the first row for each sheet, the time indices are written t = 0,
            t = 1, etc... but only every third column. Because in the row below,
            three values are extracted 'Total intensity', 'Area' and 'Variance'
            at each time index. So three columns for each time index are needed,
            for the three data points.
            The first column is left empty (starting from third row) because
            the cell numbers will be written in there.
            """
    
    #        creates a new xls file using xlwt library.
            book = Workbook()
            nbrchannels = self.reader.sizec
    
            for i in range(0,nbrchannels):
                sheetname = self.reader.channel_names[i]
    #            creates a sheet with the name of the corresponding channel.
                if i == 0:
                    sheet = book.active
                    sheet.title = sheetname
                else:
                    sheet = book.create_sheet(sheetname)
                sheet.cell(1,1, 'Cell Number / Time axis')
                sheet.cell(2,1, 'labels')
                timeaxissize = self.reader.sizet
    #           start writing the time index at column 1, column 0 is reserved for
    #           cell numbers.
                timecolindex = 2
    
                for t in range(1,timeaxissize+1):
    #                in row 0 the time index is written
                    sheet.cell(1,timecolindex).value = 't = {}'.format(t-1)
    #                in row 1, the label of the three data points are written
                    sheet.cell(2,timecolindex).value = 'Total Intensity'
                    sheet.cell(2,timecolindex+1).value = 'Total Area'
                    sheet.cell(2,timecolindex+2).value = 'Mean Intensity'
                    sheet.cell(2,timecolindex+3).value =  'Variance'
    #                updates the index, where the next time index should be written
                    timecolindex = timecolindex + 4
    
    #       saves the xls file.
            book.save(xlsfilename)
    
    
    
    
        def ExtractFluo(self, xlsfilename):
            """This is the function that takes as argument the filepath to the xls
            file and writes in the file.
            It iterates over the different channels (or the sheets of the file,
            each channel has one sheet.), and reads the image corresponding
            to the time, field of view and channel index. It reads the already
            existing file and makes a copy in which the data will be written in it.
    
            The first step of calculating the data is to iterate through each
            cell/segment of the mask (so each cell is a submatrix of one value
            in the matrix of the mask).
            For each of these value /cell, the area is extracted as being
            the number of pixels corresponding to this cell/value.
            (it is known from the microscope settings how to convert
            the pixel in area).
            The total intensity is just the value of the pixel and it is added over
            all the pixels corresonding to the cell/value.
            The mean is then calculated as being the total intensity divided by
            the number of pixels (which here is equal to the area also).
            With the mean it is then possible to calculate the variance of the
            signal for one cell/value.
    
            Then, it is checked if the value of the cell (cell number) already
            exists in the first column, if it already exists it continues to
            find the column corresponding to the time index where the values
            should be written. It sets the flag to True such that it does not
            write the cell as new one and adds it at the end of the column
    
            If the value is not found in the cell number column (new cell or
            first time writing in the file), the flag is False, thus it adds the
            cell number at the end of the column.
            It then saves the xls file.
    
            """
    
    #        disables all the buttons except the one passed in argument.
            self.Disable(self.button_extractfluorescence)
    #        shows a message on the status bar to show that the program is working
            self.statusBar.showMessage('Extracting the fluorescence...')
    
    #        opens the file to read it.
            book = load_workbook(self.xlsfilename)
    
    #       makes a copy of the reading file to write in it the new values.
    #        wb = xlscopy(readbook) # a writable copy (can't read values out of this, only write to it)
    
    #        iterate over all the channels, so over all the sheets in the file
            for channel in range(0, self.reader.sizec):
    #           loads the picture corresponding to the channel, time index and fov
                image = self.reader.LoadImageChannel(self.Tindex, self.FOVindex, channel)
    
    #            loads the sheet to read out corresponding to the current channel
                sheet = book.worksheets[channel]
    #            sheet = readbook.sheet_by_index(channel)
    
    #            this line is here to prevent some errors of streaming into
    #            the file due to read file which is open (I am not sure about this
    #            but it is a more or less working solution found on stackoverflow)
    #            os.remove(xlsfilename)
    
    #           load the sheet corresponding to the current channel to write in it
    #            writingsheet = wb.get_sheet(channel)
    
    #           this index contains the value of the maximum number of rows in the
    #           file, it is used to append at the end the cell number column a new
    #           cell/value, and it is updated each time a new cell is added.
                tempidx = sheet.max_row
    
    #           np.unique(array) returns an array which contains all the value
    #           that appear in self.m.plotmask, so it returns every cell value
    #           including the background (value 0) present in self.m.plotmask
                for val in np.unique(self.m.plotmask):
    
    #                exclude the background, as it is not a cell
                    if val != 0:
    
    #                    this (self.m.plotmask==val).sum() adds one at every pixel
    #                    where self.m.plotmask has the value val
                        area = (self.m.plotmask == val).sum()
    #                    it sums the value of the pixel in image at the coordinates
    #                    where self.m.plotmask equals val.
                        tot_intensity = image[self.m.plotmask == val].sum()
    
    #                    calculate the mean to use it in the calc. of the variance.
                        mean = tot_intensity/area
    
    #                    create a copy of plotmask because I had weird experiences
    #                    with np.where where it modified sometimes the given array
    #                    (not sure)
                        temparr = self.m.plotmask.copy()
    
    #                    extract the coordinates of the mask matrix where it equals
    #                    the current cell value val
                        coord = np.where(temparr == val)
    
    #                    variable to save the variance.
                        var = 0
    
    #                    we loop over the coord of the pixel where mask == val
                        for i in range(0,len(coord[0])):
    #                        extract the intensity at the coordinate
                            val_intensity = image[coord[0][i], coord[1][i]]
    #                        substract the value of the intensity with the mean,
    #                        and square it. It is then add to the var.
                            var = var + (val_intensity-mean)*(val_intensity-mean)
    
    #                   var is then divided by the number of the pixel
    #                   corresponding to this value (also equal to the area)
    #                   to get the variance.
                        var = var/area
    
    #                    if flag is false it means that the cell number
    #                    corresponding to val is not present in the xls file, first
    #                    column.
                        flag = False
    
    
    #                    iterate over all the rows
                        for row in range(sheet.max_row+1):
    
    #                        test if in the first column 0, the number of the cell
    #                        is already present
    #                         if sheet.cell_value(row,0) == str(val):
                             if sheet.cell(row = row+1, column = 1).value == str(val):
    
    #                             if is present, the column corresponding to the
    #                             current time index is by iterating over the cols.
                                 for col in range(sheet.max_column+1):
    #                                 test if it is the right column
    #                                 if sheet.cell_value(0, col) == 't = {}'.format(self.Tindex):
                                     if sheet.cell(row = 1, column = col+1).value == 't = {}'.format(self.Tindex):
    #                                   write in the xls file at the row, col coord
                                         sheet.cell(row+1, col+1, str(tot_intensity))
                                         sheet.cell(row+1, col+2, str(area))
                                         sheet.cell(row+1,col+3, str(mean))
                                         sheet.cell(row+1, col+4, str(var))
                                         book.save(xlsfilename)
    
    
    #                                     the flag is set to True so that it does
    #                                     not execute the code where the cell is
    #                                     added in the xls file in a new row.
                                         flag = True
                        if not flag:
    #                    this lines are executed if a new cell is detected or if
    #                    if it is the first time to write in the file.
                            for col in range(sheet.max_column+1):
                                if sheet.cell(row = 1, column =  col+1).value == 't = {}'.format(self.Tindex):
    #                                it write the cell value/cell number in the
    #                                column
                                    sheet.cell(tempidx+1,1, str(val))
    
    #                                writes the data extracted before
                                    sheet.cell(tempidx+1,col+1,str(tot_intensity))
                                    sheet.cell(tempidx+1, col+2, str(area))
                                    sheet.cell(tempidx+1, col+3, str(mean))
                                    sheet.cell(tempidx+1, col+4, str(var))
    #                                it updates the number of rows as a new cell
    #                                has been added, so there is one more row.
                                    tempidx = tempidx + 1
    #                               save in the file
                                    book.save(xlsfilename)
    
    
    #       Enable again all the buttons
            self.Enable(self.button_extractfluorescence)
    
    #        clear the message shown in the status bar
            self.statusBar.clearMessage()
    
    
    
        def LaunchBatchPrediction(self):
            """This function is called whenever the button Launch CNN is pressed.
            It allows to run the neural network over a time range and selected
            field of views.
    
            It creates a dialog window with two entries, that define the time range
            and a list where the user can select the desired fields of view.
    
            Once it reads all the value, it calls the neural network function
            inside of self.PredThreshSeg and it does the prediction of the neural
            network, thresholds this prediction and then segments it.
            """
    #        creates a dialog window from the LaunchBatchPrediction.py file
            dlg = lbp.CustomDialog(self)
    
    #        this if tests if the user pressed 'ok' in the dialog window
            if dlg.exec_():
    
    #           it tests if the user has entered some values
    #           if not it ignores and returns.
                if dlg.entry1.text()!= '' and dlg.entry2.text() != '':
    
    #                reads out the entry given by the user and converts the index
    #                to integers
                    time_value1 = int(dlg.entry1.text())
                    time_value2 = int(dlg.entry2.text())
    
    
    #                it tests if the first value is smaller or equal such that
    #                time_value1 is the lower range of the time range
    #                and time_value2 the upper boundary of the range.
                    if time_value1 <= time_value2 :
    
    #                    displays that the neural network is running
                        self.statusBar.showMessage('Running the neural network...')
    
    #                    it iterates in the list of the user-selected fields
    #                    of view, to return the corresponding index, the function
    #                    dlg.listfov.row(item) is used which gives an integer
                        for item in dlg.listfov.selectedItems():
    
    #                        iterates over the time indices in the range
                            for t in range(time_value1, time_value2+1):
    
    #                            calls the neural network for time t and selected
    #                            fov
                                if dlg.entry_threshold.text() !=  '':
                                    thr_val = float(dlg.entry_threshold.text())
                                else:
                                    thr_val = None
                                if dlg.entry_segmentation.text() != '':
                                    seg_val = int(dlg.entry_segmentation.text())
                                else:
                                    seg_val = 10
    
                                self.PredThreshSeg(t, dlg.listfov.row(item), thr_val, seg_val)
    
    
    #                   once it has iterated over all the fov, the message in
    #                   the status bar is cleared and the buttons are enabled.
                        self.statusBar.clearMessage()
                        self.EnableCNNButtons()
    
                    else:
                        return
                else:
                    return
            else:
                return
    
        def PredThreshSeg(self, timeindex, fovindex, thr_val, seg_val):
              """
              This function is called in the LaunchBatchPrediction function.
              This function calls the neural network function in the
              InteractionDisk.py file and then thresholds the result
              of the prediction, saves this thresholded prediction.
              Then it segments the thresholded prediction and saves the
              segmentation.
              """
    #          launches the neural network
              self.reader.LaunchPrediction(timeindex, fovindex)
    #          thresholds the prediction
              self.m.ThresholdMask = self.reader.ThresholdPred(thr_val, timeindex,fovindex)
    #          saves the thresholded pred.
              self.reader.SaveThresholdMask(timeindex, fovindex, self.m.ThresholdMask)
    #          segments the thresholded pred.
              self.m.SegmentedMask = self.reader.Segment(seg_val, timeindex,fovindex)
    #          saves the segmentation
              self.reader.SaveSegMask(timeindex, fovindex, self.m.SegmentedMask)
    
    
        def LaunchPrediction(self):
            """This function is not used in the gui, but it can be used to launch
            the prediction of one picture, with no thresholding and no segmentation
            """
            if not(self.reader.TestPredExisting(self.Tindex, self.FOVindex)):
                self.statusBar.showMessage('Running the neural network...')
                self.Disable(self.button_cnn)
                self.reader.LaunchPrediction(self.Tindex, self.FOVindex)
    
                self.Enable(self.button_cnn)
    
                self.button_cnn.setEnabled(False)
                self.button_threshold.setEnabled(True)
                self.button_segment.setEnabled(True)
                self.button_cellcorespondance.setEnabled(True)
                self.statusBar.clearMessage()
    
        def ChangeOneValue(self):
            """This function is called when the button Change cell value is
            clicked. It displays the instructions on the status bar.
            And if the user clicks in the graph where the current mask is displayed
            it connects the event of the click (meaning that user has clicked on
            one cell) to the function self.DialogBoxChangeOneValue.
            This function will then replaces the cell selected by the user with
            the click with a new value entered by the user.
            """
    
    #        displaying the instructions on the statusbar
            self.statusBar.showMessage('Select one cell using the left click and then enter the desired value in the dialog box')
    
    #       disables all the buttons
            self.Disable(self.button_changecellvalue)
    
    #        connects the event "press mouse button" in the matplotlib plot
    #       (picture) to the function self.DialogBoxChangeOneValue
            self.id = self.m.mpl_connect('button_press_event', self.DialogBoxChangeOneValue)
    
    
    
        def DialogBoxChangeOneValue(self, event):
            """This function is called when the user after the user has selected
            the button Change cell value and clicked in the picture to select
            the desired cell to change.
    
            It first deconnects the mouse click event in matplotlib with this
            function to not generate any other dialog window.
    
            It then tests if the click is inside the matplotlib plot (if it is
            outside it equals to None) and if it is the current and editable plot
            (the one in the middle of the gui, self.m.ax)
    
            If is true, then it sets the coordinates to int. and creates a dialog
            window where the user is asked to type a value to set it to the cell.
    
            If the user presses ok, it tests if the entry is valid (>0 and not
            empty) and looks for the old cell value and replaces it. And then
            it updates the plot such that the result of the change can be seen.
            """
    
    #        the function is disconnected from the matplotlib event.
            self.m.mpl_disconnect(self.id)
    
    #        test if the button is a left click and if the coordinates
    #        chosen by the user click is inside of the current matplotlib plot
    #        which is given by self.m.ax
            if event.button == 1 and (event.xdata != None and event.ydata != None) and self.m.ax == event.inaxes:
                newx = int(event.xdata)
                newy = int(event.ydata)
    
    #            creates a dialog window
                dlg = cocv.CustomDialog(self)
    
    #            if the user presses 'ok' in the dialog window it executes the code
    #           else it does nothing
                if dlg.exec():
    #                it tests that the user has entered some value, that it is not
    #                empty and that it is equal or bigger to 0.
                    if dlg.entry1.text() != '' and int(dlg.entry1.text()) >= 0:
    #                    reads the new value to set and converts it from str to int
                        value = int(dlg.entry1.text())
    
    #                    self.m.plotmask[newy, newx] the value selected by the user
    #                    self.m.plotmask == self.m.plotmask[newy, newx]
    #                    gives the coordinates where it is equal to the value
    #                    selected by the user. And it replaces it with the new
    #                    value.
                        self.m.plotmask[self.m.plotmask == self.m.plotmask[newy,newx]] = value
    #                    updates the plot to see the modification.
                        self.m.updatedata()
    
    #                   if the button to show cell values is checked, then it
    #                   replots the cell values
    
                        if self.button_showval.isChecked():
                            self.m.ShowCellNumbersCurr()
                            self.m.ShowCellNumbersNext()
                            self.m.ShowCellNumbersPrev()
    
    #       enables the button again
            self.Enable(self.button_changecellvalue)
    
    #        clears the message in the status bar
            self.statusBar.clearMessage()
    
    
    
    #        the button is a checkable and it has to be unchecked else it seems
    #        that the button is still in use, because it gets a blue color.
            self.button_changecellvalue.setChecked(False)